Skip to contentSkip to conversation

firmware: coreboot: Check size of table entry and split memcpy

PATCH Superseded Kees Cook 3 participants 4 messages Jan 7, 2023 – Jan 12, 2023

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Kees Cook

author
#0 lore raw

PATCH v2 1/1 firmware: coreboot: Check size of table entry and split memcpy

The memcpy() of the data following a coreboot_table_entry couldn't be evaluated by the compiler under CONFIG_FORTIFY_SOURCE. To make it easier to reason about, add an explicit flexible array member to struct coreboot_device so the entire entry can be copied at once. Additionally, validate the sizes before copying. Avoids this run-time false positive warning:

memcpy: detected field-spanning write (size 168) of single field "&device->entry" at drivers/firmware/google/coreboot_table.c:103 (size 8)

---
v2: move flex array to struct coreboot_device (julius)
v1: https://lore.kernel.org/lkml/[email protected]

---
 drivers/firmware/google/coreboot_table.c | 9 +++++++--
 drivers/firmware/google/coreboot_table.h | 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/firmware/google/coreboot_table.c +7 -2
index 2652c396c423..564a3c908838 100644
@@ -93,14 +93,19 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
 	for (i = 0; i < header->table_entries; i++) {
 		entry = ptr_entry;
 
-		device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
+		if (entry->size < sizeof(*entry)) {
+			dev_warn(dev, "coreboot table entry too small!\n");
+			return -EINVAL;
+		}
+
+		device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL);
 		if (!device)
 			return -ENOMEM;
 
 		device->dev.parent = dev;
 		device->dev.bus = &coreboot_bus_type;
 		device->dev.release = coreboot_device_release;
-		memcpy(&device->entry, ptr_entry, entry->size);
+		memcpy(device->raw, entry, entry->size);
 
 		switch (device->entry.tag) {
 		case LB_TAG_CBMEM_ENTRY:
drivers/firmware/google/coreboot_table.h +1 -0
index 37f4d335a606..d814dca33a08 100644
@@ -79,6 +79,7 @@ struct coreboot_device {
 		struct lb_cbmem_ref cbmem_ref;
 		struct lb_cbmem_entry cbmem_entry;
 		struct lb_framebuffer framebuffer;
+		DECLARE_FLEX_ARRAY(u8, raw);
 	};
 };
 
Signature · 1 line
2.34.1
Reviewed-by on v2 1/1
#1 lore raw
Quoted 10 lines · Kees Cook

The memcpy() of the data following a coreboot_table_entry couldn't be evaluated by the compiler under CONFIG_FORTIFY_SOURCE. To make it easier to reason about, add an explicit flexible array member to struct coreboot_device so the entire entry can be copied at once. Additionally, validate the sizes before copying. Avoids this run-time false positive warning:

memcpy: detected field-spanning write (size 168) of single field "&device->entry" at drivers/firmware/google/coreboot_table.c:103 (size 8)

Quoted 9 lines
Quoted 49 lines

---
v2: move flex array to struct coreboot_device (julius)
v1: https://lore.kernel.org/lkml/[email protected]

---
 drivers/firmware/google/coreboot_table.c | 9 +++++++--
 drivers/firmware/google/coreboot_table.h | 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/firmware/google/coreboot_table.c +7 -2
index 2652c396c423..564a3c908838 100644
@@ -93,14 +93,19 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
        for (i = 0; i < header->table_entries; i++) {
                entry = ptr_entry;
 
-               device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
+               if (entry->size < sizeof(*entry)) {
+                       dev_warn(dev, "coreboot table entry too small!\n");
+                       return -EINVAL;
+               }
+
+               device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL);
                if (!device)
                        return -ENOMEM;
 
                device->dev.parent = dev;
                device->dev.bus = &coreboot_bus_type;
                device->dev.release = coreboot_device_release;
-               memcpy(&device->entry, ptr_entry, entry->size);
+               memcpy(device->raw, entry, entry->size);
 
                switch (device->entry.tag) {
                case LB_TAG_CBMEM_ENTRY:
drivers/firmware/google/coreboot_table.h +1 -1
index 37f4d335a606..d814dca33a08 100644
@@ -79,6 +79,7 @@ struct coreboot_device {
                struct lb_cbmem_ref cbmem_ref;
                struct lb_cbmem_entry cbmem_entry;
                struct lb_framebuffer framebuffer;
+               DECLARE_FLEX_ARRAY(u8, raw);
        };
 };
 
--

2.34.1

Reviewed-by on v2 1/1
#2 lore raw
In reply to Kees Cook

drivers/firmware/google/coreboot_table.c static int coreboot_table_populate(struct device *dev, void *ptr) · line 108

Quoted 2 lines
drivers/firmware/google/coreboot_table.c +1 -1
-               memcpy(&device->entry, ptr_entry, entry->size);
+               memcpy(device->raw, entry, entry->size);

nit: It's a bit odd to change the source pointer from ptr_entry to entry here. Technically the static analyzer would be within its rights to give you a warning for that as well, because you're now "overrunning" the source struct instead of the destination one.

author
#3 lore raw
Quoted 9 lines · Julius Werner

drivers/firmware/google/coreboot_table.c static int coreboot_table_populate(struct device *dev, void *ptr) · line 108

Quoted 2 lines
drivers/firmware/google/coreboot_table.c +1 -1
-               memcpy(&device->entry, ptr_entry, entry->size);
+               memcpy(device->raw, entry, entry->size);

nit: It's a bit odd to change the source pointer from ptr_entry to entry here. Technically the static analyzer would be within its rights to give you a warning for that as well, because you're now "overrunning" the source struct instead of the destination one.

True. We've been focused on write overflows, but yeah, since the location of the flex array changed, I'll switch this back to ptr_entry.

Signature · 1 line
Kees Cook