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

ACPI: tables: x86: Reserve memory occupied by ACPI tables

The following problem has been reported by George Kennedy:

Since commit 7fef431be9c9 ("mm/page_alloc: place pages to tail
in __free_pages_core()") the following use after free occurs
intermittently when ACPI tables are accessed.

BUG: KASAN: use-after-free in ibft_init+0x134/0xc49
Read of size 4 at addr ffff8880be453004 by task swapper/0/1
CPU: 3 PID: 1 Comm: swapper/0 Not tainted 5.12.0-rc1-7a7fd0d #1
Call Trace:
dump_stack+0xf6/0x158
print_address_description.constprop.9+0x41/0x60
kasan_report.cold.14+0x7b/0xd4
__asan_report_load_n_noabort+0xf/0x20
ibft_init+0x134/0xc49
do_one_initcall+0xc4/0x3e0
kernel_init_freeable+0x5af/0x66b
kernel_init+0x16/0x1d0
ret_from_fork+0x22/0x30

ACPI tables mapped via kmap() do not have their mapped pages
reserved and the pages can be "stolen" by the buddy allocator.

Apparently, on the affected system, the ACPI table in question is
not located in "reserved" memory, like ACPI NVS or ACPI Data, that
will not be used by the buddy allocator, so the memory occupied by
that table has to be explicitly reserved to prevent the buddy
allocator from using it.

In order to address this problem, rearrange the initialization of the
ACPI tables on x86 to locate the initial tables earlier and reserve
the memory occupied by them.

The other architectures using ACPI should not be affected by this
change.

Link: https://lore.kernel.org/linux-acpi/1614802160-29362-1-git-send-email-george.kennedy@oracle.com/
Reported-by: George Kennedy <george.kennedy@oracle.com>
Tested-by: George Kennedy <george.kennedy@oracle.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Mike Rapoport <rppt@linux.ibm.com>
Cc: 5.10+ <stable@vger.kernel.org> # 5.10+

+62 -22
+12 -13
arch/x86/kernel/acpi/boot.c
··· 1554 1554 /* 1555 1555 * Initialize the ACPI boot-time table parser. 1556 1556 */ 1557 - if (acpi_table_init()) { 1557 + if (acpi_locate_initial_tables()) 1558 1558 disable_acpi(); 1559 - return; 1560 - } 1559 + else 1560 + acpi_reserve_initial_tables(); 1561 + } 1562 + 1563 + int __init early_acpi_boot_init(void) 1564 + { 1565 + if (acpi_disabled) 1566 + return 1; 1567 + 1568 + acpi_table_init_complete(); 1561 1569 1562 1570 acpi_table_parse(ACPI_SIG_BOOT, acpi_parse_sbf); 1563 1571 ··· 1578 1570 } else { 1579 1571 printk(KERN_WARNING PREFIX "Disabling ACPI support\n"); 1580 1572 disable_acpi(); 1581 - return; 1573 + return 1; 1582 1574 } 1583 1575 } 1584 - } 1585 - 1586 - int __init early_acpi_boot_init(void) 1587 - { 1588 - /* 1589 - * If acpi_disabled, bail out 1590 - */ 1591 - if (acpi_disabled) 1592 - return 1; 1593 1576 1594 1577 /* 1595 1578 * Process the Multiple APIC Description Table (MADT), if present
+3 -5
arch/x86/kernel/setup.c
··· 1045 1045 1046 1046 cleanup_highmap(); 1047 1047 1048 + /* Look for ACPI tables and reserve memory occupied by them. */ 1049 + acpi_boot_table_init(); 1050 + 1048 1051 memblock_set_current_limit(ISA_END_ADDRESS); 1049 1052 e820__memblock_setup(); 1050 1053 ··· 1138 1135 io_delay_init(); 1139 1136 1140 1137 early_platform_quirks(); 1141 - 1142 - /* 1143 - * Parse the ACPI tables for possible boot-time SMP configuration. 1144 - */ 1145 - acpi_boot_table_init(); 1146 1138 1147 1139 early_acpi_boot_init(); 1148 1140
+39 -3
drivers/acpi/tables.c
··· 780 780 } 781 781 782 782 /* 783 - * acpi_table_init() 783 + * acpi_locate_initial_tables() 784 784 * 785 785 * find RSDP, find and checksum SDT/XSDT. 786 786 * checksum all tables, print SDT/XSDT ··· 788 788 * result: sdt_entry[] is initialized 789 789 */ 790 790 791 - int __init acpi_table_init(void) 791 + int __init acpi_locate_initial_tables(void) 792 792 { 793 793 acpi_status status; 794 794 ··· 803 803 status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0); 804 804 if (ACPI_FAILURE(status)) 805 805 return -EINVAL; 806 - acpi_table_initrd_scan(); 807 806 807 + return 0; 808 + } 809 + 810 + void __init acpi_reserve_initial_tables(void) 811 + { 812 + int i; 813 + 814 + for (i = 0; i < ACPI_MAX_TABLES; i++) { 815 + struct acpi_table_desc *table_desc = &initial_tables[i]; 816 + u64 start = table_desc->address; 817 + u64 size = table_desc->length; 818 + 819 + if (!start || !size) 820 + break; 821 + 822 + pr_info("Reserving %4s table memory at [mem 0x%llx-0x%llx]\n", 823 + table_desc->signature.ascii, start, start + size - 1); 824 + 825 + memblock_reserve(start, size); 826 + } 827 + } 828 + 829 + void __init acpi_table_init_complete(void) 830 + { 831 + acpi_table_initrd_scan(); 808 832 check_multiple_madt(); 833 + } 834 + 835 + int __init acpi_table_init(void) 836 + { 837 + int ret; 838 + 839 + ret = acpi_locate_initial_tables(); 840 + if (ret) 841 + return ret; 842 + 843 + acpi_table_init_complete(); 844 + 809 845 return 0; 810 846 } 811 847
+8 -1
include/linux/acpi.h
··· 222 222 void __acpi_unmap_table(void __iomem *map, unsigned long size); 223 223 int early_acpi_boot_init(void); 224 224 int acpi_boot_init (void); 225 + void acpi_boot_table_prepare (void); 225 226 void acpi_boot_table_init (void); 226 227 int acpi_mps_check (void); 227 228 int acpi_numa_init (void); 228 229 230 + int acpi_locate_initial_tables (void); 231 + void acpi_reserve_initial_tables (void); 232 + void acpi_table_init_complete (void); 229 233 int acpi_table_init (void); 230 234 int acpi_table_parse(char *id, acpi_tbl_table_handler handler); 231 235 int __init acpi_table_parse_entries(char *id, unsigned long table_size, ··· 818 814 return 0; 819 815 } 820 816 817 + static inline void acpi_boot_table_prepare(void) 818 + { 819 + } 820 + 821 821 static inline void acpi_boot_table_init(void) 822 822 { 823 - return; 824 823 } 825 824 826 825 static inline int acpi_mps_check(void)