/dev/mem: Add bounce buffer for copy-out
As done for /proc/kcore in commitdf04abfd18
("fs/proc/kcore.c: Add bounce buffer for ktext data") this adds a bounce buffer when reading memory via /dev/mem. This is needed to allow kernel text memory to be read out when built with CONFIG_HARDENED_USERCOPY (which refuses to read out kernel text) and without CONFIG_STRICT_DEVMEM (which would have refused to read any RAM contents at all). Since this build configuration isn't common (most systems with CONFIG_HARDENED_USERCOPY also have CONFIG_STRICT_DEVMEM), this also tries to inform Kconfig about the recommended settings. This patch is modified from Brad Spengler/PaX Team's changes to /dev/mem code in the last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. Reported-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Fixes:f5509cc18d
("mm: Hardened usercopy") Signed-off-by: Kees Cook <keescook@chromium.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
d9cc5a0edb
commit
22ec1a2aea
|
@ -107,6 +107,8 @@ static ssize_t read_mem(struct file *file, char __user *buf,
|
||||||
phys_addr_t p = *ppos;
|
phys_addr_t p = *ppos;
|
||||||
ssize_t read, sz;
|
ssize_t read, sz;
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
char *bounce;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (p != *ppos)
|
if (p != *ppos)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -129,15 +131,22 @@ static ssize_t read_mem(struct file *file, char __user *buf,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bounce = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||||
|
if (!bounce)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
unsigned long remaining;
|
unsigned long remaining;
|
||||||
int allowed;
|
int allowed;
|
||||||
|
|
||||||
sz = size_inside_page(p, count);
|
sz = size_inside_page(p, count);
|
||||||
|
|
||||||
|
err = -EPERM;
|
||||||
allowed = page_is_allowed(p >> PAGE_SHIFT);
|
allowed = page_is_allowed(p >> PAGE_SHIFT);
|
||||||
if (!allowed)
|
if (!allowed)
|
||||||
return -EPERM;
|
goto failed;
|
||||||
|
|
||||||
|
err = -EFAULT;
|
||||||
if (allowed == 2) {
|
if (allowed == 2) {
|
||||||
/* Show zeros for restricted memory. */
|
/* Show zeros for restricted memory. */
|
||||||
remaining = clear_user(buf, sz);
|
remaining = clear_user(buf, sz);
|
||||||
|
@ -149,24 +158,32 @@ static ssize_t read_mem(struct file *file, char __user *buf,
|
||||||
*/
|
*/
|
||||||
ptr = xlate_dev_mem_ptr(p);
|
ptr = xlate_dev_mem_ptr(p);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return -EFAULT;
|
goto failed;
|
||||||
|
|
||||||
remaining = copy_to_user(buf, ptr, sz);
|
|
||||||
|
|
||||||
|
err = probe_kernel_read(bounce, ptr, sz);
|
||||||
unxlate_dev_mem_ptr(p, ptr);
|
unxlate_dev_mem_ptr(p, ptr);
|
||||||
|
if (err)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
remaining = copy_to_user(buf, bounce, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remaining)
|
if (remaining)
|
||||||
return -EFAULT;
|
goto failed;
|
||||||
|
|
||||||
buf += sz;
|
buf += sz;
|
||||||
p += sz;
|
p += sz;
|
||||||
count -= sz;
|
count -= sz;
|
||||||
read += sz;
|
read += sz;
|
||||||
}
|
}
|
||||||
|
kfree(bounce);
|
||||||
|
|
||||||
*ppos += read;
|
*ppos += read;
|
||||||
return read;
|
return read;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
kfree(bounce);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t write_mem(struct file *file, const char __user *buf,
|
static ssize_t write_mem(struct file *file, const char __user *buf,
|
||||||
|
|
|
@ -143,6 +143,7 @@ config HARDENED_USERCOPY
|
||||||
bool "Harden memory copies between kernel and userspace"
|
bool "Harden memory copies between kernel and userspace"
|
||||||
depends on HAVE_HARDENED_USERCOPY_ALLOCATOR
|
depends on HAVE_HARDENED_USERCOPY_ALLOCATOR
|
||||||
select BUG
|
select BUG
|
||||||
|
imply STRICT_DEVMEM
|
||||||
help
|
help
|
||||||
This option checks for obviously wrong memory regions when
|
This option checks for obviously wrong memory regions when
|
||||||
copying memory to/from the kernel (via copy_to_user() and
|
copying memory to/from the kernel (via copy_to_user() and
|
||||||
|
|
Loading…
Reference in New Issue