]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ACPI: scan: Harden acpi_device_add() against device ID overflows
authorDexuan Cui <decui@microsoft.com>
Fri, 8 Jan 2021 07:23:48 +0000 (23:23 -0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Jan 2021 19:45:31 +0000 (20:45 +0100)
Linux VM on Hyper-V crashes with the latest mainline:

[    4.069624] detected buffer overflow in strcpy
[    4.077733] kernel BUG at lib/string.c:1149!
..
[    4.085819] RIP: 0010:fortify_panic+0xf/0x11
...
[    4.085819] Call Trace:
[    4.085819]  acpi_device_add.cold.15+0xf2/0xfb
[    4.085819]  acpi_add_single_object+0x2a6/0x690
[    4.085819]  acpi_bus_check_add+0xc6/0x280
[    4.085819]  acpi_ns_walk_namespace+0xda/0x1aa
[    4.085819]  acpi_walk_namespace+0x9a/0xc2
[    4.085819]  acpi_bus_scan+0x78/0x90
[    4.085819]  acpi_scan_init+0xfa/0x248
[    4.085819]  acpi_init+0x2c1/0x321
[    4.085819]  do_one_initcall+0x44/0x1d0
[    4.085819]  kernel_init_freeable+0x1ab/0x1f4

This is because of the recent buffer overflow detection in the
commit 6a39e62abbaf ("lib: string.h: detect intra-object overflow in
fortified string functions")

Here acpi_device_bus_id->bus_id can only hold 14 characters, while the
the acpi_device_hid(device) returns a 22-char string
"HYPER_V_GEN_COUNTER_V1".

Per ACPI Spec v6.2, Section 6.1.5 _HID (Hardware ID), if the ID is a
string, it must be of the form AAA#### or NNNN####, i.e. 7 chars or 8
chars.

The field bus_id in struct acpi_device_bus_id was originally defined as
char bus_id[9], and later was enlarged to char bus_id[15] in 2007 in the
commit bb0958544f3c ("ACPI: use more understandable bus_id for ACPI
devices")

Fix the issue by changing the field bus_id to const char *, and use
kstrdup_const() to initialize it.

Signed-off-by: Dexuan Cui <decui@microsoft.com>
Tested-By: Jethro Beekman <jethro@fortanix.com>
[ rjw: Subject change, whitespace adjustment ]
Cc: All applicable <stable@vger.kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/internal.h
drivers/acpi/scan.c

index cb229e24c56375b0a89255667436abc46fa9b0d3..e6a5d997241c43d803d9758b01328db8b6cdb0cc 100644 (file)
@@ -97,7 +97,7 @@ void acpi_scan_table_handler(u32 event, void *table, void *context);
 extern struct list_head acpi_bus_id_list;
 
 struct acpi_device_bus_id {
-       char bus_id[15];
+       const char *bus_id;
        unsigned int instance_no;
        struct list_head node;
 };
index 80b668c80073a58417e6bd6a2ad0d1ddc0f28d31..58ff36340cd7c595521e753897da6d2dfc2a91be 100644 (file)
@@ -486,6 +486,7 @@ static void acpi_device_del(struct acpi_device *device)
                                acpi_device_bus_id->instance_no--;
                        else {
                                list_del(&acpi_device_bus_id->node);
+                               kfree_const(acpi_device_bus_id->bus_id);
                                kfree(acpi_device_bus_id);
                        }
                        break;
@@ -674,7 +675,14 @@ int acpi_device_add(struct acpi_device *device,
        }
        if (!found) {
                acpi_device_bus_id = new_bus_id;
-               strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device));
+               acpi_device_bus_id->bus_id =
+                       kstrdup_const(acpi_device_hid(device), GFP_KERNEL);
+               if (!acpi_device_bus_id->bus_id) {
+                       pr_err(PREFIX "Memory allocation error for bus id\n");
+                       result = -ENOMEM;
+                       goto err_free_new_bus_id;
+               }
+
                acpi_device_bus_id->instance_no = 0;
                list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
        }
@@ -709,6 +717,11 @@ int acpi_device_add(struct acpi_device *device,
        if (device->parent)
                list_del(&device->node);
        list_del(&device->wakeup_list);
+
+ err_free_new_bus_id:
+       if (!found)
+               kfree(new_bus_id);
+
        mutex_unlock(&acpi_device_lock);
 
  err_detach: