mm: Tighten x86 /dev/mem with zeroing
Subsystems (4)
RFC PATCH 1/1 mm: Tighten x86 /dev/mem with zeroing
This changes the x86 exception for the low 1MB by reading back zeros for RAM areas instead of blindly allowing them. (It may be possible for heap to end up getting allocated in low 1MB RAM, and then read out, possibly tripping hardened usercopy.)
Unfinished: this still needs mmap support.
---
Tommi, can you check and see if this fixes what you're seeing? I want to make sure this actually works first. (x86info uses seek/read not mmap.)
---
arch/x86/mm/init.c | 41 +++++++++++++++++++-------- drivers/char/mem.c | 82 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 41 deletions(-)
arch/x86/mm/init.c +30 -11
@@ -643,21 +643,40 @@ void __init init_mem_mapping(void)* devmem_is_allowed() checks to see if /dev/mem access to a certain address * is valid. The argument is a physical page number. * -* -* On x86, access has to be given to the first megabyte of ram because that area -* contains BIOS code and data regions used by X and dosemu and similar apps. -* Access has to be given to non-kernel-ram areas as well, these contain the PCI -* mmio resources as well as potential bios/acpi data regions. +* On x86, access has to be given to the first megabyte of RAM because that +* area traditionally contains BIOS code and data regions used by X, dosemu, +* and similar apps. Since they map the entire memory range, the whole range +* must be allowed (for mapping), but any areas that would otherwise be +* disallowed are flagged as being "zero filled" instead of rejected. +* Access has to be given to non-kernel-ram areas as well, these contain the +* PCI mmio resources as well as potential bios/acpi data regions. */ int devmem_is_allowed (unsigned long pagenr) { -if (pagenr <256 ) -return 1 ; -if (iomem_is_exclusive (pagenr <<PAGE_SHIFT )) +if (page_is_ram (pagenr)) { +/* +* For disallowed memory regions in the low 1MB range, +* request that the page be shown as all zeros. +*/ +if (pagenr <256 ) +return 2 ; + +return 0 ; + } + +/* +* This must follow RAM test, since System RAM is considered a +* restricted resource under CONFIG_STRICT_IOMEM. +*/ +if (iomem_is_exclusive (pagenr <<PAGE_SHIFT )) { +/* Low 1MB bypasses iomem restrictions. */ +if (pagenr <256 ) +return 1 ; +return 0 ; -if (!page_is_ram (pagenr)) -return 1 ; -return 0 ; + } + +return 1 ; }void free_init_pages (char *what,unsigned long begin,unsigned long end)
drivers/char/mem.c +52 -30
117 lines not on this page · raw message
Signature · 12 lines
2.7.4 -- Kees Cook Pixel Security -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 5 lines · Kees Cook
This changes the x86 exception for the low 1MB by reading back zeros for RAM areas instead of blindly allowing them. (It may be possible for heap to end up getting allocated in low 1MB RAM, and then read out, possibly tripping hardened usercopy.)
Quoted 7 lines
Unfinished: this still needs mmap support.
Reported-by Tommi Rantala <[email protected]>Signed-off-by Kees Cook <[email protected]>---
Tommi, can you check and see if this fixes what you're seeing? I want to make sure this actually works first. (x86info uses seek/read not mmap.)
Hi, I can confirm that it works (after adding CONFIG_STRICT_DEVMEM), no more kernel bugs when running x86info.
open("/dev/mem", O_RDONLY) = 3
lseek(3, 1038, SEEK_SET) = 1038
read(3, "\300\235", 2) = 2
lseek(3, 646144, SEEK_SET) = 646144
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024
lseek(3, 1043, SEEK_SET) = 1043
read(3, "w\2", 2) = 2
lseek(3, 645120, SEEK_SET) = 645120read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024lseek(3, 654336, SEEK_SET) = 654336read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024lseek(3, 983040, SEEK_SET) = 983040read(3, "IFE$\245S\0\0\1\0\0\0\0\360y\0\0\360\220\260\30\237{=\23\10\17\0000\276\17\0"..., 65536) = 65536lseek(3, 917504, SEEK_SET) = 917504
read(3, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"..., 65536) = 65536lseek(3, 524288, SEEK_SET) = 524288read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536) = 65536lseek(3, 589824, SEEK_SET) = 589824read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536) = 65536
dd works too:
# LANG=C dd if=/dev/mem of=/dev/null bs=4096 count=256 256+0 records in
256+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.0874073 s, 12.0 MB/s
Quoted 5 lines
---
arch/x86/mm/init.c | 41 +++++++++++++++++++-------- drivers/char/mem.c | 82 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 41 deletions(-)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 16 lines · Tommi Rantala
Quoted 6 lines · Kees Cook
This changes the x86 exception for the low 1MB by reading back zeros for RAM areas instead of blindly allowing them. (It may be possible for heap to end up getting allocated in low 1MB RAM, and then read out, possibly tripping hardened usercopy.)
Quoted 7 lines
Unfinished: this still needs mmap support.
Reported-by Tommi Rantala <[email protected]>Signed-off-by Kees Cook <[email protected]>---
Tommi, can you check and see if this fixes what you're seeing? I want to make sure this actually works first. (x86info uses seek/read not mmap.)
Quoted 2 lines
Hi, I can confirm that it works (after adding
CONFIG_STRICT_DEVMEM), no more kernel bugs when running x86info.
Great, thanks for testing!
Linus, given that this fixes the problem, are you okay with this patch as at least the first step? It doesn't solve the mmap exposure case, but I'm struggling to figure out how to construct zero-page holes in the mmap vma, and strictly speaking hardened usercopy doesn't trip over the mmap since it's not using copy_to_user...
-Kees
Quoted 52 lines
open("/dev/mem", O_RDONLY) = 3 lseek(3, 1038, SEEK_SET) = 1038 read(3, "\300\235", 2) = 2 lseek(3, 646144, SEEK_SET) = 646144 read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024 lseek(3, 1043, SEEK_SET) = 1043 read(3, "w\2", 2) = 2 lseek(3, 645120, SEEK_SET) = 645120read(3,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024)
= 1024lseek(3, 654336, SEEK_SET)= 654336
read(3,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024)
= 1024lseek(3, 983040, SEEK_SET)= 983040
read(3,
"IFE$\245S\0\0\1\0\0\0\0\360y\0\0\360\220\260\30\237{=\23\10\17\0000\276\17\0"...,
65536) = 65536lseek(3, 917504, SEEK_SET)= 917504
read(3, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"..., 65536) = 65536lseek(3, 524288, SEEK_SET)= 524288
read(3,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
65536) = 65536lseek(3, 589824, SEEK_SET)= 589824
read(3,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
65536) = 65536dd works too:
# LANG=C dd if=/dev/mem of=/dev/null bs=4096 count=256 256+0 records in
256+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.0874073 s, 12.0 MB/sQuoted 6 lines
---
arch/x86/mm/init.c | 41 +++++++++++++++++++-------- drivers/char/mem.c | 82 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 41 deletions(-)
Signature · 8 lines
Kees Cook Pixel Security -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>