Re: [RFC 2/4] lib/strncpy_from_user: Remove redundant user space pointer range check
From: Linus Torvalds
Date: Tue Jan 14 2020 - 16:22:30 EST
On Tue, Jan 14, 2020 at 12:09 PM Vineet Gupta
<Vineet.Gupta1@xxxxxxxxxxxx> wrote:
>
> This came up when switching ARC to word-at-a-time interface and using
> generic/optimized strncpy_from_user
>
> It seems the existing code checks for user buffer/string range multiple
> times and one of tem cn be avoided.
NO!
DO NOT DO THIS.
This is seriously buggy.
> long strncpy_from_user(char *dst, const char __user *src, long count)
> {
> - unsigned long max_addr, src_addr;
> -
> if (unlikely(count <= 0))
> return 0;
>
> - max_addr = user_addr_max();
> - src_addr = (unsigned long)untagged_addr(src);
> - if (likely(src_addr < max_addr)) {
> - unsigned long max = max_addr - src_addr;
> + kasan_check_write(dst, count);
> + check_object_size(dst, count, false);
> + if (user_access_begin(src, count)) {
You can't do that "user_access_begin(src, count)", because "count" is
the maximum _possible_ length, but it is *NOT* necessarily the actual
length of the string we really get from user space!
Think of this situation:
- user has a 5-byte string at the end of the address space
- kernel does a
n = strncpy_from_user(uaddr, page, PAGE_SIZE)
now your "user_access_begin(src, count)" will _fail_, because "uaddr"
is close to the end of the user address space, and there's not room
for PAGE_SIZE bytes any more.
But "count" isn't actually how many bytes we will access from user
space, it's only the maximum limit on the *target*. IOW, it's about a
kernel buffer size, not about the user access size.
Because we'll only access that 5-byte string, which fits just fine in
the user space, and doing that "user_access_begin(src, count)" gives
the wrong answer.
The fact is, copying a string from user space is *very* different from
copying a fixed number of bytes, and that whole dance with
max_addr = user_addr_max();
is absolutely required and necessary.
You completely broke string copying.
It is very possible that string copying was horribly broken on ARC
before too - almost nobody ever gets this right, but the generic
routine does.
So the generic routine is not only faster, it is *correct*, and your
change broke it.
Don't touch generic code. If you want to use the generic code, please
do so. But DO NOT TOUCH IT. It is correct, your patch is wrong.
The exact same issue is true in strnlen_user(). Don't break it.
Linus