diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-04 15:56:09 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-04 15:56:09 -0500 |
commit | 594cc251fdd0d231d342d88b2fdff4bc42fb0690 (patch) | |
tree | 259269a399e6504a7cf8739201cf172d1cbb197a | |
parent | 0b2c8f8b6b0c7530e2866c95862546d0da2057b0 (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.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem_execbuffer.c | 15 | ||||
-rw-r--r-- | include/linux/uaccess.h | 2 | ||||
-rw-r--r-- | kernel/compat.c | 6 | ||||
-rw-r--r-- | kernel/exit.c | 6 | ||||
-rw-r--r-- | lib/strncpy_from_user.c | 9 | ||||
-rw-r--r-- | lib/strnlen_user.c | 9 |
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() | 708 | static __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 | } |