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 <[email protected]>
---
 .../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

Reply via email to