| From foo@baz Mon Apr 9 17:09:24 CEST 2018 |
| From: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| Date: Fri, 19 May 2017 16:42:00 +0100 |
| Subject: arm64: kernel: restrict /dev/mem read() calls to linear region |
| |
| From: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| |
| |
| [ Upstream commit 1151f838cb626005f4d69bf675dacaaa5ea909d6 ] |
| |
| 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. |
| |
| Reported-by: Alexander Graf <agraf@suse.de> |
| Fixes: 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP") |
| Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| Signed-off-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/arm64/mm/mmap.c | 19 +++++++++++++------ |
| 1 file changed, 13 insertions(+), 6 deletions(-) |
| |
| --- a/arch/arm64/mm/mmap.c |
| +++ b/arch/arm64/mm/mmap.c |
| @@ -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> |
| @@ -102,12 +103,18 @@ void arch_pick_mmap_layout(struct mm_str |
| */ |
| 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; |
| + /* |
| + * Check whether addr is covered by a memory region without the |
| + * MEMBLOCK_NOMAP attribute, and whether that region covers the |
| + * entire range. In theory, this could lead to false negatives |
| + * if the range is covered by distinct but adjacent memory regions |
| + * that only differ in other attributes. However, few of such |
| + * attributes have been defined, and it is debatable whether it |
| + * follows that /dev/mem read() calls should be able traverse |
| + * such boundaries. |
| + */ |
| + return memblock_is_region_memory(addr, size) && |
| + memblock_is_map_memory(addr); |
| } |
| |
| /* |