From 412746bb6652ddc6fa435bc11a9b1253d63bf8a6 Mon Sep 17 00:00:00 2001 From: Vlatko Kosturjak Date: Mon, 31 Aug 2026 06:51:04 +0200 Subject: ACPI: OSL: Use vsnprintf() in acpi_os_vprintf() acpi_os_vprintf() formats every ACPICA diagnostic into a 512-byte static buffer with an unbounded vsprintf(): static char buffer[512]; vsprintf(buffer, fmt, args); ACPICA interpolates table-derived namespace paths into its messages, so the length is driven by table content. A malformed ACPI table can therefore produce a diagnostic longer than the buffer and overwrite whatever follows it. From the System.map of an x86_64_defconfig build, acpi_ioremap_lock sits 624 bytes into that buffer - 112 bytes past its end - so bytes 624..631 of an over-long message land on the mutex's owner field, which __mutex_lock() then dereferences. The overwritten bytes are attacker-influenced but not attacker-controlled: they are printable ASCII from a NUL-terminated string, so the resulting pointer is always non-canonical and always faults. This is a reliable denial of service, not a demonstrated pointer-control primitive. Because acpi_tb_load_namespace() parses the DSDT and every SSDT during acpi_init(), a table supplied by firmware triggers this in PID 1 inside do_one_initcall(), panicking the kernel before userspace exists. Bound the write with vsnprintf(). Truncating a diagnostic is strictly better than corrupting adjacent kernel objects. Tested on pristine Linux 7.2.2 - tag: v7.2.2., Stock x86_64_defconfig (no KASAN, no debug options), table supplied by firmware via "qemu -acpitable": [ 0.022259] ACPI: SSDT 0x000000003FFE1C52 000640 (v01 _ASUS_ Notebook 00000005 MSFT 0100000D) [ 0.353684] Oops: general protection fault, probably for non-canonical address 0x41435f2e345f2e64: 0000 [#1] SMP NOPTI [ 0.353727] RIP: 0010:__mutex_lock.constprop.0+0xbd/0xa40 [ 0.353727] RAX: 41435f2e345f2e30 RBX: 00000000fed00000 [ 0.353727] R15: ffffffff8c248530 [ 0.353727] Call Trace: [ 0.353727] acpi_os_map_iomem [ 0.353727] acpi_ex_system_memory_space_handler [ 0.353727] acpi_ev_address_space_dispatch [ 0.353727] acpi_ex_access_region [ 0.353727] acpi_ex_read_data_from_field [ 0.353727] acpi_ex_resolve_node_to_value [ 0.353727] acpi_ds_evaluate_name_path [ 0.353727] acpi_ps_parse_loop [ 0.353727] acpi_ps_execute_method [ 0.354729] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000009 RAX is the corrupted owner; the faulting address is RAX + 0x34. R15 matches acpi_ioremap_lock. On a KASAN build the overflow is reported directly as: BUG: KASAN: global-out-of-bounds in string. Signed-off-by: Vlatko Kosturjak [ rjw: Subject tweak ] Link: https://patch.msgid.link/CAPAw8HEMdsNN6WhizHkv2Orp2pkQ2-g009XCGFnKnhXoRhcKZQ@mail.gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index ed2162a97bd2..5b7aefb48eeb 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -159,7 +159,7 @@ void __printf(1, 0) acpi_os_vprintf(const char *fmt, va_list args) { static char buffer[512]; - vsprintf(buffer, fmt, args); + vsnprintf(buffer, sizeof(buffer), fmt, args); #ifdef ENABLE_DEBUGGER if (acpi_in_debugger) { -- cgit v1.2.3