x86: make word-at-a-time strncpy_from_user clear bytes at the end
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 Apr 2012 21:27:38 +0000 (14:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 Apr 2012 21:27:38 +0000 (14:27 -0700)
This makes the newly optimized x86 strncpy_from_user clear the final
bytes in the word past the final NUL character, rather than copy them as
the word they were in the source.

NOTE! Unlike the silly semantics of the libc 'strncpy()' function, the
kernel strncpy_from_user() has never cleared all of the end of the
destination buffer.  And neither does it do so now: it only clears the
bytes at the end of the last word it copied.

So why make this change at all? It doesn't really cost us anything extra
(we have to calculate the mask to get the length anyway), and it means
that *if* any user actually cares about zeroing the whole buffer, they
can do a "memset()" before the strncpy_from_user(), and we will no
longer write random bytes after the NUL character.

In particular, the buffer contents will now at no point contain random
source data from beyond the end of the string.

In other words, it makes behavior a bit more repeatable at no new cost,
so it's a small cleanup.  I've been carrying this as a patch for the
last few weeks or so in my tree (done at the same time the sign error
was fixed in commit 12e993b89464), I might as well commit it.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/x86/lib/usercopy.c

index d6ae30bbd7bb833356049d89bdf8d0e5ae004b0c..2e4e4b02c37a62cd593f3a59645898d946d95ffb 100644 (file)
@@ -44,13 +44,6 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n)
 }
 EXPORT_SYMBOL_GPL(copy_from_user_nmi);
 
-static inline unsigned long count_bytes(unsigned long mask)
-{
-       mask = (mask - 1) & ~mask;
-       mask >>= 7;
-       return count_masked_bytes(mask);
-}
-
 /*
  * Do a strncpy, return length of string without final '\0'.
  * 'count' is the user-supplied count (return 'count' if we
@@ -69,16 +62,19 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long
                max = count;
 
        while (max >= sizeof(unsigned long)) {
-               unsigned long c;
+               unsigned long c, mask;
 
                /* Fall back to byte-at-a-time if we get a page fault */
                if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
                        break;
-               /* This can write a few bytes past the NUL character, but that's ok */
+               mask = has_zero(c);
+               if (mask) {
+                       mask = (mask - 1) & ~mask;
+                       mask >>= 7;
+                       *(unsigned long *)(dst+res) = c & mask;
+                       return res + count_masked_bytes(mask);
+               }
                *(unsigned long *)(dst+res) = c;
-               c = has_zero(c);
-               if (c)
-                       return res + count_bytes(c);
                res += sizeof(unsigned long);
                max -= sizeof(unsigned long);
        }