arm64: kernel: restrict /dev/mem read() calls to linear region
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140
This is caused by the fact that lspci issues a read() on /dev/mem at the offset where it expects to find the SMBIOS structure array. However, this region is classified as EFI_RUNTIME_SERVICE_DATA (as per the UEFI spec), and so it is omitted from the linear mapping.
So let's restrict /dev/mem read/write access to those areas that are covered by the linear region.
--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +3 -6
@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h>
@@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 ); }/*
Signature · 1 line
2.9.3
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
Quoted 43 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +1
-0@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/
arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 108
Quoted 10 lines
arch/arm64/mm/mmap.c +2
-6int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 );
Is that safe? Are we guaranteed that size is less than one page? Otherwise, someone could map a region that spans over a reserved one:
[conv mem]
[reserved]
[conv mem]
Alex
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
Quoted 64 lines · Alexander Graf
Quoted 46 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as
MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +1
-0@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 108
Quoted 10 lines
arch/arm64/mm/mmap.c +2
-6int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 );Is that safe? Are we guaranteed that size is less than one page? Otherwise, someone could map a region that spans over a reserved one:
Quoted 4 lines
[conv mem]
[reserved]
[conv mem]
Well, I will leave it to the maintainers to decide how elaborate they want this logic to become, given that read()ing from /dev/mem is something we are not eager to support in the first place.
But indeed, if the start and end of the region are covered by the linear region, there could potentially be an uncovered hole in the middle.
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
Quoted 70 lines · Ard Biesheuvel
Quoted 64 lines · Alexander Graf
Quoted 46 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as
MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +1
-0@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 108
Quoted 10 lines
arch/arm64/mm/mmap.c +2
-6int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 );Is that safe? Are we guaranteed that size is less than one page? Otherwise, someone could map a region that spans over a reserved one:
Quoted 4 lines
[conv mem]
[reserved]
[conv mem]
Quoted 7 lines
Well, I will leave it to the maintainers to decide how elaborate they want this logic to become, given that read()ing from
/dev/memis something we are not eager to support in the first place.But indeed, if the start and end of the region are covered by the linear region, there could potentially be an uncovered hole in the middle.
I think it would be worth handling that case, even if it means we have to walk over the memblocks which the region overlaps.
Will
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
In reply to Ard BiesheuvelQuoted 46 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +1
-1@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET )
arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 111
Quoted 10 lines
arch/arm64/mm/mmap.c +2
-5-return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 ); }/*
Does arch/arm/mm/mmap.c need the same treatment?
Regards,
Domenico
Signature · 1 line
3B10 0CA1 8674 ACBA B4FE FCD2 CE5B CF17 9960 DE13
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
Quoted 56 lines · Domenico Andreoli
Quoted 55 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +3
-6@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 ); }
Quoted 3 lines
arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 111
Quoted 1 line
arch/arm64/mm/mmap.c +0
-0/* Does
arch/arm/mm/mmap.cneed the same treatment?
high_memory is supposed to be the virtual address of the end of the linearly mapped region. ARM conforms to that.
Signature · 3 lines
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net.
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
Quoted 57 lines · Russell King - ARM Linux
Quoted 56 lines · Domenico Andreoli
Quoted 55 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +3
-6@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 ); }
Quoted 7 lines
Quoted 3 lines
arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 111
Quoted 1 line
arch/arm64/mm/mmap.c +0
-0/* Does
arch/arm/mm/mmap.cneed the same treatment?
high_memoryis supposed to be the virtual address of the end of the linearly mapped region. ARM conforms to that.
Yes, but with UEFI boot, there will be holes in the linear region. This is due to the fact that those regions may have special significance to the firmware, and may be mapped with mismatched attributes.
As it happens, the UEFI spec recommends that SMBIOS tables (which only have significance to the OS) are covered by a EFI_RUNTIME_SERVICE_DATA region, which is one of the types we mark MEMBLOCK_NOMAP, and omit from the linear region.
So when lspcu goes looking for the SMBIOS tables, using the read() interface to /dev/mem, it will dereference a physical pointer into an unmapped region. This will undoubtedly produce a similar result to the above if we don't account for NOMAP regions.
However, we have no DMI/SMBIOS support yet for ARM (nor ACPI support), so this particular issue does not exist on ARM atm. But read() calls to /dev/mem may indeed provoke a similar splat when (ab)used in a similar manner.
Signature · 1 line
Ard.
PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region
Quoted 82 lines · Ard Biesheuvel
Quoted 57 lines · Russell King - ARM Linux
Quoted 56 lines · Domenico Andreoli
Quoted 55 lines · Ard Biesheuvel
When running lscpu on an AArch64 system that has SMBIOS version 2.0 tables, it will segfault in the following way:
Unable to handle kernel paging request at virtual address ffff8000bfff0000 pgd = ffff8000f9615000 [ffff8000bfff0000] *pgd=0000000000000000 Internal error: Oops: 96000007 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1284 Comm: lscpu Not tainted 4.11.0-rc3+ #103 Hardware name: QEMU QEMU Virtual Machine, BIOS 0.0.0 02/06/2015 task: ffff8000fa78e800 task.stack: ffff8000f9780000 PC is at __arch_copy_to_user+0x90/0x220 LR is at read_mem+0xcc/0x140This is caused by the fact that lspci issues a
read()on/dev/memat the offset where it expects to find the SMBIOS structure array. However, this region is classified asEFI_RUNTIME_SERVICE_DATA(as per the UEFI spec), and so it is omitted from the linear mapping.So let's restrict
/dev/memread/write access to those areas that are covered by the linear region.Reported-by Alexander Graf <[email protected]>Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/mm/mmap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)arch/arm64/mm/mmap.c +3
-6@@ -18,6 +18,7 @@#include <linux/elf.h> #include <linux/fs.h> +#include <linux/memblock.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/export.h> @@ -103,12 +104,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm)*/ int valid_phys_addr_range (phys_addr_t addr,size_t size) { -if (addr <PHYS_OFFSET ) -return 0 ; -if (addr + size >__pa (high_memory -1 ) +1 ) -return 0 ; - -return 1 ; +return memblock_is_map_memory (addr) && +memblock_is_map_memory (addr + size -1 ); }Quoted 7 lines
Quoted 3 lines
arch/arm64/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 111
Quoted 1 line
arch/arm64/mm/mmap.c +0
-0/* Does
arch/arm/mm/mmap.cneed the same treatment?
high_memoryis supposed to be the virtual address of the end of the linearly mapped region. ARM conforms to that.Yes, but with UEFI boot, there will be holes in the linear region. This is due to the fact that those regions may have special significance to the firmware, and may be mapped with mismatched attributes.
As it happens, the UEFI spec recommends that SMBIOS tables (which only have significance to the OS) are covered by a
EFI_RUNTIME_SERVICE_DATAregion, which is one of the types we markMEMBLOCK_NOMAP, and omit from the linear region.So when lspcu goes looking for the SMBIOS tables, using the
read()interface to/dev/mem, it will dereference a physical pointer into an unmapped region. This will undoubtedly produce a similar result to the above if we don't account for NOMAP regions.
Quoted 4 lines
However, we have no DMI/SMBIOS support yet for ARM (nor ACPI support), so this particular issue does not exist on ARM atm. But
read()calls to/dev/memmay indeed provoke a similar splat when (ab)used in a similar manner.
I was exactly thinking at this last abuse indeed. Maybe other archs are affected.
thanks,
Domenico
Signature · 1 line
3B10 0CA1 8674 ACBA B4FE FCD2 CE5B CF17 9960 DE13