sysctl: refactor sysctl string writing logic
authorKees Cook <keescook@chromium.org>
Fri, 6 Jun 2014 21:37:18 +0000 (14:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 Jun 2014 23:08:13 +0000 (16:08 -0700)
Consolidate buffer length checking with new-line/end-of-line checking.
Additionally, instead of reading user memory twice, just do the
assignment during the loop.

This change doesn't affect the potential races here.  It was already
possible to read a sysctl that was in the middle of a write.  In both
cases, the string will always be NULL terminated.  The pre-existing race
remains a problem to be solved.

Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
kernel/sysctl.c

index 3e214beabbe9aed686ac2e226837b6e7d94f0279..ac6847feaa83bb6bcb7d86366590a7dd26d6c591 100644 (file)
@@ -1717,21 +1717,18 @@ static int _proc_do_string(char *data, int maxlen, int write,
        }
 
        if (write) {
+               /* Start writing from beginning of buffer. */
                len = 0;
+               *ppos += *lenp;
                p = buffer;
-               while (len < *lenp) {
+               while ((p - buffer) < *lenp && len < maxlen - 1) {
                        if (get_user(c, p++))
                                return -EFAULT;
                        if (c == 0 || c == '\n')
                                break;
-                       len++;
+                       data[len++] = c;
                }
-               if (len >= maxlen)
-                       len = maxlen-1;
-               if(copy_from_user(data, buffer, len))
-                       return -EFAULT;
                data[len] = 0;
-               *ppos += *lenp;
        } else {
                len = strlen(data);
                if (len > maxlen)