arm64/acpi: restrict AML opregion memory access
Subsystems (3)
v3 cover arm64/acpi: restrict AML opregion memory access
v2:
- do a more elaborate check on the region, against the EFI memory map
v3:
- split into two patches
- fallback to __ioremap() for ACPI reclaim memory, in case it is not covered by the linear mapping (e.g., when booting a kdump kernel)
Ard Biesheuvel (2):
arm64/acpi: disallow AML memory opregions to access kernel memory arm64/acpi: disallow writeable AML opregion mapping for EFI code
regions
arch/arm64/include/asm/acpi.h | 15 +--- arch/arm64/kernel/acpi.c | 75 ++++++++++++++++++++ 2 files changed, 76 insertions(+), 14 deletions(-)
Signature · 1 line
2.27.0
PATCH v3 1/2 arm64/acpi: disallow AML memory opregions to access kernel memory
AML uses SystemMemory opregions to allow AML handlers to access MMIO registers of, e.g., GPIO controllers, or access reserved regions of memory that are owned by the firmware.
Currently, we also allow AML access to memory that is owned by the kernel and mapped via the linear region, which does not seem to be supported by a valid use case, and exposes the kernel's internal state to AML methods that may be buggy and exploitable.
On arm64, ACPI support requires booting in EFI mode, and so we can cross reference the requested region against the EFI memory map, rather than just do a minimal check on the first page. So let's only permit regions to be remapped by the ACPI core if
- they don't appear in the EFI memory map at all (which is the case for most MMIO), or
- they are covered by a single region in the EFI memory map, which is not of a type that describes memory that is given to the kernel at boot.
--- arch/arm64/include/asm/acpi.h | 15 +---- arch/arm64/kernel/acpi.c | 66 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-)
arch/arm64/include/asm/acpi.h +1 -14
@@ -47,20 +47,7 @@pgprot_t __acpi_get_mem_attribute (phys_addr_t addr);/* ACPI table mapping after acpi_permanent_mmap is set */ -static inline void __iomem *acpi_os_ioremap (acpi_physical_address phys, -acpi_size size) -{ -/* For normal memory we already have a cacheable mapping. */ -if (memblock_is_map_memory (phys)) -return (void __iomem *)__phys_to_virt (phys); - -/* -* We should still honor the memory's attribute here because -* crash dump kernel possibly excludes some ACPI (reclaim) -* regions from memblock list. -*/ -return __ioremap (phys, size,__acpi_get_mem_attribute (phys)); -} +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size);#define acpi_os_ioremap acpi_os_ioremaptypedef u64 phys_cpuid_t ;
arch/arm64/kernel/acpi.c +66 -0
72 lines not on this page · raw message
Signature · 1 line
2.27.0
Re: [PATCH v3 1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
Quoted 18 lines · On Fri, 26 Jun 2020 17:58:31 +0200 Ard Biesheuvel <[email protected]> wrote:
AML uses SystemMemory opregions to allow AML handlers to access MMIO registers of, e.g., GPIO controllers, or access reserved regions of memory that are owned by the firmware.
Currently, we also allow AML access to memory that is owned by the kernel and mapped via the linear region, which does not seem to be supported by a valid use case, and exposes the kernel's internal state to AML methods that may be buggy and exploitable.
On arm64, ACPI support requires booting in EFI mode, and so we can cross reference the requested region against the EFI memory map, rather than just do a minimal check on the first page. So let's only permit regions to be remapped by the ACPI core if
- they don't appear in the EFI memory map at all (which is the case for most MMIO), or
- they are covered by a single region in the EFI memory map, which is not of a type that describes memory that is given to the kernel at boot.
Quoted 2 lines
Reported-by Jason A. Donenfeld <[email protected]>Signed-off-by Ard Biesheuvel <[email protected]>
Hi Ard,
Ran into a problem with this one. See below
Quoted 62 lines
--- arch/arm64/include/asm/acpi.h | 15 +---- arch/arm64/kernel/acpi.c | 66 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-)arch/arm64/include/asm/acpi.h +1
-14@@ -47,20 +47,7 @@pgprot_t __acpi_get_mem_attribute (phys_addr_t addr);/* ACPI table mapping after acpi_permanent_mmap is set */ -static inline void __iomem *acpi_os_ioremap (acpi_physical_address phys, -acpi_size size) -{ -/* For normal memory we already have a cacheable mapping. */ -if (memblock_is_map_memory (phys)) -return (void __iomem *)__phys_to_virt (phys); - -/* -* We should still honor the memory's attribute here because -* crash dump kernel possibly excludes some ACPI (reclaim) -* regions from memblock list. -*/ -return __ioremap (phys, size,__acpi_get_mem_attribute (phys)); -} +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size);#define acpi_os_ioremap acpi_os_ioremaptypedef u64 phys_cpuid_t ;arch/arm64/kernel/acpi.c +23
-0@@ -261,6 +261,72 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)return __pgprot (PROT_DEVICE_nGnRnE); } +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size) +{ +efi_memory_desc_t *md, *region =NULL ; +pgprot_t prot; + +if (WARN_ON_ONCE (!efi_enabled (EFI_MEMMAP ))) +return NULL ; + +for_each_efi_memory_desc (md) { +u64 end = md->phys_addr + (md->num_pages <<EFI_PAGE_SHIFT ); + +if (phys < md->phys_addr || phys >= end) +continue ; + +if (phys + size > end) { +pr_warn (FW_BUG "requested region covers multiple EFI memory regions\n" ); +return NULL ; + } + region = md; +break ; + } + +/*
arch/arm64/kernel/acpi.c pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) · line 296
Quoted 10 lines
arch/arm64/kernel/acpi.c +10
-0+* It is fine for AML to remap regions that are not represented in the +* EFI memory map at all, as it only describes normal memory, and MMIO +* regions that require a virtual mapping to make them accessible to +* the EFI runtime services. +*/ + prot =__pgprot (PROT_DEVICE_nGnRnE); +if (region) { +switch (region->type) { +case EFI_LOADER_CODE : +case EFI_LOADER_DATA :
Unfortunately this seems to have broken overriding of ACPI tables from an initrd. My particular test environment is qemu + EDK2.
It only has obvious visible affect on tables that are used late in the boot such as PPTT as they get dropped before they are used.
These are read after ACPICA is initialized and acpi_reallocate_root_table() has been called. The back trace is:
acpi_os_ioremap+0xfc/0x288 acpi_os_map_iomem+0xc4/0x188 acpi_os_map_memory+0x18/0x28 acpi_tb_acquire_table+0x54/0x8c acpi_tb_validate_table+0x34/0x5c acpi_tb_validate_temp_table+0x34/0x40 acpi_tb_verify_temp_table+0x48/0x250 acpi_reallocate_root_table+0x12c/0x160
Seems that the table is in a region of type EFI_LOADER_DATA.
I don't really know enough about this area to be sure what the right fix is or even whether this is a kernel issue, or one that should be fixed elsewhere in the stack.
For now I'm just carry a hack that treats EFI_LOADER_DATA in the same fashion as EFI_ACPI_RECLAIM_MEMORY below.
What's the right way to fix this?
Jonathan
Quoted 36 lines
arch/arm64/kernel/acpi.c +33
-0+case EFI_BOOT_SERVICES_CODE : +case EFI_BOOT_SERVICES_DATA : +case EFI_CONVENTIONAL_MEMORY : +case EFI_PERSISTENT_MEMORY : +pr_warn (FW_BUG "requested region covers kernel memory @ %pa\n" , &phys); +return NULL ; + +case EFI_ACPI_RECLAIM_MEMORY : +/* +* ACPI reclaim memory is used to pass firmware tables +* and other data that is intended for consumption by +* the OS only, which may decide it wants to reclaim +* that memory and use it for something else. We never +* do that, but we usually add it to the linear map +* anyway, in which case we should use the existing +* mapping. +*/ +if (memblock_is_map_memory (phys)) +return (void __iomem *)__phys_to_virt (phys); +/* fall through */ + +default : +if (region->attribute &EFI_MEMORY_WB ) + prot =PAGE_KERNEL ; +else if (region->attribute &EFI_MEMORY_WT ) + prot =__pgprot (PROT_NORMAL_WT ); +else if (region->attribute &EFI_MEMORY_WC ) + prot =__pgprot (PROT_NORMAL_NC ); + } + } +return __ioremap (phys, size, prot); +} +/* * Claim Synchronous External Aborts as a firmware first notification. *
Re: [PATCH v3 1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
Quoted 122 lines · Jonathan Cameron
Quoted 18 lines · On Fri, 26 Jun 2020 17:58:31 +0200 Ard Biesheuvel <[email protected]> wrote:
AML uses SystemMemory opregions to allow AML handlers to access MMIO registers of, e.g., GPIO controllers, or access reserved regions of memory that are owned by the firmware.
Currently, we also allow AML access to memory that is owned by the kernel and mapped via the linear region, which does not seem to be supported by a valid use case, and exposes the kernel's internal state to AML methods that may be buggy and exploitable.
On arm64, ACPI support requires booting in EFI mode, and so we can cross reference the requested region against the EFI memory map, rather than just do a minimal check on the first page. So let's only permit regions to be remapped by the ACPI core if
- they don't appear in the EFI memory map at all (which is the case for most MMIO), or
- they are covered by a single region in the EFI memory map, which is not of a type that describes memory that is given to the kernel at boot.Quoted 2 lines
Reported-by Jason A. Donenfeld <[email protected]>Signed-off-by Ard Biesheuvel <[email protected]>Hi Ard,
Ran into a problem with this one. See below
Quoted 62 lines
--- arch/arm64/include/asm/acpi.h | 15 +---- arch/arm64/kernel/acpi.c | 66 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-)arch/arm64/include/asm/acpi.h +1
-14@@ -47,20 +47,7 @@pgprot_t __acpi_get_mem_attribute (phys_addr_t addr);/* ACPI table mapping after acpi_permanent_mmap is set */ -static inline void __iomem *acpi_os_ioremap (acpi_physical_address phys, -acpi_size size) -{ -/* For normal memory we already have a cacheable mapping. */ -if (memblock_is_map_memory (phys)) -return (void __iomem *)__phys_to_virt (phys); - -/* -* We should still honor the memory's attribute here because -* crash dump kernel possibly excludes some ACPI (reclaim) -* regions from memblock list. -*/ -return __ioremap (phys, size,__acpi_get_mem_attribute (phys)); -} +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size);#define acpi_os_ioremap acpi_os_ioremaptypedef u64 phys_cpuid_t ;arch/arm64/kernel/acpi.c +23
-0@@ -261,6 +261,72 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)return __pgprot (PROT_DEVICE_nGnRnE); } +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size) +{ +efi_memory_desc_t *md, *region =NULL ; +pgprot_t prot; + +if (WARN_ON_ONCE (!efi_enabled (EFI_MEMMAP ))) +return NULL ; + +for_each_efi_memory_desc (md) { +u64 end = md->phys_addr + (md->num_pages <<EFI_PAGE_SHIFT ); + +if (phys < md->phys_addr || phys >= end) +continue ; + +if (phys + size > end) { +pr_warn (FW_BUG "requested region covers multiple EFI memory regions\n" ); +return NULL ; + } + region = md; +break ; + } + +/* arch/arm64/kernel/acpi.c pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) · line 296
Quoted 10 lines
arch/arm64/kernel/acpi.c +10
-0+* It is fine for AML to remap regions that are not represented in the +* EFI memory map at all, as it only describes normal memory, and MMIO +* regions that require a virtual mapping to make them accessible to +* the EFI runtime services. +*/ + prot =__pgprot (PROT_DEVICE_nGnRnE); +if (region) { +switch (region->type) { +case EFI_LOADER_CODE : +case EFI_LOADER_DATA :Unfortunately this seems to have broken overriding of ACPI tables from an initrd. My particular test environment is qemu + EDK2.
It only has obvious visible affect on tables that are used late in the boot such as PPTT as they get dropped before they are used.
These are read after ACPICA is initialized and
acpi_reallocate_root_table()has been called. The back trace is:acpi_os_ioremap+0xfc/0x288 acpi_os_map_iomem+0xc4/0x188 acpi_os_map_memory+0x18/0x28 acpi_tb_acquire_table+0x54/0x8c acpi_tb_validate_table+0x34/0x5c acpi_tb_validate_temp_table+0x34/0x40 acpi_tb_verify_temp_table+0x48/0x250 acpi_reallocate_root_table+0x12c/0x160Seems that the table is in a region of type
EFI_LOADER_DATA.
Quoted 9 lines
I don't really know enough about this area to be sure what the right fix is or even whether this is a kernel issue, or one that should be fixed elsewhere in the stack.
For now I'm just carry a hack that treats
EFI_LOADER_DATAin the same fashion asEFI_ACPI_RECLAIM_MEMORYbelow.What's the right way to fix this?
Hi Jonathan,
That is an excellent question.
The purpose of this change is to ensure that firmware cannot manipulate the internal state of the kernel. So as long as we can ensure that this memory is not claimed by the kernel's memory subsystem, we should be fine.
Since this is an obvious debug feature, what we could do is reserve this memory permanently in some way, and make the test take this into account.
Do you have a full stack trace? How early does this run?
Re: [PATCH v3 1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
Quoted 138 lines · On Mon, 28 Sep 2020 18:49:35 +0200 Ard Biesheuvel <[email protected]> wrote:
Quoted 122 lines · Jonathan Cameron
Quoted 18 lines · On Fri, 26 Jun 2020 17:58:31 +0200 Ard Biesheuvel <[email protected]> wrote:
AML uses SystemMemory opregions to allow AML handlers to access MMIO registers of, e.g., GPIO controllers, or access reserved regions of memory that are owned by the firmware.
Currently, we also allow AML access to memory that is owned by the kernel and mapped via the linear region, which does not seem to be supported by a valid use case, and exposes the kernel's internal state to AML methods that may be buggy and exploitable.
On arm64, ACPI support requires booting in EFI mode, and so we can cross reference the requested region against the EFI memory map, rather than just do a minimal check on the first page. So let's only permit regions to be remapped by the ACPI core if
- they don't appear in the EFI memory map at all (which is the case for most MMIO), or
- they are covered by a single region in the EFI memory map, which is not of a type that describes memory that is given to the kernel at boot.Quoted 2 lines
Reported-by Jason A. Donenfeld <[email protected]>Signed-off-by Ard Biesheuvel <[email protected]>Hi Ard,
Ran into a problem with this one. See below
Quoted 62 lines
--- arch/arm64/include/asm/acpi.h | 15 +---- arch/arm64/kernel/acpi.c | 66 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-)arch/arm64/include/asm/acpi.h +1
-14@@ -47,20 +47,7 @@pgprot_t __acpi_get_mem_attribute (phys_addr_t addr);/* ACPI table mapping after acpi_permanent_mmap is set */ -static inline void __iomem *acpi_os_ioremap (acpi_physical_address phys, -acpi_size size) -{ -/* For normal memory we already have a cacheable mapping. */ -if (memblock_is_map_memory (phys)) -return (void __iomem *)__phys_to_virt (phys); - -/* -* We should still honor the memory's attribute here because -* crash dump kernel possibly excludes some ACPI (reclaim) -* regions from memblock list. -*/ -return __ioremap (phys, size,__acpi_get_mem_attribute (phys)); -} +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size);#define acpi_os_ioremap acpi_os_ioremaptypedef u64 phys_cpuid_t ;arch/arm64/kernel/acpi.c +23
-0@@ -261,6 +261,72 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)return __pgprot (PROT_DEVICE_nGnRnE); } +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size) +{ +efi_memory_desc_t *md, *region =NULL ; +pgprot_t prot; + +if (WARN_ON_ONCE (!efi_enabled (EFI_MEMMAP ))) +return NULL ; + +for_each_efi_memory_desc (md) { +u64 end = md->phys_addr + (md->num_pages <<EFI_PAGE_SHIFT ); + +if (phys < md->phys_addr || phys >= end) +continue ; + +if (phys + size > end) { +pr_warn (FW_BUG "requested region covers multiple EFI memory regions\n" ); +return NULL ; + } + region = md; +break ; + } + +/* arch/arm64/kernel/acpi.c pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) · line 296
Quoted 10 lines
arch/arm64/kernel/acpi.c +10
-0+* It is fine for AML to remap regions that are not represented in the +* EFI memory map at all, as it only describes normal memory, and MMIO +* regions that require a virtual mapping to make them accessible to +* the EFI runtime services. +*/ + prot =__pgprot (PROT_DEVICE_nGnRnE); +if (region) { +switch (region->type) { +case EFI_LOADER_CODE : +case EFI_LOADER_DATA :Unfortunately this seems to have broken overriding of ACPI tables from an initrd. My particular test environment is qemu + EDK2.
It only has obvious visible affect on tables that are used late in the boot such as PPTT as they get dropped before they are used.
These are read after ACPICA is initialized and
acpi_reallocate_root_table()has been called. The back trace is:acpi_os_ioremap+0xfc/0x288 acpi_os_map_iomem+0xc4/0x188 acpi_os_map_memory+0x18/0x28 acpi_tb_acquire_table+0x54/0x8c acpi_tb_validate_table+0x34/0x5c acpi_tb_validate_temp_table+0x34/0x40 acpi_tb_verify_temp_table+0x48/0x250 acpi_reallocate_root_table+0x12c/0x160Seems that the table is in a region of type
EFI_LOADER_DATA.Quoted 9 lines
I don't really know enough about this area to be sure what the right fix is or even whether this is a kernel issue, or one that should be fixed elsewhere in the stack.
For now I'm just carry a hack that treats
EFI_LOADER_DATAin the same fashion asEFI_ACPI_RECLAIM_MEMORYbelow.What's the right way to fix this?
Hi Jonathan,
That is an excellent question.
Quoted 8 lines
The purpose of this change is to ensure that firmware cannot manipulate the internal state of the kernel. So as long as we can ensure that this memory is not claimed by the kernel's memory subsystem, we should be fine.
Since this is an obvious debug feature, what we could do is reserve this memory permanently in some way, and make the test take this into account.
Whilst it is a debug feature, I wonder if it gets shipped in production hardware. If not, could be we cynical and just drop the check if the relevant config option is enabled?
Perhaps just don't release the EFI_LOADER_DATA for other use? (if this option is enabled only)
Quoted 2 lines
Do you have a full stack trace? How early does this run?
For the place where it first occurs, ie the trace above, the acpi_reallocate_root_table() is the call from acpi_early_init() from start_kernel().
We hit the table a lot during later calls though and hence would run into the same problem.
Jonathan
Re: [PATCH v3 1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
Quoted 159 lines · Jonathan Cameron
Quoted 138 lines · On Mon, 28 Sep 2020 18:49:35 +0200 Ard Biesheuvel <[email protected]> wrote:
Quoted 122 lines · Jonathan Cameron
Quoted 18 lines · On Fri, 26 Jun 2020 17:58:31 +0200 Ard Biesheuvel <[email protected]> wrote:
AML uses SystemMemory opregions to allow AML handlers to access MMIO registers of, e.g., GPIO controllers, or access reserved regions of memory that are owned by the firmware.
Currently, we also allow AML access to memory that is owned by the kernel and mapped via the linear region, which does not seem to be supported by a valid use case, and exposes the kernel's internal state to AML methods that may be buggy and exploitable.
On arm64, ACPI support requires booting in EFI mode, and so we can cross reference the requested region against the EFI memory map, rather than just do a minimal check on the first page. So let's only permit regions to be remapped by the ACPI core if
- they don't appear in the EFI memory map at all (which is the case for most MMIO), or
- they are covered by a single region in the EFI memory map, which is not of a type that describes memory that is given to the kernel at boot.Quoted 2 lines
Reported-by Jason A. Donenfeld <[email protected]>Signed-off-by Ard Biesheuvel <[email protected]>Hi Ard,
Ran into a problem with this one. See below
Quoted 62 lines
--- arch/arm64/include/asm/acpi.h | 15 +---- arch/arm64/kernel/acpi.c | 66 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-)arch/arm64/include/asm/acpi.h +1
-14@@ -47,20 +47,7 @@pgprot_t __acpi_get_mem_attribute (phys_addr_t addr);/* ACPI table mapping after acpi_permanent_mmap is set */ -static inline void __iomem *acpi_os_ioremap (acpi_physical_address phys, -acpi_size size) -{ -/* For normal memory we already have a cacheable mapping. */ -if (memblock_is_map_memory (phys)) -return (void __iomem *)__phys_to_virt (phys); - -/* -* We should still honor the memory's attribute here because -* crash dump kernel possibly excludes some ACPI (reclaim) -* regions from memblock list. -*/ -return __ioremap (phys, size,__acpi_get_mem_attribute (phys)); -} +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size);#define acpi_os_ioremap acpi_os_ioremaptypedef u64 phys_cpuid_t ;arch/arm64/kernel/acpi.c +23
-0@@ -261,6 +261,72 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)return __pgprot (PROT_DEVICE_nGnRnE); } +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size) +{ +efi_memory_desc_t *md, *region =NULL ; +pgprot_t prot; + +if (WARN_ON_ONCE (!efi_enabled (EFI_MEMMAP ))) +return NULL ; + +for_each_efi_memory_desc (md) { +u64 end = md->phys_addr + (md->num_pages <<EFI_PAGE_SHIFT ); + +if (phys < md->phys_addr || phys >= end) +continue ; + +if (phys + size > end) { +pr_warn (FW_BUG "requested region covers multiple EFI memory regions\n" ); +return NULL ; + } + region = md; +break ; + } + +/* arch/arm64/kernel/acpi.c pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) · line 296
Quoted 10 lines
arch/arm64/kernel/acpi.c +10
-0+* It is fine for AML to remap regions that are not represented in the +* EFI memory map at all, as it only describes normal memory, and MMIO +* regions that require a virtual mapping to make them accessible to +* the EFI runtime services. +*/ + prot =__pgprot (PROT_DEVICE_nGnRnE); +if (region) { +switch (region->type) { +case EFI_LOADER_CODE : +case EFI_LOADER_DATA :Unfortunately this seems to have broken overriding of ACPI tables from an initrd. My particular test environment is qemu + EDK2.
It only has obvious visible affect on tables that are used late in the boot such as PPTT as they get dropped before they are used.
These are read after ACPICA is initialized and
acpi_reallocate_root_table()has been called. The back trace is:acpi_os_ioremap+0xfc/0x288 acpi_os_map_iomem+0xc4/0x188 acpi_os_map_memory+0x18/0x28 acpi_tb_acquire_table+0x54/0x8c acpi_tb_validate_table+0x34/0x5c acpi_tb_validate_temp_table+0x34/0x40 acpi_tb_verify_temp_table+0x48/0x250 acpi_reallocate_root_table+0x12c/0x160Seems that the table is in a region of type
EFI_LOADER_DATA.Quoted 9 lines
I don't really know enough about this area to be sure what the right fix is or even whether this is a kernel issue, or one that should be fixed elsewhere in the stack.
For now I'm just carry a hack that treats
EFI_LOADER_DATAin the same fashion asEFI_ACPI_RECLAIM_MEMORYbelow.What's the right way to fix this?
Hi Jonathan,
That is an excellent question.
Quoted 8 lines
The purpose of this change is to ensure that firmware cannot manipulate the internal state of the kernel. So as long as we can ensure that this memory is not claimed by the kernel's memory subsystem, we should be fine.
Since this is an obvious debug feature, what we could do is reserve this memory permanently in some way, and make the test take this into account.
Whilst it is a debug feature, I wonder if it gets shipped in production hardware. If not, could be we cynical and just drop the check if the relevant config option is enabled?
Perhaps just don't release the
EFI_LOADER_DATAfor other use? (if this option is enabled only)Quoted 1 line
Quoted 8 lines
Quoted 1 line
Do you have a full stack trace? How early does this run?
For the place where it first occurs, ie the trace above, the
acpi_reallocate_root_table()is the call fromacpi_early_init()fromstart_kernel().We hit the table a lot during later calls though and hence would run into the same problem.
Could you try the patch below? Since the memory holding the tables is already memblock_reserve()d, we can just mark it NOMAP, and permit r/o remapping of NOMAP regions.
arch/arm64/kernel/acpi.c +0 -0
@@ -298,8 +298,11 @@ void __iomem
*acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
case EFI_BOOT_SERVICES_DATA:
case EFI_CONVENTIONAL_MEMORY:
case EFI_PERSISTENT_MEMORY:
- pr_warn(FW_BUG "requested region covers kernel
memory @ %pa\n", &phys);
- return NULL;
+ if (memblock_is_map_memory(phys)) {
+ pr_warn(FW_BUG "requested region
covers kernel memory @ %pa\n", &phys);
+ return NULL;
+ }
+ fallthrough;
case EFI_RUNTIME_SERVICES_CODE:
/*@@ -388,3 +391,8 @@ int apei_claim_sea(struct pt_regs *regs)
return err;
}
+
+void arch_reserve_mem_area(acpi_physical_address addr, size_t size) +{
+ memblock_mark_nomap(addr, size);
+}
include/linux/acpi.h +0 -0
@@ -958,7 +958,7 @@ void acpi_os_set_prepare_extended_sleep(int
(*func)(u8 sleep_state,
acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state,
u32 val_a, u32 val_b);
-#ifdef CONFIG_X86
+#ifndef CONFIG_IA64
void arch_reserve_mem_area(acpi_physical_address addr, size_t size); #else
static inline void arch_reserve_mem_area(acpi_physical_address addr,
Re: [PATCH v3 1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
Quoted 166 lines · On Tue, 29 Sep 2020 11:29:48 +0200 Ard Biesheuvel <[email protected]> wrote:
Quoted 164 lines · Jonathan Cameron
Quoted 138 lines · On Mon, 28 Sep 2020 18:49:35 +0200 Ard Biesheuvel <[email protected]> wrote:
Quoted 122 lines · Jonathan Cameron
Quoted 18 lines · On Fri, 26 Jun 2020 17:58:31 +0200 Ard Biesheuvel <[email protected]> wrote:
AML uses SystemMemory opregions to allow AML handlers to access MMIO registers of, e.g., GPIO controllers, or access reserved regions of memory that are owned by the firmware.
Currently, we also allow AML access to memory that is owned by the kernel and mapped via the linear region, which does not seem to be supported by a valid use case, and exposes the kernel's internal state to AML methods that may be buggy and exploitable.
On arm64, ACPI support requires booting in EFI mode, and so we can cross reference the requested region against the EFI memory map, rather than just do a minimal check on the first page. So let's only permit regions to be remapped by the ACPI core if
- they don't appear in the EFI memory map at all (which is the case for most MMIO), or
- they are covered by a single region in the EFI memory map, which is not of a type that describes memory that is given to the kernel at boot.Quoted 2 lines
Reported-by Jason A. Donenfeld <[email protected]>Signed-off-by Ard Biesheuvel <[email protected]>Hi Ard,
Ran into a problem with this one. See below
Quoted 62 lines
--- arch/arm64/include/asm/acpi.h | 15 +---- arch/arm64/kernel/acpi.c | 66 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 14 deletions(-)arch/arm64/include/asm/acpi.h +1
-14@@ -47,20 +47,7 @@pgprot_t __acpi_get_mem_attribute (phys_addr_t addr);/* ACPI table mapping after acpi_permanent_mmap is set */ -static inline void __iomem *acpi_os_ioremap (acpi_physical_address phys, -acpi_size size) -{ -/* For normal memory we already have a cacheable mapping. */ -if (memblock_is_map_memory (phys)) -return (void __iomem *)__phys_to_virt (phys); - -/* -* We should still honor the memory's attribute here because -* crash dump kernel possibly excludes some ACPI (reclaim) -* regions from memblock list. -*/ -return __ioremap (phys, size,__acpi_get_mem_attribute (phys)); -} +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size);#define acpi_os_ioremap acpi_os_ioremaptypedef u64 phys_cpuid_t ;arch/arm64/kernel/acpi.c +23
-0@@ -261,6 +261,72 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)return __pgprot (PROT_DEVICE_nGnRnE); } +void __iomem *acpi_os_ioremap (acpi_physical_address phys,acpi_size size) +{ +efi_memory_desc_t *md, *region =NULL ; +pgprot_t prot; + +if (WARN_ON_ONCE (!efi_enabled (EFI_MEMMAP ))) +return NULL ; + +for_each_efi_memory_desc (md) { +u64 end = md->phys_addr + (md->num_pages <<EFI_PAGE_SHIFT ); + +if (phys < md->phys_addr || phys >= end) +continue ; + +if (phys + size > end) { +pr_warn (FW_BUG "requested region covers multiple EFI memory regions\n" ); +return NULL ; + } + region = md; +break ; + } + +/* arch/arm64/kernel/acpi.c pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) · line 296
Quoted 10 lines
arch/arm64/kernel/acpi.c +10
-0+* It is fine for AML to remap regions that are not represented in the +* EFI memory map at all, as it only describes normal memory, and MMIO +* regions that require a virtual mapping to make them accessible to +* the EFI runtime services. +*/ + prot =__pgprot (PROT_DEVICE_nGnRnE); +if (region) { +switch (region->type) { +case EFI_LOADER_CODE : +case EFI_LOADER_DATA :Unfortunately this seems to have broken overriding of ACPI tables from an initrd. My particular test environment is qemu + EDK2.
It only has obvious visible affect on tables that are used late in the boot such as PPTT as they get dropped before they are used.
These are read after ACPICA is initialized and
acpi_reallocate_root_table()has been called. The back trace is:acpi_os_ioremap+0xfc/0x288 acpi_os_map_iomem+0xc4/0x188 acpi_os_map_memory+0x18/0x28 acpi_tb_acquire_table+0x54/0x8c acpi_tb_validate_table+0x34/0x5c acpi_tb_validate_temp_table+0x34/0x40 acpi_tb_verify_temp_table+0x48/0x250 acpi_reallocate_root_table+0x12c/0x160Seems that the table is in a region of type
EFI_LOADER_DATA.Quoted 9 lines
I don't really know enough about this area to be sure what the right fix is or even whether this is a kernel issue, or one that should be fixed elsewhere in the stack.
For now I'm just carry a hack that treats
EFI_LOADER_DATAin the same fashion asEFI_ACPI_RECLAIM_MEMORYbelow.What's the right way to fix this?
Hi Jonathan,
That is an excellent question.
Quoted 8 lines
The purpose of this change is to ensure that firmware cannot manipulate the internal state of the kernel. So as long as we can ensure that this memory is not claimed by the kernel's memory subsystem, we should be fine.
Since this is an obvious debug feature, what we could do is reserve this memory permanently in some way, and make the test take this into account.
Whilst it is a debug feature, I wonder if it gets shipped in production hardware. If not, could be we cynical and just drop the check if the relevant config option is enabled?
Perhaps just don't release the
EFI_LOADER_DATAfor other use? (if this option is enabled only)Quoted 2 lines
Do you have a full stack trace? How early does this run?
For the place where it first occurs, ie the trace above, the
acpi_reallocate_root_table()is the call fromacpi_early_init()fromstart_kernel().
Quoted 7 lines
Quoted 3 lines
We hit the table a lot during later calls though and hence would run into the same problem.
Could you try the patch below? Since the memory holding the tables is already memblock_reserve()d, we can just mark it NOMAP, and permit r/o remapping of NOMAP regions.
Looks good. Thanks.
Quoted 45 lines
arch/arm64/kernel/acpi.c +5
-2@@ -298,8 +298,11 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)case EFI_BOOT_SERVICES_DATA :case EFI_CONVENTIONAL_MEMORY :case EFI_PERSISTENT_MEMORY : -pr_warn (FW_BUG "requested region covers kernel memory @ %pa\n", &phys); -return NULL ; + if (memblock_is_map_memory(phys)) { + pr_warn(FW_BUG "requested region covers kernel memory @ %pa\n", &phys); + return NULL; + } + fallthrough;case EFI_RUNTIME_SERVICES_CODE: /*arch/arm64/kernel/acpi.c +5
-0@@ -388,3 +391,8 @@ int apei_claim_sea(struct pt_regs *regs)return err; } + +void arch_reserve_mem_area (acpi_physical_address addr,size_t size) +{ +memblock_mark_nomap (addr, size); +}include/linux/acpi.h +1
-1@@ -958,7 +958,7 @@ void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, u32 val_a, u32 val_b); -#ifdef CONFIG_X86 +#ifndef CONFIG_IA64 void arch_reserve_mem_area (acpi_physical_address addr,size_t size);#else static inline void arch_reserve_mem_area(acpi_physical_address addr,
Catalin Marinas
maintainerRe: [PATCH v3 1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
Hi Jonathan,
Quoted 9 lines · Jonathan Cameron
Quoted 3 lines · On Tue, 29 Sep 2020 11:29:48 +0200 Ard Biesheuvel <[email protected]> wrote:
Could you try the patch below? Since the memory holding the tables is already memblock_reserve()d, we can just mark it NOMAP, and permit r/o remapping of NOMAP regions.
Looks good. Thanks.
Tested-by Jonathan Cameron <[email protected]>Reviewed-by Jonathan Cameron <[email protected]>
Could you please try the updated patch that Ard posted. There are a few minor differences:
https://lore.kernel.org/linux-acpi/[email protected]/
Thanks.
Signature · 1 line
Catalin
PATCH v3 2/2 arm64/acpi: disallow writeable AML opregion mapping for EFI code regions
In reply to Ard BiesheuvelGiven that the contents of EFI runtime code and data regions are provided by the firmware, as well as the DSDT, it is not unimaginable that AML code exists today that accesses EFI runtime code regions using a SystemMemory OpRegion. There is nothing fundamentally wrong with that, but since we take great care to ensure that executable code is never mapped writeable and executable at the same time, we should not permit AML to create writable mapping.
--- arch/arm64/kernel/acpi.c | 9 +++++++++ 1 file changed, 9 insertions(+)
arch/arm64/kernel/acpi.c +9 -0
@@ -301,6 +301,15 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)pr_warn (FW_BUG "requested region covers kernel memory @ %pa\n" , &phys);return NULL ; +case EFI_RUNTIME_SERVICES_CODE : +/* +* This would be unusual, but not problematic per se, +* as long as we take care not to create a writable +* mapping for executable code. +*/ + prot =PAGE_KERNEL_RO ; +break ; +case EFI_ACPI_RECLAIM_MEMORY :/* * ACPI reclaim memory is used to pass firmware tables
Signature · 1 line
2.27.0
Re: [PATCH v3 2/2] arm64/acpi: disallow writeable AML opregion mapping for EFI code regions
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
Given that the contents of EFI runtime code and data regions are provided by the firmware, as well as the DSDT, it is not unimaginable that AML code exists today that accesses EFI runtime code regions using a SystemMemory OpRegion. There is nothing fundamentally wrong with that, but since we take great care to ensure that executable code is never mapped writeable and executable at the same time, we should not permit AML to create writable mapping.
Signed-off-by Ard Biesheuvel <[email protected]>
I'm booting Lenovo Flex 5G laptop with ACPI, and seeing this change causes a memory abort[1] when upgrading ACPI tables via initrd[2]. Dropping this change seems to fix the issue for me. But does that looks like a correct fix to you?
Shawn
[1] https://fileserver.linaro.org/s/iDe9SaZeNNkyNxG
[2] Documentation/admin-guide/acpi/initrd_table_override.rst
Quoted 26 lines
--- arch/arm64/kernel/acpi.c | 9 +++++++++ 1 file changed, 9 insertions(+)arch/arm64/kernel/acpi.c +9
-0@@ -301,6 +301,15 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)pr_warn (FW_BUG "requested region covers kernel memory @ %pa\n" , &phys);return NULL ; +case EFI_RUNTIME_SERVICES_CODE : +/* +* This would be unusual, but not problematic per se, +* as long as we take care not to create a writable +* mapping for executable code. +*/ + prot =PAGE_KERNEL_RO ; +break ; +case EFI_ACPI_RECLAIM_MEMORY :/* * ACPI reclaim memory is used to pass firmware tables Signature · 1 line
2.27.0
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v3 2/2] arm64/acpi: disallow writeable AML opregion mapping for EFI code regions
Quoted 19 lines · Shawn Guo
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
Given that the contents of EFI runtime code and data regions are provided by the firmware, as well as the DSDT, it is not unimaginable that AML code exists today that accesses EFI runtime code regions using a SystemMemory OpRegion. There is nothing fundamentally wrong with that, but since we take great care to ensure that executable code is never mapped writeable and executable at the same time, we should not permit AML to create writable mapping.
Signed-off-by Ard Biesheuvel <[email protected]>I'm booting Lenovo Flex 5G laptop with ACPI, and seeing this change causes a memory abort[1] when upgrading ACPI tables via initrd[2]. Dropping this change seems to fix the issue for me. But does that looks like a correct fix to you?
Quoted 5 lines
Shawn
[1] https://fileserver.linaro.org/s/iDe9SaZeNNkyNxG
[2]Documentation/admin-guide/acpi/initrd_table_override.rst
Can you check whether reverting
32cf1a12cad43358e47dac8014379c2f33dfbed4
fixes the issue too?
If it does, please report this as a regression. The OS should not modify firmware provided tables in-place, regardless of how they were delivered.
BTW I recently started using my Yoga C630 with Debian, and I am quite happy with it! Thanks a lot for spending the time on the installer etc.
I have observed some issues while using mine - I'm happy to share them, on a mailing list or anywhere else.
Quoted 26 lines
Quoted 26 lines
--- arch/arm64/kernel/acpi.c | 9 +++++++++ 1 file changed, 9 insertions(+)arch/arm64/kernel/acpi.c +9
-1@@ -301,6 +301,15 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)pr_warn (FW_BUG "requested region covers kernel memory @ %pa\n" , &phys);return NULL ; +case EFI_RUNTIME_SERVICES_CODE : +/* +* This would be unusual, but not problematic per se, +* as long as we take care not to create a writable +* mapping for executable code. +*/ + prot =PAGE_KERNEL_RO ; +break ; +case EFI_ACPI_RECLAIM_MEMORY :/* * ACPI reclaim memory is used to pass firmware tables -- 2.27.0
Re: [PATCH v3 2/2] arm64/acpi: disallow writeable AML opregion mapping for EFI code regions
Quoted 29 lines · Ard Biesheuvel
Quoted 19 lines · Shawn Guo
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
Given that the contents of EFI runtime code and data regions are provided by the firmware, as well as the DSDT, it is not unimaginable that AML code exists today that accesses EFI runtime code regions using a SystemMemory OpRegion. There is nothing fundamentally wrong with that, but since we take great care to ensure that executable code is never mapped writeable and executable at the same time, we should not permit AML to create writable mapping.
Signed-off-by Ard Biesheuvel <[email protected]>I'm booting Lenovo Flex 5G laptop with ACPI, and seeing this change causes a memory abort[1] when upgrading ACPI tables via initrd[2]. Dropping this change seems to fix the issue for me. But does that looks like a correct fix to you?
Quoted 5 lines
Shawn
[1] https://fileserver.linaro.org/s/iDe9SaZeNNkyNxG
[2]Documentation/admin-guide/acpi/initrd_table_override.rstCan you check whether reverting
Quoted 8 lines
32cf1a12cad43358e47dac8014379c2f33dfbed4fixes the issue too?
If it does, please report this as a regression. The OS should not modify firmware provided tables in-place, regardless of how they were delivered.
That patch modifies firmware provided tables in-place, which invalidates checksums and TPM measurements, so it needs to be reverted in any case, and I have already sent out the patch for doing so.
Re: [PATCH v3 2/2] arm64/acpi: disallow writeable AML opregion mapping for EFI code regions
In reply to Ard BiesheuvelQuoted 26 lines · Ard Biesheuvel
Quoted 19 lines · Shawn Guo
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
Given that the contents of EFI runtime code and data regions are provided by the firmware, as well as the DSDT, it is not unimaginable that AML code exists today that accesses EFI runtime code regions using a SystemMemory OpRegion. There is nothing fundamentally wrong with that, but since we take great care to ensure that executable code is never mapped writeable and executable at the same time, we should not permit AML to create writable mapping.
Signed-off-by Ard Biesheuvel <[email protected]>I'm booting Lenovo Flex 5G laptop with ACPI, and seeing this change causes a memory abort[1] when upgrading ACPI tables via initrd[2]. Dropping this change seems to fix the issue for me. But does that looks like a correct fix to you?
Quoted 5 lines
Shawn
[1] https://fileserver.linaro.org/s/iDe9SaZeNNkyNxG
[2]Documentation/admin-guide/acpi/initrd_table_override.rst
Quoted 5 lines
Can you check whether reverting
32cf1a12cad43358e47dac8014379c2f33dfbed4fixes the issue too?
Yes, it does.
Quoted 7 lines
If it does, please report this as a regression. The OS should not modify firmware provided tables in-place, regardless of how they were delivered.
BTW I recently started using my Yoga C630 with Debian, and I am quite happy with it! Thanks a lot for spending the time on the installer etc.
Cool, glad to hear that!
Shawn
(+ James)
Quoted 9 lines · Ard Biesheuvel
v2:
- do a more elaborate check on the region, against the EFI memory mapv3:
- split into two patches
- fallback to__ioremap()for ACPI reclaim memory, in case it is not covered by the linear mapping (e.g., when booting a kdump kernel)
Quoted 5 lines
Ard Biesheuvel (2):
arm64/acpi: disallow AML memory opregions to access kernel memory arm64/acpi: disallow writeable AML opregion mapping for EFI code
regions
With some adult supervision from James (thanks!), I have given this a spin myself with kexec under QEMU/kvm, to boot a crashkernel, and everything works as expected.
Quoted 7 lines
arch/arm64/include/asm/acpi.h | 15 +--- arch/arm64/kernel/acpi.c | 75 ++++++++++++++++++++ 2 files changed, 76 insertions(+), 14 deletions(-)--
2.27.0
Quoted 8 lines · Ard Biesheuvel
v2:
- do a more elaborate check on the region, against the EFI memory mapv3:
- split into two patches
- fallback to__ioremap()for ACPI reclaim memory, in case it is not covered by the linear mapping (e.g., when booting a kdump kernel)
Quoted 8 lines
Ard Biesheuvel (2):
arm64/acpi: disallow AML memory opregions to access kernel memory arm64/acpi: disallow writeable AML opregion mapping for EFI code
regionsarch/arm64/include/asm/acpi.h | 15 +--- arch/arm64/kernel/acpi.c | 75 ++++++++++++++++++++ 2 files changed, 76 insertions(+), 14 deletions(-)
Thanks Ard for fixing this, for the series:
Quoted 9 lines · Ard Biesheuvel
v2:
- do a more elaborate check on the region, against the EFI memory mapv3:
- split into two patches
- fallback to__ioremap()for ACPI reclaim memory, in case it is not covered by the linear mapping (e.g., when booting a kdump kernel)[...]
Applied to arm64 (for-next/acpi), thanks!
[1/2] arm64/acpi: disallow AML memory opregions to access kernel memory
https://git.kernel.org/arm64/c/1583052d111f [2/2] arm64/acpi: disallow writeable AML opregion mapping for EFI code regions https://git.kernel.org/arm64/c/325f5585ec36
Signature · 1 line
Catalin