aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Murphy <robin.murphy@arm.com>2017-01-05 12:15:01 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-19 14:18:03 -0500
commit3fbaff3adc763d999fa803bc1aeb5e49c48ce5c0 (patch)
tree11359187efd8dcd81499f4550a05381e17b47e92
parent1a62a0f76556f39d6d67789bb981b28230aaa338 (diff)
drivers: char: mem: Fix thinkos in kmem address checks
commit 488debb9971bc7d0edd6d8080ba78ca02a04f6c4 upstream. When borrowing the pfn_valid() check from mmap_kmem(), somebody managed to get physical and virtual addresses spectacularly muddled up, such that we've ended up with checks for one being the other. Whilst this does indeed prevent out-of-bounds accesses crashing, on most systems it also prevents the more desirable use-case of working at all ever. Check the *virtual* offset correctly for what it is. Furthermore, do so in the right place - a read or write may span multiple pages, so a single up-front check is insufficient. High memory accesses already have a similar validity check just before the copy_to_user() call, so just make the low memory path fully consistent with that. Reported-by: Jason A. Donenfeld <Jason@zx2c4.com> Fixes: 148a1bc84398 ("drivers: char: mem: Check {read,write}_kmem() addresses") Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/char/mem.c10
1 files changed, 4 insertions, 6 deletions
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 5bb1985ec484..6d9cc2d39d22 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -381,9 +381,6 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
381 char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ 381 char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
382 int err = 0; 382 int err = 0;
383 383
384 if (!pfn_valid(PFN_DOWN(p)))
385 return -EIO;
386
387 read = 0; 384 read = 0;
388 if (p < (unsigned long) high_memory) { 385 if (p < (unsigned long) high_memory) {
389 low_count = count; 386 low_count = count;
@@ -412,6 +409,8 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
412 * by the kernel or data corruption may occur 409 * by the kernel or data corruption may occur
413 */ 410 */
414 kbuf = xlate_dev_kmem_ptr((void *)p); 411 kbuf = xlate_dev_kmem_ptr((void *)p);
412 if (!virt_addr_valid(kbuf))
413 return -ENXIO;
415 414
416 if (copy_to_user(buf, kbuf, sz)) 415 if (copy_to_user(buf, kbuf, sz))
417 return -EFAULT; 416 return -EFAULT;
@@ -482,6 +481,8 @@ static ssize_t do_write_kmem(unsigned long p, const char __user *buf,
482 * corruption may occur. 481 * corruption may occur.
483 */ 482 */
484 ptr = xlate_dev_kmem_ptr((void *)p); 483 ptr = xlate_dev_kmem_ptr((void *)p);
484 if (!virt_addr_valid(ptr))
485 return -ENXIO;
485 486
486 copied = copy_from_user(ptr, buf, sz); 487 copied = copy_from_user(ptr, buf, sz);
487 if (copied) { 488 if (copied) {
@@ -512,9 +513,6 @@ static ssize_t write_kmem(struct file *file, const char __user *buf,
512 char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ 513 char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
513 int err = 0; 514 int err = 0;
514 515
515 if (!pfn_valid(PFN_DOWN(p)))
516 return -EIO;
517
518 if (p < (unsigned long) high_memory) { 516 if (p < (unsigned long) high_memory) {
519 unsigned long to_write = min_t(unsigned long, count, 517 unsigned long to_write = min_t(unsigned long, count,
520 (unsigned long)high_memory - p); 518 (unsigned long)high_memory - p);