Skip to content

Commit 6739cc1

Browse files
keesgregkh
authored andcommitted
mm: Tighten x86 /dev/mem with zeroing reads
commit a4866aa812518ed1a37d8ea0c881dc946409de94 upstream. Under CONFIG_STRICT_DEVMEM, reading System RAM through /dev/mem is disallowed. However, on x86, the first 1MB was always allowed for BIOS and similar things, regardless of it actually being System RAM. It was possible for heap to end up getting allocated in low 1MB RAM, and then read by things like x86info or dd, which would trip hardened usercopy: usercopy: kernel memory exposure attempt detected from ffff880000090000 (dma-kmalloc-256) (4096 bytes) This changes the x86 exception for the low 1MB by reading back zeros for System RAM areas instead of blindly allowing them. More work is needed to extend this to mmap, but currently mmap doesn't go through usercopy, so hardened usercopy won't Oops the kernel. Reported-by: Tommi Rantala <tommi.t.rantala@nokia.com> Tested-by: Tommi Rantala <tommi.t.rantala@nokia.com> Signed-off-by: Kees Cook <keescook@chromium.org> Cc: Brad Spengler <spender@grsecurity.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent ba02781 commit 6739cc1

2 files changed

Lines changed: 82 additions & 41 deletions

File tree

arch/x86/mm/init.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -628,21 +628,40 @@ void __init init_mem_mapping(void)
628628
* devmem_is_allowed() checks to see if /dev/mem access to a certain address
629629
* is valid. The argument is a physical page number.
630630
*
631-
*
632-
* On x86, access has to be given to the first megabyte of ram because that area
633-
* contains BIOS code and data regions used by X and dosemu and similar apps.
634-
* Access has to be given to non-kernel-ram areas as well, these contain the PCI
635-
* mmio resources as well as potential bios/acpi data regions.
631+
* On x86, access has to be given to the first megabyte of RAM because that
632+
* area traditionally contains BIOS code and data regions used by X, dosemu,
633+
* and similar apps. Since they map the entire memory range, the whole range
634+
* must be allowed (for mapping), but any areas that would otherwise be
635+
* disallowed are flagged as being "zero filled" instead of rejected.
636+
* Access has to be given to non-kernel-ram areas as well, these contain the
637+
* PCI mmio resources as well as potential bios/acpi data regions.
636638
*/
637639
int devmem_is_allowed(unsigned long pagenr)
638640
{
639-
if (pagenr < 256)
640-
return 1;
641-
if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
641+
if (page_is_ram(pagenr)) {
642+
/*
643+
* For disallowed memory regions in the low 1MB range,
644+
* request that the page be shown as all zeros.
645+
*/
646+
if (pagenr < 256)
647+
return 2;
648+
649+
return 0;
650+
}
651+
652+
/*
653+
* This must follow RAM test, since System RAM is considered a
654+
* restricted resource under CONFIG_STRICT_IOMEM.
655+
*/
656+
if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) {
657+
/* Low 1MB bypasses iomem restrictions. */
658+
if (pagenr < 256)
659+
return 1;
660+
642661
return 0;
643-
if (!page_is_ram(pagenr))
644-
return 1;
645-
return 0;
662+
}
663+
664+
return 1;
646665
}
647666

648667
void free_init_pages(char *what, unsigned long begin, unsigned long end)

drivers/char/mem.c

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
5959
#endif
6060

6161
#ifdef CONFIG_STRICT_DEVMEM
62+
static inline int page_is_allowed(unsigned long pfn)
63+
{
64+
return devmem_is_allowed(pfn);
65+
}
6266
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
6367
{
6468
u64 from = ((u64)pfn) << PAGE_SHIFT;
@@ -78,6 +82,10 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
7882
return 1;
7983
}
8084
#else
85+
static inline int page_is_allowed(unsigned long pfn)
86+
{
87+
return 1;
88+
}
8189
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
8290
{
8391
return 1;
@@ -125,23 +133,31 @@ static ssize_t read_mem(struct file *file, char __user *buf,
125133

126134
while (count > 0) {
127135
unsigned long remaining;
136+
int allowed;
128137

129138
sz = size_inside_page(p, count);
130139

131-
if (!range_is_allowed(p >> PAGE_SHIFT, count))
140+
allowed = page_is_allowed(p >> PAGE_SHIFT);
141+
if (!allowed)
132142
return -EPERM;
143+
if (allowed == 2) {
144+
/* Show zeros for restricted memory. */
145+
remaining = clear_user(buf, sz);
146+
} else {
147+
/*
148+
* On ia64 if a page has been mapped somewhere as
149+
* uncached, then it must also be accessed uncached
150+
* by the kernel or data corruption may occur.
151+
*/
152+
ptr = xlate_dev_mem_ptr(p);
153+
if (!ptr)
154+
return -EFAULT;
133155

134-
/*
135-
* On ia64 if a page has been mapped somewhere as uncached, then
136-
* it must also be accessed uncached by the kernel or data
137-
* corruption may occur.
138-
*/
139-
ptr = xlate_dev_mem_ptr(p);
140-
if (!ptr)
141-
return -EFAULT;
156+
remaining = copy_to_user(buf, ptr, sz);
157+
158+
unxlate_dev_mem_ptr(p, ptr);
159+
}
142160

143-
remaining = copy_to_user(buf, ptr, sz);
144-
unxlate_dev_mem_ptr(p, ptr);
145161
if (remaining)
146162
return -EFAULT;
147163

@@ -184,30 +200,36 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
184200
#endif
185201

186202
while (count > 0) {
203+
int allowed;
204+
187205
sz = size_inside_page(p, count);
188206

189-
if (!range_is_allowed(p >> PAGE_SHIFT, sz))
207+
allowed = page_is_allowed(p >> PAGE_SHIFT);
208+
if (!allowed)
190209
return -EPERM;
191210

192-
/*
193-
* On ia64 if a page has been mapped somewhere as uncached, then
194-
* it must also be accessed uncached by the kernel or data
195-
* corruption may occur.
196-
*/
197-
ptr = xlate_dev_mem_ptr(p);
198-
if (!ptr) {
199-
if (written)
200-
break;
201-
return -EFAULT;
202-
}
211+
/* Skip actual writing when a page is marked as restricted. */
212+
if (allowed == 1) {
213+
/*
214+
* On ia64 if a page has been mapped somewhere as
215+
* uncached, then it must also be accessed uncached
216+
* by the kernel or data corruption may occur.
217+
*/
218+
ptr = xlate_dev_mem_ptr(p);
219+
if (!ptr) {
220+
if (written)
221+
break;
222+
return -EFAULT;
223+
}
203224

204-
copied = copy_from_user(ptr, buf, sz);
205-
unxlate_dev_mem_ptr(p, ptr);
206-
if (copied) {
207-
written += sz - copied;
208-
if (written)
209-
break;
210-
return -EFAULT;
225+
copied = copy_from_user(ptr, buf, sz);
226+
unxlate_dev_mem_ptr(p, ptr);
227+
if (copied) {
228+
written += sz - copied;
229+
if (written)
230+
break;
231+
return -EFAULT;
232+
}
211233
}
212234

213235
buf += sz;

0 commit comments

Comments
 (0)