* [PATCH 1/5] drm/amdgpu: fix potential overflow in fs_info.debugfs_name
@ 2026-05-18 9:40 Stanley.Yang
2026-05-18 9:40 ` [PATCH 2/5] drm/amdgpu: init locals in umc_v12_0_convert_error_address Stanley.Yang
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Stanley.Yang @ 2026-05-18 9:40 UTC (permalink / raw)
To: amd-gfx; +Cc: Hawking.Zhang, Tao.Zhou1, YiPeng.Chai, Candice.Li, Stanley.Yang
Use snprintf() with sizeof(fs_info.debugfs_name) so a long RAS block
name plus the "_err_inject" suffix cannot overflow the 32-byte buffer.
Signed-off-by: Stanley.Yang <Stanley.Yang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
index c38af6d3599e..57f13ad5605a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
@@ -2280,7 +2280,8 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev)
list_for_each_entry(obj, &con->head, node) {
if (amdgpu_ras_is_supported(adev, obj->head.block) &&
(obj->attr_inuse == 1)) {
- sprintf(fs_info.debugfs_name, "%s_err_inject",
+ snprintf(fs_info.debugfs_name, sizeof(fs_info.debugfs_name),
+ "%s_err_inject",
get_ras_block_str(&obj->head));
fs_info.head = obj->head;
amdgpu_ras_debugfs_create(adev, &fs_info, dir);
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/5] drm/amdgpu: init locals in umc_v12_0_convert_error_address
2026-05-18 9:40 [PATCH 1/5] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Stanley.Yang
@ 2026-05-18 9:40 ` Stanley.Yang
2026-05-18 9:40 ` [PATCH 3/5] drm/amd/ras: cap pending_ecc_list size Stanley.Yang
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Stanley.Yang @ 2026-05-18 9:40 UTC (permalink / raw)
To: amd-gfx; +Cc: Hawking.Zhang, Tao.Zhou1, YiPeng.Chai, Candice.Li, Stanley.Yang
row, col, col_lower, row_lower, row_high and bank could be read on
code paths that never assign them. Initialize them to 0.
Signed-off-by: Stanley.Yang <Stanley.Yang@amd.com>
---
drivers/gpu/drm/amd/amdgpu/umc_v12_0.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
index 6cf674dfc8c4..a6df6a778f50 100644
--- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
@@ -285,7 +285,8 @@ static int umc_v12_0_convert_error_address(struct amdgpu_device *adev,
struct ta_ras_query_address_output *addr_out,
bool dump_addr)
{
- uint32_t col, col_lower, row, row_lower, row_high, bank;
+ uint32_t row = 0, row_lower = 0, row_high = 0;
+ uint32_t col = 0, col_lower = 0, bank = 0;
uint32_t channel_index = 0, umc_inst = 0;
uint32_t i, bit_num, retire_unit, *flip_bits;
uint64_t soc_pa, column, err_addr;
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/5] drm/amd/ras: cap pending_ecc_list size
2026-05-18 9:40 [PATCH 1/5] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Stanley.Yang
2026-05-18 9:40 ` [PATCH 2/5] drm/amdgpu: init locals in umc_v12_0_convert_error_address Stanley.Yang
@ 2026-05-18 9:40 ` Stanley.Yang
2026-05-18 9:40 ` [PATCH 4/5] drm/amd/ras: snapshot remote cmd header to fix double-fetch Stanley.Yang
2026-05-18 9:40 ` [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded helpers Stanley.Yang
3 siblings, 0 replies; 6+ messages in thread
From: Stanley.Yang @ 2026-05-18 9:40 UTC (permalink / raw)
To: amd-gfx; +Cc: Hawking.Zhang, Tao.Zhou1, YiPeng.Chai, Candice.Li, Stanley.Yang
Drop new entries once pending_ecc_count hits RAS_UMC_PENDING_ECC_MAX
(4096) so an ECC storm or repeated UMC error injection cannot exhaust
kernel memory. Dropped events are counted and reported via a
rate-limited warning.
Signed-off-by: Stanley.Yang <Stanley.Yang@amd.com>
---
drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h | 9 ++++++
drivers/gpu/drm/amd/ras/rascore/ras_umc.c | 35 +++++++++++++++++++++++
drivers/gpu/drm/amd/ras/rascore/ras_umc.h | 12 ++++++++
3 files changed, 56 insertions(+)
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
index 8156531a7b63..f34dda7ce87b 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
@@ -46,6 +46,15 @@
printk(KERN_WARNING fmt, ##__VA_ARGS__); \
} while (0)
+#define RAS_DEV_WARN_RATELIMITED(device, fmt, ...) \
+ do { \
+ if (device) \
+ dev_warn_ratelimited(((struct amdgpu_device *)device)->dev, \
+ fmt, ##__VA_ARGS__); \
+ else \
+ printk_ratelimited(KERN_WARNING fmt, ##__VA_ARGS__); \
+ } while (0)
+
#define RAS_DEV_INFO(device, fmt, ...) \
do { \
if (device) \
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
index d4072350f48f..e8c13e42c2f8 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
@@ -193,12 +193,29 @@ static void ras_umc_reserve_eeprom_record(struct ras_core_context *ras_core,
}
/* When gpu reset is ongoing, ecc logging operations will be pended.
+ *
+ * The pending list is bounded by RAS_UMC_PENDING_ECC_MAX so that an ECC
+ * storm or repeated UMC error injection cannot make this list (and the
+ * kernel allocations behind it) grow without bound. Once the limit is
+ * reached, additional events are dropped and counted in
+ * pending_ecc_dropped, with a rate-limited warning emitted.
*/
int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_bank_ecc *bank)
{
struct ras_umc *ras_umc = &ras_core->ras_umc;
struct ras_bank_ecc_node *ecc_node;
+ mutex_lock(&ras_umc->pending_ecc_lock);
+ if (ras_umc->pending_ecc_count >= RAS_UMC_PENDING_ECC_MAX) {
+ ras_umc->pending_ecc_dropped++;
+ mutex_unlock(&ras_umc->pending_ecc_lock);
+ RAS_DEV_WARN_RATELIMITED(ras_core->dev,
+ "pending ECC list full (%u), dropping bad bank event (total dropped:%u)\n",
+ RAS_UMC_PENDING_ECC_MAX, ras_umc->pending_ecc_dropped);
+ return -ENOSPC;
+ }
+ mutex_unlock(&ras_umc->pending_ecc_lock);
+
ecc_node = kzalloc(sizeof(*ecc_node), GFP_KERNEL);
if (!ecc_node)
return -ENOMEM;
@@ -206,7 +223,15 @@ int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_b
memcpy(&ecc_node->ecc, bank, sizeof(ecc_node->ecc));
mutex_lock(&ras_umc->pending_ecc_lock);
+ /* re-check under the lock to honor the cap across concurrent callers */
+ if (ras_umc->pending_ecc_count >= RAS_UMC_PENDING_ECC_MAX) {
+ ras_umc->pending_ecc_dropped++;
+ mutex_unlock(&ras_umc->pending_ecc_lock);
+ kfree(ecc_node);
+ return -ENOSPC;
+ }
list_add_tail(&ecc_node->node, &ras_umc->pending_ecc_list);
+ ras_umc->pending_ecc_count++;
mutex_unlock(&ras_umc->pending_ecc_lock);
return 0;
@@ -225,8 +250,16 @@ int ras_umc_log_pending_bad_bank(struct ras_core_context *ras_core)
if (!ras_umc_log_bad_bank(ras_core, &ecc_node->ecc)) {
list_del(&ecc_node->node);
kfree(ecc_node);
+ if (ras_umc->pending_ecc_count)
+ ras_umc->pending_ecc_count--;
}
}
+ if (ras_umc->pending_ecc_dropped) {
+ RAS_DEV_WARN(ras_core->dev,
+ "%u pending ECC bad-bank events were dropped during GPU reset\n",
+ ras_umc->pending_ecc_dropped);
+ ras_umc->pending_ecc_dropped = 0;
+ }
mutex_unlock(&ras_umc->pending_ecc_lock);
return 0;
@@ -611,6 +644,8 @@ int ras_umc_sw_fini(struct ras_core_context *ras_core)
list_del(&ecc_node->node);
kfree(ecc_node);
}
+ ras_umc->pending_ecc_count = 0;
+ ras_umc->pending_ecc_dropped = 0;
mutex_unlock(&ras_umc->pending_ecc_lock);
mutex_destroy(&ras_umc->tree_lock);
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
index 1d3026be509b..237525b46b9b 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
@@ -139,8 +139,20 @@ struct ras_umc {
struct mutex pending_ecc_lock;
struct ras_umc_err_data umc_err_data;
struct list_head pending_ecc_list;
+ /* number of entries currently queued on pending_ecc_list */
+ u32 pending_ecc_count;
+ /* number of entries dropped because pending_ecc_list was full */
+ u32 pending_ecc_dropped;
};
+/*
+ * Upper bound on entries that can be queued on pending_ecc_list while a
+ * GPU reset is in progress. Beyond this, new ECC events are dropped to
+ * prevent unbounded kernel memory growth in case of an ECC storm or
+ * malicious/repeated UMC error injection.
+ */
+#define RAS_UMC_PENDING_ECC_MAX 8192
+
int ras_umc_sw_init(struct ras_core_context *ras);
int ras_umc_sw_fini(struct ras_core_context *ras);
int ras_umc_hw_init(struct ras_core_context *ras);
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/5] drm/amd/ras: snapshot remote cmd header to fix double-fetch
2026-05-18 9:40 [PATCH 1/5] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Stanley.Yang
2026-05-18 9:40 ` [PATCH 2/5] drm/amdgpu: init locals in umc_v12_0_convert_error_address Stanley.Yang
2026-05-18 9:40 ` [PATCH 3/5] drm/amd/ras: cap pending_ecc_list size Stanley.Yang
@ 2026-05-18 9:40 ` Stanley.Yang
2026-05-18 9:40 ` [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded helpers Stanley.Yang
3 siblings, 0 replies; 6+ messages in thread
From: Stanley.Yang @ 2026-05-18 9:40 UTC (permalink / raw)
To: amd-gfx; +Cc: Hawking.Zhang, Tao.Zhou1, YiPeng.Chai, Candice.Li, Stanley.Yang
The response header lives in PF-controlled shared memory. Copy it
into a local struct once, then read cmd_res and output_size from the
snapshot so the PF cannot flip cmd_res or grow output_size between
checks.
Signed-off-by: Stanley.Yang <Stanley.Yang@amd.com>
---
.../drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c | 29 +++++++++++++++----
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c
index 838eb91aef39..ebbf92a2bd94 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c
@@ -92,6 +92,7 @@ static int amdgpu_virt_ras_remote_ioctl_cmd(struct ras_core_context *ras_core,
struct amdgpu_virt_ras_cmd *virt_ras = ras_mgr->virt_ras_cmd;
uint32_t mem_len = ALIGN(sizeof(*cmd) + output_size, AMDGPU_GPU_PAGE_SIZE);
struct ras_cmd_ctx *rcmd;
+ struct ras_cmd_ctx hdr_snap;
struct amdgpu_virt_shared_mem shared_mem = {0};
int ret = 0;
@@ -108,15 +109,31 @@ static int amdgpu_virt_ras_remote_ioctl_cmd(struct ras_core_context *ras_core,
ret = amdgpu_virt_send_remote_ras_cmd(ras_core->dev,
shared_mem.gpa, mem_len);
if (!ret) {
- if (rcmd->cmd_res) {
- ret = rcmd->cmd_res;
+ /*
+ * rcmd lives in shared memory the PF can mutate at any time.
+ * Snapshot the entire fixed-size response header into a local
+ * struct in one shot so every subsequent decision (cmd_res,
+ * output_size, version, etc.) operates on a stable copy. This
+ * defeats double-fetch / TOCTOU attacks where a malicious or
+ * buggy PF could flip cmd_res from SUCCESS to an error after
+ * our success branch, or enlarge output_size between the
+ * bounds check and the memcpy below to corrupt the caller's
+ * local output buffer.
+ */
+ memcpy(&hdr_snap, rcmd, sizeof(hdr_snap));
+ barrier();
+
+ if (hdr_snap.cmd_res) {
+ ret = hdr_snap.cmd_res;
goto out;
}
- cmd->cmd_res = rcmd->cmd_res;
- cmd->output_size = rcmd->output_size;
- if (rcmd->output_size && (rcmd->output_size <= output_size) && output_data)
- memcpy(output_data, rcmd->output_buff_raw, rcmd->output_size);
+ cmd->cmd_res = hdr_snap.cmd_res;
+ cmd->output_size = hdr_snap.output_size;
+
+ if (hdr_snap.output_size && output_data &&
+ hdr_snap.output_size <= output_size)
+ memcpy(output_data, rcmd->output_buff_raw, hdr_snap.output_size);
}
out:
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded helpers
2026-05-18 9:40 [PATCH 1/5] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Stanley.Yang
` (2 preceding siblings ...)
2026-05-18 9:40 ` [PATCH 4/5] drm/amd/ras: snapshot remote cmd header to fix double-fetch Stanley.Yang
@ 2026-05-18 9:40 ` Stanley.Yang
2026-05-19 7:00 ` Zhou1, Tao
3 siblings, 1 reply; 6+ messages in thread
From: Stanley.Yang @ 2026-05-18 9:40 UTC (permalink / raw)
To: amd-gfx; +Cc: Hawking.Zhang, Tao.Zhou1, YiPeng.Chai, Candice.Li, Stanley.Yang
Replace the open-coded TLV walk with fru_pia_advance()
and fru_pia_copy_field() helpers that bound every read
by the actual EEPROM data length, preventing out-of-bounds
reads on truncated or malformed FRU data.
Signed-off-by: Stanley.Yang <Stanley.Yang@amd.com>
---
.../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c | 95 ++++++++++++-------
1 file changed, 63 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
index c5178e2b794d..86b2d5a79993 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
@@ -115,6 +115,43 @@ static bool is_fru_eeprom_supported(struct amdgpu_device *adev, u32 *fru_addr)
}
}
+/*
+ * IPMI FRU Product Info Area fields are TLV: one type/length byte
+ * (low 6 bits = data length) followed by that many data bytes. These
+ * helpers walk the cursor and copy a single field while bounding all
+ * accesses to the actual buffer length read from the EEPROM.
+ */
+#define FRU_FIELD_LEN(p, a) ((p)[a] & 0x3F)
+
+/* Advance cursor past the current TLV. Returns false if no more data. */
+static bool fru_pia_advance(u32 *addr, const unsigned char *pia, int len)
+{
+ if (*addr >= (u32)len)
+ return false;
+ *addr += 1 + FRU_FIELD_LEN(pia, *addr);
+ return true;
+}
+
+/*
+ * Copy the current TLV's data into dst (NUL-terminated). Returns false if
+ * the TLV header or data would read past the end of pia.
+ */
+static bool fru_pia_copy_field(char *dst, size_t dst_size,
+ const unsigned char *pia, u32 addr, int len)
+{
+ size_t fl;
+
+ if (addr + 1 >= (u32)len)
+ return false;
+
+ fl = min3((size_t)FRU_FIELD_LEN(pia, addr),
+ dst_size -1,
+ (size_t)(len - addr - 1));
+ memcpy(dst, pia + addr + 1, fl);
+ dst[fl] = '\0';
+ return true;
+}
+
int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
{
struct amdgpu_fru_info *fru_info;
@@ -223,52 +260,46 @@ int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
* Read Manufacturer Name field whose length is [3].
*/
addr = 3;
- if (addr + 1 >= len)
+ if (!fru_pia_copy_field(fru_info->manufacturer_name,
+ sizeof(fru_info->manufacturer_name),
+ pia, addr, len))
goto Out;
- memcpy(fru_info->manufacturer_name, pia + addr + 1,
- min_t(size_t, sizeof(fru_info->manufacturer_name),
- pia[addr] & 0x3F));
- fru_info->manufacturer_name[sizeof(fru_info->manufacturer_name) - 1] =
- '\0';
/* Read Product Name field. */
- addr += 1 + (pia[addr] & 0x3F);
- if (addr + 1 >= len)
+ if (!fru_pia_advance(&addr, pia, len) ||
+ !fru_pia_copy_field(fru_info->product_name,
+ sizeof(fru_info->product_name),
+ pia, addr, len))
goto Out;
- memcpy(fru_info->product_name, pia + addr + 1,
- min_t(size_t, sizeof(fru_info->product_name), pia[addr] & 0x3F));
- fru_info->product_name[sizeof(fru_info->product_name) - 1] = '\0';
/* Go to the Product Part/Model Number field. */
- addr += 1 + (pia[addr] & 0x3F);
- if (addr + 1 >= len)
+ if (!fru_pia_advance(&addr, pia, len) ||
+ !fru_pia_copy_field(fru_info->product_number,
+ sizeof(fru_info->product_number),
+ pia, addr, len))
goto Out;
- memcpy(fru_info->product_number, pia + addr + 1,
- min_t(size_t, sizeof(fru_info->product_number),
- pia[addr] & 0x3F));
- fru_info->product_number[sizeof(fru_info->product_number) - 1] = '\0';
- /* Go to the Product Version field. */
- addr += 1 + (pia[addr] & 0x3F);
+ /* Skip the Product Version field. */
+ if (!fru_pia_advance(&addr, pia, len))
+ goto Out;
- /* Go to the Product Serial Number field. */
- addr += 1 + (pia[addr] & 0x3F);
- if (addr + 1 >= len)
+ /* Read the Product Serial Number field. */
+ if (!fru_pia_advance(&addr, pia, len) ||
+ !fru_pia_copy_field(fru_info->serial,
+ sizeof(fru_info->serial),
+ pia, addr, len))
goto Out;
- memcpy(fru_info->serial, pia + addr + 1,
- min_t(size_t, sizeof(fru_info->serial), pia[addr] & 0x3F));
- fru_info->serial[sizeof(fru_info->serial) - 1] = '\0';
- /* Asset Tag field */
- addr += 1 + (pia[addr] & 0x3F);
+ /* Skip the Asset Tag field. */
+ if (!fru_pia_advance(&addr, pia, len))
+ goto Out;
/* FRU File Id field. This could be 'null'. */
- addr += 1 + (pia[addr] & 0x3F);
- if ((addr + 1 >= len) || !(pia[addr] & 0x3F))
+ if (!fru_pia_advance(&addr, pia, len) ||
+ !fru_pia_copy_field(fru_info->fru_id,
+ sizeof(fru_info->fru_id),
+ pia, addr, len))
goto Out;
- memcpy(fru_info->fru_id, pia + addr + 1,
- min_t(size_t, sizeof(fru_info->fru_id), pia[addr] & 0x3F));
- fru_info->fru_id[sizeof(fru_info->fru_id) - 1] = '\0';
Out:
kfree(pia);
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* RE: [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded helpers
2026-05-18 9:40 ` [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded helpers Stanley.Yang
@ 2026-05-19 7:00 ` Zhou1, Tao
0 siblings, 0 replies; 6+ messages in thread
From: Zhou1, Tao @ 2026-05-19 7:00 UTC (permalink / raw)
To: Yang, Stanley, amd-gfx@lists.freedesktop.org
Cc: Zhang, Hawking, Chai, Thomas, Li, Candice
AMD General
The series is: Reviewed-by: Tao Zhou <tao.zhou1@amd.com>
> -----Original Message-----
> From: Yang, Stanley <Stanley.Yang@amd.com>
> Sent: Monday, May 18, 2026 5:40 PM
> To: amd-gfx@lists.freedesktop.org
> Cc: Zhang, Hawking <Hawking.Zhang@amd.com>; Zhou1, Tao
> <Tao.Zhou1@amd.com>; Chai, Thomas <YiPeng.Chai@amd.com>; Li, Candice
> <Candice.Li@amd.com>; Yang, Stanley <Stanley.Yang@amd.com>
> Subject: [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded
> helpers
>
> Replace the open-coded TLV walk with fru_pia_advance() and
> fru_pia_copy_field() helpers that bound every read by the actual EEPROM
> data length, preventing out-of-bounds reads on truncated or malformed FRU
> data.
>
> Signed-off-by: Stanley.Yang <Stanley.Yang@amd.com>
> ---
> .../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c | 95 ++++++++++++-------
> 1 file changed, 63 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> index c5178e2b794d..86b2d5a79993 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> @@ -115,6 +115,43 @@ static bool is_fru_eeprom_supported(struct
> amdgpu_device *adev, u32 *fru_addr)
> }
> }
>
> +/*
> + * IPMI FRU Product Info Area fields are TLV: one type/length byte
> + * (low 6 bits = data length) followed by that many data bytes. These
> + * helpers walk the cursor and copy a single field while bounding all
> + * accesses to the actual buffer length read from the EEPROM.
> + */
> +#define FRU_FIELD_LEN(p, a) ((p)[a] & 0x3F)
> +
> +/* Advance cursor past the current TLV. Returns false if no more data.
> +*/ static bool fru_pia_advance(u32 *addr, const unsigned char *pia, int
> +len) {
> + if (*addr >= (u32)len)
> + return false;
> + *addr += 1 + FRU_FIELD_LEN(pia, *addr);
> + return true;
> +}
> +
> +/*
> + * Copy the current TLV's data into dst (NUL-terminated). Returns false
> +if
> + * the TLV header or data would read past the end of pia.
> + */
> +static bool fru_pia_copy_field(char *dst, size_t dst_size,
> + const unsigned char *pia, u32 addr, int len) {
> + size_t fl;
> +
> + if (addr + 1 >= (u32)len)
> + return false;
> +
> + fl = min3((size_t)FRU_FIELD_LEN(pia, addr),
> + dst_size -1,
> + (size_t)(len - addr - 1));
> + memcpy(dst, pia + addr + 1, fl);
> + dst[fl] = '\0';
> + return true;
> +}
> +
> int amdgpu_fru_get_product_info(struct amdgpu_device *adev) {
> struct amdgpu_fru_info *fru_info;
> @@ -223,52 +260,46 @@ int amdgpu_fru_get_product_info(struct
> amdgpu_device *adev)
> * Read Manufacturer Name field whose length is [3].
> */
> addr = 3;
> - if (addr + 1 >= len)
> + if (!fru_pia_copy_field(fru_info->manufacturer_name,
> + sizeof(fru_info->manufacturer_name),
> + pia, addr, len))
> goto Out;
> - memcpy(fru_info->manufacturer_name, pia + addr + 1,
> - min_t(size_t, sizeof(fru_info->manufacturer_name),
> - pia[addr] & 0x3F));
> - fru_info->manufacturer_name[sizeof(fru_info->manufacturer_name)
> - 1] =
> - '\0';
>
> /* Read Product Name field. */
> - addr += 1 + (pia[addr] & 0x3F);
> - if (addr + 1 >= len)
> + if (!fru_pia_advance(&addr, pia, len) ||
> + !fru_pia_copy_field(fru_info->product_name,
> + sizeof(fru_info->product_name),
> + pia, addr, len))
> goto Out;
> - memcpy(fru_info->product_name, pia + addr + 1,
> - min_t(size_t, sizeof(fru_info->product_name), pia[addr] & 0x3F));
> - fru_info->product_name[sizeof(fru_info->product_name) - 1] = '\0';
>
> /* Go to the Product Part/Model Number field. */
> - addr += 1 + (pia[addr] & 0x3F);
> - if (addr + 1 >= len)
> + if (!fru_pia_advance(&addr, pia, len) ||
> + !fru_pia_copy_field(fru_info->product_number,
> + sizeof(fru_info->product_number),
> + pia, addr, len))
> goto Out;
> - memcpy(fru_info->product_number, pia + addr + 1,
> - min_t(size_t, sizeof(fru_info->product_number),
> - pia[addr] & 0x3F));
> - fru_info->product_number[sizeof(fru_info->product_number) - 1] =
> '\0';
>
> - /* Go to the Product Version field. */
> - addr += 1 + (pia[addr] & 0x3F);
> + /* Skip the Product Version field. */
> + if (!fru_pia_advance(&addr, pia, len))
> + goto Out;
>
> - /* Go to the Product Serial Number field. */
> - addr += 1 + (pia[addr] & 0x3F);
> - if (addr + 1 >= len)
> + /* Read the Product Serial Number field. */
> + if (!fru_pia_advance(&addr, pia, len) ||
> + !fru_pia_copy_field(fru_info->serial,
> + sizeof(fru_info->serial),
> + pia, addr, len))
> goto Out;
> - memcpy(fru_info->serial, pia + addr + 1,
> - min_t(size_t, sizeof(fru_info->serial), pia[addr] & 0x3F));
> - fru_info->serial[sizeof(fru_info->serial) - 1] = '\0';
>
> - /* Asset Tag field */
> - addr += 1 + (pia[addr] & 0x3F);
> + /* Skip the Asset Tag field. */
> + if (!fru_pia_advance(&addr, pia, len))
> + goto Out;
>
> /* FRU File Id field. This could be 'null'. */
> - addr += 1 + (pia[addr] & 0x3F);
> - if ((addr + 1 >= len) || !(pia[addr] & 0x3F))
> + if (!fru_pia_advance(&addr, pia, len) ||
> + !fru_pia_copy_field(fru_info->fru_id,
> + sizeof(fru_info->fru_id),
> + pia, addr, len))
> goto Out;
> - memcpy(fru_info->fru_id, pia + addr + 1,
> - min_t(size_t, sizeof(fru_info->fru_id), pia[addr] & 0x3F));
> - fru_info->fru_id[sizeof(fru_info->fru_id) - 1] = '\0';
>
> Out:
> kfree(pia);
> --
> 2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-05-19 7:00 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-18 9:40 [PATCH 1/5] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Stanley.Yang
2026-05-18 9:40 ` [PATCH 2/5] drm/amdgpu: init locals in umc_v12_0_convert_error_address Stanley.Yang
2026-05-18 9:40 ` [PATCH 3/5] drm/amd/ras: cap pending_ecc_list size Stanley.Yang
2026-05-18 9:40 ` [PATCH 4/5] drm/amd/ras: snapshot remote cmd header to fix double-fetch Stanley.Yang
2026-05-18 9:40 ` [PATCH 5/5] drm/amdgpu: harden FRU PIA parsing with bounded helpers Stanley.Yang
2026-05-19 7:00 ` Zhou1, Tao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox