Skip to contentSkip to conversation

arm64: kernel: restrict /dev/mem read() calls to linear region

PATCH New Ard Biesheuvel 5 participants 8 messages Apr 12, 2017 – Apr 25, 2017 arm64

lore
Share & tools
lore
#0 lore raw

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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")
---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +3 -6
index 7b0d55756eb1..2956240d17d7 100644
@@ -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
#1 lore raw

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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")
---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +1 -0
index 7b0d55756eb1..2956240d17d7 100644
@@ -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 -6
 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);

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

#2 lore raw

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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as

MEMBLOCK_NOMAP")

---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +1 -0
index 7b0d55756eb1..2956240d17d7 100644
@@ -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 -6
 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);

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.

#3 lore raw

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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as

MEMBLOCK_NOMAP")

---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +1 -0
index 7b0d55756eb1..2956240d17d7 100644
@@ -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 -6
 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);

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/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.

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

#4 lore raw

PATCH 1/1 arm64: kernel: restrict /dev/mem read() calls to linear region

In reply to Ard Biesheuvel
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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")
---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +1 -1
index 7b0d55756eb1..2956240d17d7 100644
@@ -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
#5 lore raw

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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")
---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +3 -6
index 7b0d55756eb1..2956240d17d7 100644
@@ -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.c need 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.
#6 lore raw

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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")
---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +3 -6
index 7b0d55756eb1..2956240d17d7 100644
@@ -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.c need the same treatment?

high_memory is 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.
#7 lore raw

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/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.

Fixes 4dffbfc48d65 ("arm64/efi: mark UEFI reserved regions as MEMBLOCK_NOMAP")
---
 arch/arm64/mm/mmap.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/arm64/mm/mmap.c +3 -6
index 7b0d55756eb1..2956240d17d7 100644
@@ -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.c need the same treatment?

high_memory is 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.

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/mem may 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