aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-01-04 15:56:09 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2019-01-04 15:56:09 -0500
commit594cc251fdd0d231d342d88b2fdff4bc42fb0690 (patch)
tree259269a399e6504a7cf8739201cf172d1cbb197a
parent0b2c8f8b6b0c7530e2866c95862546d0da2057b0 (diff)
make 'user_access_begin()' do 'access_ok()'
Originally, the rule used to be that you'd have to do access_ok() separately, and then user_access_begin() before actually doing the direct (optimized) user access. But experience has shown that people then decide not to do access_ok() at all, and instead rely on it being implied by other operations or similar. Which makes it very hard to verify that the access has actually been range-checked. If you use the unsafe direct user accesses, hardware features (either SMAP - Supervisor Mode Access Protection - on x86, or PAN - Privileged Access Never - on ARM) do force you to use user_access_begin(). But nothing really forces the range check. By putting the range check into user_access_begin(), we actually force people to do the right thing (tm), and the range check vill be visible near the actual accesses. We have way too long a history of people trying to avoid them. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/x86/include/asm/uaccess.h9
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c15
-rw-r--r--include/linux/uaccess.h2
-rw-r--r--kernel/compat.c6
-rw-r--r--kernel/exit.c6
-rw-r--r--lib/strncpy_from_user.c9
-rw-r--r--lib/strnlen_user.c9
7 files changed, 36 insertions, 20 deletions
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 3920f456db79..a87ab5290ab4 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -705,7 +705,14 @@ extern struct movsl_mask {
705 * checking before using them, but you have to surround them with the 705 * checking before using them, but you have to surround them with the
706 * user_access_begin/end() pair. 706 * user_access_begin/end() pair.
707 */ 707 */
708#define user_access_begin() __uaccess_begin() 708static __must_check inline bool user_access_begin(const void __user *ptr, size_t len)
709{
710 if (unlikely(!access_ok(ptr,len)))
711 return 0;
712 __uaccess_begin();
713 return 1;
714}
715#define user_access_begin(a,b) user_access_begin(a,b)
709#define user_access_end() __uaccess_end() 716#define user_access_end() __uaccess_end()
710 717
711#define unsafe_put_user(x, ptr, err_label) \ 718#define unsafe_put_user(x, ptr, err_label) \
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 55d8f9b8777f..485b259127c3 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -1624,7 +1624,9 @@ end_user:
1624 * happened we would make the mistake of assuming that the 1624 * happened we would make the mistake of assuming that the
1625 * relocations were valid. 1625 * relocations were valid.
1626 */ 1626 */
1627 user_access_begin(); 1627 if (!user_access_begin(urelocs, size))
1628 goto end_user;
1629
1628 for (copied = 0; copied < nreloc; copied++) 1630 for (copied = 0; copied < nreloc; copied++)
1629 unsafe_put_user(-1, 1631 unsafe_put_user(-1,
1630 &urelocs[copied].presumed_offset, 1632 &urelocs[copied].presumed_offset,
@@ -2606,7 +2608,16 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
2606 unsigned int i; 2608 unsigned int i;
2607 2609
2608 /* Copy the new buffer offsets back to the user's exec list. */ 2610 /* Copy the new buffer offsets back to the user's exec list. */
2609 user_access_begin(); 2611 /*
2612 * Note: count * sizeof(*user_exec_list) does not overflow,
2613 * because we checked 'count' in check_buffer_count().
2614 *
2615 * And this range already got effectively checked earlier
2616 * when we did the "copy_from_user()" above.
2617 */
2618 if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list)))
2619 goto end_user;
2620
2610 for (i = 0; i < args->buffer_count; i++) { 2621 for (i = 0; i < args->buffer_count; i++) {
2611 if (!(exec2_list[i].offset & UPDATE)) 2622 if (!(exec2_list[i].offset & UPDATE))
2612 continue; 2623 continue;
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index bf2523867a02..37b226e8df13 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -264,7 +264,7 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
264 probe_kernel_read(&retval, addr, sizeof(retval)) 264 probe_kernel_read(&retval, addr, sizeof(retval))
265 265
266#ifndef user_access_begin 266#ifndef user_access_begin
267#define user_access_begin() do { } while (0) 267#define user_access_begin(ptr,len) access_ok(ptr, len)
268#define user_access_end() do { } while (0) 268#define user_access_end() do { } while (0)
269#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0) 269#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0)
270#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0) 270#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0)
diff --git a/kernel/compat.c b/kernel/compat.c
index 705d4ae6c018..f01affa17e22 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -354,10 +354,9 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
354 bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG); 354 bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
355 nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size); 355 nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
356 356
357 if (!access_ok(umask, bitmap_size / 8)) 357 if (!user_access_begin(umask, bitmap_size / 8))
358 return -EFAULT; 358 return -EFAULT;
359 359
360 user_access_begin();
361 while (nr_compat_longs > 1) { 360 while (nr_compat_longs > 1) {
362 compat_ulong_t l1, l2; 361 compat_ulong_t l1, l2;
363 unsafe_get_user(l1, umask++, Efault); 362 unsafe_get_user(l1, umask++, Efault);
@@ -384,10 +383,9 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
384 bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG); 383 bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
385 nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size); 384 nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
386 385
387 if (!access_ok(umask, bitmap_size / 8)) 386 if (!user_access_begin(umask, bitmap_size / 8))
388 return -EFAULT; 387 return -EFAULT;
389 388
390 user_access_begin();
391 while (nr_compat_longs > 1) { 389 while (nr_compat_longs > 1) {
392 unsigned long m = *mask++; 390 unsigned long m = *mask++;
393 unsafe_put_user((compat_ulong_t)m, umask++, Efault); 391 unsafe_put_user((compat_ulong_t)m, umask++, Efault);
diff --git a/kernel/exit.c b/kernel/exit.c
index 8a01b671dc1f..2d14979577ee 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -1604,10 +1604,9 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
1604 if (!infop) 1604 if (!infop)
1605 return err; 1605 return err;
1606 1606
1607 if (!access_ok(infop, sizeof(*infop))) 1607 if (!user_access_begin(infop, sizeof(*infop)))
1608 return -EFAULT; 1608 return -EFAULT;
1609 1609
1610 user_access_begin();
1611 unsafe_put_user(signo, &infop->si_signo, Efault); 1610 unsafe_put_user(signo, &infop->si_signo, Efault);
1612 unsafe_put_user(0, &infop->si_errno, Efault); 1611 unsafe_put_user(0, &infop->si_errno, Efault);
1613 unsafe_put_user(info.cause, &infop->si_code, Efault); 1612 unsafe_put_user(info.cause, &infop->si_code, Efault);
@@ -1732,10 +1731,9 @@ COMPAT_SYSCALL_DEFINE5(waitid,
1732 if (!infop) 1731 if (!infop)
1733 return err; 1732 return err;
1734 1733
1735 if (!access_ok(infop, sizeof(*infop))) 1734 if (!user_access_begin(infop, sizeof(*infop)))
1736 return -EFAULT; 1735 return -EFAULT;
1737 1736
1738 user_access_begin();
1739 unsafe_put_user(signo, &infop->si_signo, Efault); 1737 unsafe_put_user(signo, &infop->si_signo, Efault);
1740 unsafe_put_user(0, &infop->si_errno, Efault); 1738 unsafe_put_user(0, &infop->si_errno, Efault);
1741 unsafe_put_user(info.cause, &infop->si_code, Efault); 1739 unsafe_put_user(info.cause, &infop->si_code, Efault);
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index b53e1b5d80f4..58eacd41526c 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -114,10 +114,11 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
114 114
115 kasan_check_write(dst, count); 115 kasan_check_write(dst, count);
116 check_object_size(dst, count, false); 116 check_object_size(dst, count, false);
117 user_access_begin(); 117 if (user_access_begin(src, max)) {
118 retval = do_strncpy_from_user(dst, src, count, max); 118 retval = do_strncpy_from_user(dst, src, count, max);
119 user_access_end(); 119 user_access_end();
120 return retval; 120 return retval;
121 }
121 } 122 }
122 return -EFAULT; 123 return -EFAULT;
123} 124}
diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c
index 60d0bbda8f5e..1c1a1b0e38a5 100644
--- a/lib/strnlen_user.c
+++ b/lib/strnlen_user.c
@@ -114,10 +114,11 @@ long strnlen_user(const char __user *str, long count)
114 unsigned long max = max_addr - src_addr; 114 unsigned long max = max_addr - src_addr;
115 long retval; 115 long retval;
116 116
117 user_access_begin(); 117 if (user_access_begin(str, max)) {
118 retval = do_strnlen_user(str, count, max); 118 retval = do_strnlen_user(str, count, max);
119 user_access_end(); 119 user_access_end();
120 return retval; 120 return retval;
121 }
121 } 122 }
122 return 0; 123 return 0;
123} 124}