Commit 3116ad38 authored by Eric Sandeen's avatar Eric Sandeen Committed by Linus Torvalds

kernel/sysctl.c: fix proc_do_large_bitmap for large input buffers

Today, proc_do_large_bitmap() truncates a large write input buffer to
PAGE_SIZE - 1, which may result in misparsed numbers at the (truncated)
end of the buffer.  Further, it fails to notify the caller that the
buffer was truncated, so it doesn't get called iteratively to finish the
entire input buffer.

Tell the caller if there's more work to do by adding the skipped amount
back to left/*lenp before returning.

To fix the misparsing, reset the position if we have completely consumed
a truncated buffer (or if just one char is left, which may be a "-" in a
range), and ask the caller to come back for more.

Link: http://lkml.kernel.org/r/20190320222831.8243-7-mcgrof@kernel.orgSigned-off-by: default avatarEric Sandeen <sandeen@redhat.com>
Signed-off-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
Acked-by: default avatarKees Cook <keescook@chromium.org>
Cc: Eric Sandeen <sandeen@sandeen.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 2ea622b8
...@@ -3172,9 +3172,13 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, ...@@ -3172,9 +3172,13 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
if (write) { if (write) {
char *kbuf, *p; char *kbuf, *p;
size_t skipped = 0;
if (left > PAGE_SIZE - 1) if (left > PAGE_SIZE - 1) {
left = PAGE_SIZE - 1; left = PAGE_SIZE - 1;
/* How much of the buffer we'll skip this pass */
skipped = *lenp - left;
}
p = kbuf = memdup_user_nul(buffer, left); p = kbuf = memdup_user_nul(buffer, left);
if (IS_ERR(kbuf)) if (IS_ERR(kbuf))
...@@ -3189,9 +3193,22 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, ...@@ -3189,9 +3193,22 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
while (!err && left) { while (!err && left) {
unsigned long val_a, val_b; unsigned long val_a, val_b;
bool neg; bool neg;
size_t saved_left;
/* In case we stop parsing mid-number, we can reset */
saved_left = left;
err = proc_get_long(&p, &left, &val_a, &neg, tr_a, err = proc_get_long(&p, &left, &val_a, &neg, tr_a,
sizeof(tr_a), &c); sizeof(tr_a), &c);
/*
* If we consumed the entirety of a truncated buffer or
* only one char is left (may be a "-"), then stop here,
* reset, & come back for more.
*/
if ((left <= 1) && skipped) {
left = saved_left;
break;
}
if (err) if (err)
break; break;
if (val_a >= bitmap_len || neg) { if (val_a >= bitmap_len || neg) {
...@@ -3209,6 +3226,15 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, ...@@ -3209,6 +3226,15 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
err = proc_get_long(&p, &left, &val_b, err = proc_get_long(&p, &left, &val_b,
&neg, tr_b, sizeof(tr_b), &neg, tr_b, sizeof(tr_b),
&c); &c);
/*
* If we consumed all of a truncated buffer or
* then stop here, reset, & come back for more.
*/
if (!left && skipped) {
left = saved_left;
break;
}
if (err) if (err)
break; break;
if (val_b >= bitmap_len || neg || if (val_b >= bitmap_len || neg ||
...@@ -3227,6 +3253,7 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, ...@@ -3227,6 +3253,7 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
proc_skip_char(&p, &left, '\n'); proc_skip_char(&p, &left, '\n');
} }
kfree(kbuf); kfree(kbuf);
left += skipped;
} else { } else {
unsigned long bit_a, bit_b = 0; unsigned long bit_a, bit_b = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment