* bug reading /proc/sys/kernel/*: only first byte read.
@ 2006-10-20 12:43 Michael Tokarev
2007-01-30 10:24 ` Andrew Morton
0 siblings, 1 reply; 6+ messages in thread
From: Michael Tokarev @ 2006-10-20 12:43 UTC (permalink / raw)
To: Kernel Mailing List
I were debugging a weird problem with busybox, and come across
this chunk of strace output:
open("/proc/sys/kernel/osrelease", O_RDONLY) = 3
read(3, "2", 1) = 1
read(3, "", 1) = 0
close(3) = 0
As you can see, after reading one byte from /proc/sys/kernel/osrelease,
next read() returns 0, which is treated as end-of-file by an application.
Why busybox does this single-byte reads is another question (many
shells does that, in order to be able to stop reading at newline).
But this is definitely a bug in kernel, and should be fixed....
It exists in 2.6.17 and 2.6.18
Thanks.
/mjt
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: bug reading /proc/sys/kernel/*: only first byte read. 2006-10-20 12:43 bug reading /proc/sys/kernel/*: only first byte read Michael Tokarev @ 2007-01-30 10:24 ` Andrew Morton 2007-01-30 13:25 ` Oleg Nesterov 0 siblings, 1 reply; 6+ messages in thread From: Andrew Morton @ 2007-01-30 10:24 UTC (permalink / raw) To: Michael Tokarev; +Cc: Kernel Mailing List, Oleg Nesterov, Eric W. Biederman On Fri, 20 Oct 2006 16:43:39 +0400 Michael Tokarev <mjt@tls.msk.ru> wrote: > I were debugging a weird problem with busybox, and come across > this chunk of strace output: > > open("/proc/sys/kernel/osrelease", O_RDONLY) = 3 > read(3, "2", 1) = 1 > read(3, "", 1) = 0 > close(3) = 0 > > As you can see, after reading one byte from /proc/sys/kernel/osrelease, > next read() returns 0, which is treated as end-of-file by an application. > > Why busybox does this single-byte reads is another question (many > shells does that, in order to be able to stop reading at newline). > > But this is definitely a bug in kernel, and should be fixed.... > > It exists in 2.6.17 and 2.6.18 > Well this nearly killed me. kernel-side proc handlers are ghastly things. Could I have this reviewed please? It surely has a hole in it somewhere. From: Andrew Morton <akpm@osdl.org> If you try to read things like /proc/sys/kernel/osrelease with single-byte reads, you get just one byte and then EOF. This is because _proc_do_string() assumes that the caller is read()ing into a buffer which is large enough to fit the whole string in a single hit. Fix. Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> --- kernel/sysctl.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff -puN kernel/sysctl.c~_proc_do_string-fix-short-reads kernel/sysctl.c --- a/kernel/sysctl.c~_proc_do_string-fix-short-reads +++ a/kernel/sysctl.c @@ -1682,8 +1682,7 @@ static int _proc_do_string(void* data, i char __user *p; char c; - if (!data || !maxlen || !*lenp || - (*ppos && !write)) { + if (!data || !maxlen || !*lenp) { *lenp = 0; return 0; } @@ -1705,18 +1704,38 @@ static int _proc_do_string(void* data, i ((char *) data)[len] = 0; *ppos += *lenp; } else { - len = strlen(data); + loff_t pos = *ppos; + const size_t slen = strlen(data); + + /* + * len is the amount of data to copy, and becomes the amount of + * data which was copied + */ + len = slen; + if (pos > len) { + *lenp = 0; + return 0; + } if (len > maxlen) len = maxlen; if (len > *lenp) len = *lenp; - if (len) - if(copy_to_user(buffer, data, len)) - return -EFAULT; - if (len < *lenp) { - if(put_user('\n', ((char __user *) buffer) + len)) + /* Don't copy past the end of the string */ + if (len > slen - pos) + len = slen - pos; + data += pos; + /* Copy as much of the string as we can */ + if (len) { + if (copy_to_user(buffer, data, len)) return -EFAULT; - len++; + } + /* If we copied the whole string, now write a \n */ + if (len + pos == slen) { + if (len + pos < maxlen) { + if (put_user('\n', (char __user *)buffer + len)) + return -EFAULT; + len++; + } } *lenp = len; *ppos += len; _ ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: bug reading /proc/sys/kernel/*: only first byte read. 2007-01-30 10:24 ` Andrew Morton @ 2007-01-30 13:25 ` Oleg Nesterov 2007-01-30 14:00 ` Oleg Nesterov 0 siblings, 1 reply; 6+ messages in thread From: Oleg Nesterov @ 2007-01-30 13:25 UTC (permalink / raw) To: Andrew Morton; +Cc: Michael Tokarev, Kernel Mailing List, Eric W. Biederman On 01/30, Andrew Morton wrote: > > @@ -1705,18 +1704,38 @@ static int _proc_do_string(void* data, i > ((char *) data)[len] = 0; > *ppos += *lenp; > } else { > - len = strlen(data); > + loff_t pos = *ppos; > + const size_t slen = strlen(data); > + > + /* > + * len is the amount of data to copy, and becomes the amount of > + * data which was copied > + */ > + len = slen; > + if (pos > len) { > + *lenp = 0; > + return 0; > + } > if (len > maxlen) > len = maxlen; > if (len > *lenp) > len = *lenp; > - if (len) > - if(copy_to_user(buffer, data, len)) > - return -EFAULT; > - if (len < *lenp) { > - if(put_user('\n', ((char __user *) buffer) + len)) > + /* Don't copy past the end of the string */ > + if (len > slen - pos) > + len = slen - pos; > + data += pos; > + /* Copy as much of the string as we can */ > + if (len) { > + if (copy_to_user(buffer, data, len)) > return -EFAULT; > - len++; > + } > + /* If we copied the whole string, now write a \n */ > + if (len + pos == slen) { > + if (len + pos < maxlen) { ^^^^^^^^^^^^^^^^^^^^^^^ Shouldn't this be if (len < *lenp) ? > + if (put_user('\n', (char __user *)buffer + len)) > + return -EFAULT; > + len++; > + } > } > *lenp = len; > *ppos += len; ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: bug reading /proc/sys/kernel/*: only first byte read. 2007-01-30 13:25 ` Oleg Nesterov @ 2007-01-30 14:00 ` Oleg Nesterov 2007-01-30 15:07 ` Eric W. Biederman 0 siblings, 1 reply; 6+ messages in thread From: Oleg Nesterov @ 2007-01-30 14:00 UTC (permalink / raw) To: Andrew Morton; +Cc: Michael Tokarev, Kernel Mailing List, Eric W. Biederman On 01/30, Oleg Nesterov wrote: > > On 01/30, Andrew Morton wrote: > > > > + if (len + pos < maxlen) { > ^^^^^^^^^^^^^^^^^^^^^^^ > Shouldn't this be > if (len < *lenp) > > ? On the other hand. If we may assume that original code was correct, we can make a simpler patch? Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> --- 6.19/kernel/sysctl.c~ 2006-11-17 19:42:31.000000000 +0300 +++ 6.19/kernel/sysctl.c 2007-01-30 16:46:00.000000000 +0300 @@ -1683,13 +1683,12 @@ static int _proc_do_string(void* data, i size_t len; char __user *p; char c; - - if (!data || !maxlen || !*lenp || - (*ppos && !write)) { + + if (!data || !maxlen || !*lenp) { *lenp = 0; return 0; } - + if (write) { len = 0; p = buffer; @@ -1710,6 +1709,15 @@ static int _proc_do_string(void* data, i len = strlen(data); if (len > maxlen) len = maxlen; + + if (*ppos > len) { + *lenp = 0; + return 0; + } + + data += *ppos; + len -= *ppos; + if (len > *lenp) len = *lenp; if (len) ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: bug reading /proc/sys/kernel/*: only first byte read. 2007-01-30 14:00 ` Oleg Nesterov @ 2007-01-30 15:07 ` Eric W. Biederman 2007-01-30 15:57 ` Michael Tokarev 0 siblings, 1 reply; 6+ messages in thread From: Eric W. Biederman @ 2007-01-30 15:07 UTC (permalink / raw) To: Oleg Nesterov; +Cc: Andrew Morton, Michael Tokarev, Kernel Mailing List Oleg Nesterov <oleg@tv-sign.ru> writes: > On 01/30, Oleg Nesterov wrote: >> >> On 01/30, Andrew Morton wrote: >> > >> > + if (len + pos < maxlen) { >> ^^^^^^^^^^^^^^^^^^^^^^^ >> Shouldn't this be >> if (len < *lenp) >> >> ? > > On the other hand. If we may assume that original code was correct, > we can make a simpler patch? I think there is an issue worth fixing her. However as far as I can tell the code has this limitation deliberately for simplicity. Getting the string side of this fixed even by itself is worthwhile, although it might be worth teach people about sys_uname and /bin/uname. It seems is the biggest thing people look at /proc/sys/ for... For the non-string data the only way we can do this is to generate it into a temporary buffer and the read out the part of the buffer the user is requesting. Isn't there something in seq_file that will do this? On the other side I'm fairly certain we can't support short writes to magic sysctl files on the write side of things. I do have to say I like Oleg short version of this patch. Eric ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: bug reading /proc/sys/kernel/*: only first byte read. 2007-01-30 15:07 ` Eric W. Biederman @ 2007-01-30 15:57 ` Michael Tokarev 0 siblings, 0 replies; 6+ messages in thread From: Michael Tokarev @ 2007-01-30 15:57 UTC (permalink / raw) To: Eric W. Biederman; +Cc: Oleg Nesterov, Andrew Morton, Kernel Mailing List Eric W. Biederman wrote: [] > However as far as I can tell the code has this limitation > deliberately for simplicity. > > Getting the string side of this fixed even by itself is > worthwhile, although it might be worth teach people > about sys_uname and /bin/uname. It seems is the biggest thing > people look at /proc/sys/ for... I come across this very issue when I discovered another bug, using un-initialized pipefs structures during early hotplug calls. I know about /bin/uname, but it requires a pipe, so wont work. Obvious alternative is /proc/sys/version, which is alot faster too (no fork+exec overhead). /mjt ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2007-01-30 15:57 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-10-20 12:43 bug reading /proc/sys/kernel/*: only first byte read Michael Tokarev 2007-01-30 10:24 ` Andrew Morton 2007-01-30 13:25 ` Oleg Nesterov 2007-01-30 14:00 ` Oleg Nesterov 2007-01-30 15:07 ` Eric W. Biederman 2007-01-30 15:57 ` Michael Tokarev
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox