Skip to content

Commit 594cc25

Browse files
committed
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 <[email protected]>
1 parent 0b2c8f8 commit 594cc25

File tree

7 files changed

+36
-20
lines changed

7 files changed

+36
-20
lines changed

arch/x86/include/asm/uaccess.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,14 @@ extern struct movsl_mask {
705705
* checking before using them, but you have to surround them with the
706706
* user_access_begin/end() pair.
707707
*/
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)
709716
#define user_access_end() __uaccess_end()
710717

711718
#define unsafe_put_user(x, ptr, err_label) \

drivers/gpu/drm/i915/i915_gem_execbuffer.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,9 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb)
16241624
* happened we would make the mistake of assuming that the
16251625
* relocations were valid.
16261626
*/
1627-
user_access_begin();
1627+
if (!user_access_begin(urelocs, size))
1628+
goto end_user;
1629+
16281630
for (copied = 0; copied < nreloc; copied++)
16291631
unsafe_put_user(-1,
16301632
&urelocs[copied].presumed_offset,
@@ -2606,7 +2608,16 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
26062608
unsigned int i;
26072609

26082610
/* 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+
26102621
for (i = 0; i < args->buffer_count; i++) {
26112622
if (!(exec2_list[i].offset & UPDATE))
26122623
continue;

include/linux/uaccess.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
264264
probe_kernel_read(&retval, addr, sizeof(retval))
265265

266266
#ifndef user_access_begin
267-
#define user_access_begin() do { } while (0)
267+
#define user_access_begin(ptr,len) access_ok(ptr, len)
268268
#define user_access_end() do { } while (0)
269269
#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0)
270270
#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0)

kernel/compat.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,10 +354,9 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
354354
bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
355355
nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
356356

357-
if (!access_ok(umask, bitmap_size / 8))
357+
if (!user_access_begin(umask, bitmap_size / 8))
358358
return -EFAULT;
359359

360-
user_access_begin();
361360
while (nr_compat_longs > 1) {
362361
compat_ulong_t l1, l2;
363362
unsafe_get_user(l1, umask++, Efault);
@@ -384,10 +383,9 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
384383
bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
385384
nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
386385

387-
if (!access_ok(umask, bitmap_size / 8))
386+
if (!user_access_begin(umask, bitmap_size / 8))
388387
return -EFAULT;
389388

390-
user_access_begin();
391389
while (nr_compat_longs > 1) {
392390
unsigned long m = *mask++;
393391
unsafe_put_user((compat_ulong_t)m, umask++, Efault);

kernel/exit.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,10 +1604,9 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
16041604
if (!infop)
16051605
return err;
16061606

1607-
if (!access_ok(infop, sizeof(*infop)))
1607+
if (!user_access_begin(infop, sizeof(*infop)))
16081608
return -EFAULT;
16091609

1610-
user_access_begin();
16111610
unsafe_put_user(signo, &infop->si_signo, Efault);
16121611
unsafe_put_user(0, &infop->si_errno, Efault);
16131612
unsafe_put_user(info.cause, &infop->si_code, Efault);
@@ -1732,10 +1731,9 @@ COMPAT_SYSCALL_DEFINE5(waitid,
17321731
if (!infop)
17331732
return err;
17341733

1735-
if (!access_ok(infop, sizeof(*infop)))
1734+
if (!user_access_begin(infop, sizeof(*infop)))
17361735
return -EFAULT;
17371736

1738-
user_access_begin();
17391737
unsafe_put_user(signo, &infop->si_signo, Efault);
17401738
unsafe_put_user(0, &infop->si_errno, Efault);
17411739
unsafe_put_user(info.cause, &infop->si_code, Efault);

lib/strncpy_from_user.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
114114

115115
kasan_check_write(dst, count);
116116
check_object_size(dst, count, false);
117-
user_access_begin();
118-
retval = do_strncpy_from_user(dst, src, count, max);
119-
user_access_end();
120-
return retval;
117+
if (user_access_begin(src, max)) {
118+
retval = do_strncpy_from_user(dst, src, count, max);
119+
user_access_end();
120+
return retval;
121+
}
121122
}
122123
return -EFAULT;
123124
}

lib/strnlen_user.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ long strnlen_user(const char __user *str, long count)
114114
unsigned long max = max_addr - src_addr;
115115
long retval;
116116

117-
user_access_begin();
118-
retval = do_strnlen_user(str, count, max);
119-
user_access_end();
120-
return retval;
117+
if (user_access_begin(str, max)) {
118+
retval = do_strnlen_user(str, count, max);
119+
user_access_end();
120+
return retval;
121+
}
121122
}
122123
return 0;
123124
}

0 commit comments

Comments
 (0)