diff options
author | Kees Cook <keescook@chromium.org> | 2017-12-01 16:19:39 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-12-18 09:59:17 -0500 |
commit | 22ec1a2aea73b9dfe340dff7945bd85af4cc6280 (patch) | |
tree | 92c1a38f121417e5702fe1f41ef99ea1004273ae | |
parent | d9cc5a0edb705ced4ce91b4c6ee73ec6f5bfa49a (diff) |
/dev/mem: Add bounce buffer for copy-out
As done for /proc/kcore in
commit df04abfd181a ("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: f5509cc18daa ("mm: Hardened usercopy")
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/char/mem.c | 27 | ||||
-rw-r--r-- | security/Kconfig | 1 |
2 files changed, 23 insertions, 5 deletions
diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6aefe5370e5b..052011bcf100 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c | |||
@@ -107,6 +107,8 @@ static ssize_t read_mem(struct file *file, char __user *buf, | |||
107 | phys_addr_t p = *ppos; | 107 | phys_addr_t p = *ppos; |
108 | ssize_t read, sz; | 108 | ssize_t read, sz; |
109 | void *ptr; | 109 | void *ptr; |
110 | char *bounce; | ||
111 | int err; | ||
110 | 112 | ||
111 | if (p != *ppos) | 113 | if (p != *ppos) |
112 | return 0; | 114 | return 0; |
@@ -129,15 +131,22 @@ static ssize_t read_mem(struct file *file, char __user *buf, | |||
129 | } | 131 | } |
130 | #endif | 132 | #endif |
131 | 133 | ||
134 | bounce = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
135 | if (!bounce) | ||
136 | return -ENOMEM; | ||
137 | |||
132 | while (count > 0) { | 138 | while (count > 0) { |
133 | unsigned long remaining; | 139 | unsigned long remaining; |
134 | int allowed; | 140 | int allowed; |
135 | 141 | ||
136 | sz = size_inside_page(p, count); | 142 | sz = size_inside_page(p, count); |
137 | 143 | ||
144 | err = -EPERM; | ||
138 | allowed = page_is_allowed(p >> PAGE_SHIFT); | 145 | allowed = page_is_allowed(p >> PAGE_SHIFT); |
139 | if (!allowed) | 146 | if (!allowed) |
140 | return -EPERM; | 147 | goto failed; |
148 | |||
149 | err = -EFAULT; | ||
141 | if (allowed == 2) { | 150 | if (allowed == 2) { |
142 | /* Show zeros for restricted memory. */ | 151 | /* Show zeros for restricted memory. */ |
143 | remaining = clear_user(buf, sz); | 152 | remaining = clear_user(buf, sz); |
@@ -149,24 +158,32 @@ static ssize_t read_mem(struct file *file, char __user *buf, | |||
149 | */ | 158 | */ |
150 | ptr = xlate_dev_mem_ptr(p); | 159 | ptr = xlate_dev_mem_ptr(p); |
151 | if (!ptr) | 160 | if (!ptr) |
152 | return -EFAULT; | 161 | goto failed; |
153 | |||
154 | remaining = copy_to_user(buf, ptr, sz); | ||
155 | 162 | ||
163 | err = probe_kernel_read(bounce, ptr, sz); | ||
156 | unxlate_dev_mem_ptr(p, ptr); | 164 | unxlate_dev_mem_ptr(p, ptr); |
165 | if (err) | ||
166 | goto failed; | ||
167 | |||
168 | remaining = copy_to_user(buf, bounce, sz); | ||
157 | } | 169 | } |
158 | 170 | ||
159 | if (remaining) | 171 | if (remaining) |
160 | return -EFAULT; | 172 | goto failed; |
161 | 173 | ||
162 | buf += sz; | 174 | buf += sz; |
163 | p += sz; | 175 | p += sz; |
164 | count -= sz; | 176 | count -= sz; |
165 | read += sz; | 177 | read += sz; |
166 | } | 178 | } |
179 | kfree(bounce); | ||
167 | 180 | ||
168 | *ppos += read; | 181 | *ppos += read; |
169 | return read; | 182 | return read; |
183 | |||
184 | failed: | ||
185 | kfree(bounce); | ||
186 | return err; | ||
170 | } | 187 | } |
171 | 188 | ||
172 | static ssize_t write_mem(struct file *file, const char __user *buf, | 189 | static ssize_t write_mem(struct file *file, const char __user *buf, |
diff --git a/security/Kconfig b/security/Kconfig index e8e449444e65..3b4effd8bbc2 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
@@ -143,6 +143,7 @@ config HARDENED_USERCOPY | |||
143 | bool "Harden memory copies between kernel and userspace" | 143 | bool "Harden memory copies between kernel and userspace" |
144 | depends on HAVE_HARDENED_USERCOPY_ALLOCATOR | 144 | depends on HAVE_HARDENED_USERCOPY_ALLOCATOR |
145 | select BUG | 145 | select BUG |
146 | imply STRICT_DEVMEM | ||
146 | help | 147 | help |
147 | This option checks for obviously wrong memory regions when | 148 | This option checks for obviously wrong memory regions when |
148 | copying memory to/from the kernel (via copy_to_user() and | 149 | copying memory to/from the kernel (via copy_to_user() and |