Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI: scan: Harden acpi_device_add() against device ID overflows

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>

authored by

Dexuan Cui and committed by
Rafael J. Wysocki
a58015d6 7c53f6b6

+15 -2
+1 -1
drivers/acpi/internal.h
··· 97 97 extern struct list_head acpi_bus_id_list; 98 98 99 99 struct acpi_device_bus_id { 100 - char bus_id[15]; 100 + const char *bus_id; 101 101 unsigned int instance_no; 102 102 struct list_head node; 103 103 };
+14 -1
drivers/acpi/scan.c
··· 486 486 acpi_device_bus_id->instance_no--; 487 487 else { 488 488 list_del(&acpi_device_bus_id->node); 489 + kfree_const(acpi_device_bus_id->bus_id); 489 490 kfree(acpi_device_bus_id); 490 491 } 491 492 break; ··· 675 674 } 676 675 if (!found) { 677 676 acpi_device_bus_id = new_bus_id; 678 - strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device)); 677 + acpi_device_bus_id->bus_id = 678 + kstrdup_const(acpi_device_hid(device), GFP_KERNEL); 679 + if (!acpi_device_bus_id->bus_id) { 680 + pr_err(PREFIX "Memory allocation error for bus id\n"); 681 + result = -ENOMEM; 682 + goto err_free_new_bus_id; 683 + } 684 + 679 685 acpi_device_bus_id->instance_no = 0; 680 686 list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); 681 687 } ··· 717 709 if (device->parent) 718 710 list_del(&device->node); 719 711 list_del(&device->wakeup_list); 712 + 713 + err_free_new_bus_id: 714 + if (!found) 715 + kfree(new_bus_id); 716 + 720 717 mutex_unlock(&acpi_device_lock); 721 718 722 719 err_detach: