]> git.itanic.dy.fi Git - linux-stable/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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Jan 2021 14:38:16 +0000 (15:38 +0100)
commit a58015d638cd4e4555297b04bec9b49028369075 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/acpi/internal.h
drivers/acpi/scan.c

index b012e94b7d9f9787f008ad1208b88eb9fe2e488a..eae0b278d517230b0c933aa084490d871e725fc5 100644 (file)
@@ -98,7 +98,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 9a7f017dda47f151aaeb5b58ee7b119bee62c1b6..f49e2b7880ac25b69fd6ac82adb63720c2a1873d 100644 (file)
@@ -485,6 +485,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;
@@ -673,7 +674,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);
        }
@@ -708,6 +716,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: