* copy_mount_options() @ 2004-08-20 20:01 David S. Miller 2004-08-20 20:10 ` copy_mount_options() Andrew Morton 2004-08-20 23:15 ` copy_mount_options() Benjamin Herrenschmidt 0 siblings, 2 replies; 12+ messages in thread From: David S. Miller @ 2004-08-20 20:01 UTC (permalink / raw) To: linux-arch So the sparc64 user copy bug I fixed recently is pretty much present on every platform. Basically, copy_mount_options() requires exact byte granularity to exception reporting from copy_from_user(). If you don't do this it can break things like busybox's mount(). Even reporting on a word boundary is illegal. On sparc64 it was quite poignant because we can report on a 64-byte boundary for large copies because that is the granularity of the load/store we use. Other platforms will need to fix this. I recommend a two stage exception handling scheme. Basically, on the first exception, you merely note that an exception occurred and you retry the user copy a byte at a time until you hit the exact address that fails. You cannot optimize this to just check a page at a time, because copy_mount_options wants all the data to be there. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 20:01 copy_mount_options() David S. Miller @ 2004-08-20 20:10 ` Andrew Morton 2004-08-20 21:11 ` copy_mount_options() David S. Miller 2004-08-20 23:15 ` copy_mount_options() Benjamin Herrenschmidt 1 sibling, 1 reply; 12+ messages in thread From: Andrew Morton @ 2004-08-20 20:10 UTC (permalink / raw) To: David S. Miller; +Cc: linux-arch "David S. Miller" <davem@redhat.com> wrote: > > > So the sparc64 user copy bug I fixed recently is pretty > much present on every platform. > > Basically, copy_mount_options() requires exact byte granularity > to exception reporting from copy_from_user(). If you don't > do this it can break things like busybox's mount(). > > Even reporting on a word boundary is illegal. On sparc64 > it was quite poignant because we can report on a 64-byte > boundary for large copies because that is the granularity > of the load/store we use. > > Other platforms will need to fix this. I recommend a two > stage exception handling scheme. Basically, on the first > exception, you merely note that an exception occurred > and you retry the user copy a byte at a time until you > hit the exact address that fails. You cannot optimize > this to just check a page at a time, because copy_mount_options > wants all the data to be there. For some reason, copy_mount_options() is a continual pain in the ass. It just comes up again and again. For this problem I'd suggest we just rip the copy_from_user() out of there and rewrite the function to use byte-at-a-time get_user()s. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 20:10 ` copy_mount_options() Andrew Morton @ 2004-08-20 21:11 ` David S. Miller 2004-08-20 21:31 ` copy_mount_options() Andrew Morton 0 siblings, 1 reply; 12+ messages in thread From: David S. Miller @ 2004-08-20 21:11 UTC (permalink / raw) To: Andrew Morton; +Cc: linux-arch On Fri, 20 Aug 2004 13:10:53 -0700 Andrew Morton <akpm@osdl.org> wrote: > For some reason, copy_mount_options() is a continual pain in the ass. It > just comes up again and again. > > For this problem I'd suggest we just rip the copy_from_user() out of there > and rewrite the function to use byte-at-a-time get_user()s. I totally agree. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 21:11 ` copy_mount_options() David S. Miller @ 2004-08-20 21:31 ` Andrew Morton 2004-08-20 21:40 ` copy_mount_options() David S. Miller 0 siblings, 1 reply; 12+ messages in thread From: Andrew Morton @ 2004-08-20 21:31 UTC (permalink / raw) To: David S. Miller; +Cc: linux-arch "David S. Miller" <davem@redhat.com> wrote: > > On Fri, 20 Aug 2004 13:10:53 -0700 > Andrew Morton <akpm@osdl.org> wrote: > > > For some reason, copy_mount_options() is a continual pain in the ass. It > > just comes up again and again. > > > > For this problem I'd suggest we just rip the copy_from_user() out of there > > and rewrite the function to use byte-at-a-time get_user()s. > > I totally agree. Something like this? --- 25/fs/namespace.c~copy_mount_options-size-fix Fri Aug 20 14:25:39 2004 +++ 25-akpm/fs/namespace.c Fri Aug 20 14:30:09 2004 @@ -930,7 +930,29 @@ void mark_mounts_for_expiry(struct list_ EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); -int copy_mount_options (const void __user *data, unsigned long *where) +/* + * Some copy_from_user() implementations do not return the exact number of + * bytes remaining to copy on a fault. But copy_mount_options() requires that. + */ + +static long +exact_copy_from_user(void *to, const void __user *from, unsigned long n) +{ + char *t = to; + const char __user *f = from; + char c; + + while (n) { + if (get_user(c, f)) + break; + *t++ = c; + f++; + n--; + } + return n; +} + +int copy_mount_options(const void __user *data, unsigned long *where) { int i; unsigned long page; @@ -952,7 +974,7 @@ int copy_mount_options (const void __use if (size > PAGE_SIZE) size = PAGE_SIZE; - i = size - copy_from_user((void *)page, data, size); + i = size - exact_copy_from_user((void *)page, data, size); if (!i) { free_page(page); return -EFAULT; _ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 21:31 ` copy_mount_options() Andrew Morton @ 2004-08-20 21:40 ` David S. Miller 2004-08-20 22:47 ` copy_mount_options() Andrew Morton 0 siblings, 1 reply; 12+ messages in thread From: David S. Miller @ 2004-08-20 21:40 UTC (permalink / raw) To: Andrew Morton; +Cc: linux-arch On Fri, 20 Aug 2004 14:31:11 -0700 Andrew Morton <akpm@osdl.org> wrote: > Something like this? Looks pretty good. Although I would do a verify_area() above the loop and then use __get_user(). I know this doesn't need to be fast, but it doesn't need to be that slow either :-))) ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 21:40 ` copy_mount_options() David S. Miller @ 2004-08-20 22:47 ` Andrew Morton 2004-08-20 23:18 ` copy_mount_options() Anton Blanchard 0 siblings, 1 reply; 12+ messages in thread From: Andrew Morton @ 2004-08-20 22:47 UTC (permalink / raw) To: David S. Miller; +Cc: linux-arch "David S. Miller" <davem@redhat.com> wrote: > > > Something like this? > > Looks pretty good. Although I would do a verify_area() above > the loop and then use __get_user(). OK, here's #2, with enhanced comment and changelog! Untested though. davem says that copy_mount_options is failing in obscure ways if the architecture's copy_from_user() doesn't return an exact count of the number of uncopied bytes. Fixing that up in each architecture is a pain - it involves falling back to byte-at-a-time copies. It's simple to open-code this in namespace.c. If we find other places in the kernel which care about this we can promote this to a global function. Signed-off-by: Andrew Morton <akpm@osdl.org> --- 25-akpm/fs/namespace.c | 30 ++++++++++++++++++++++++++++-- 1 files changed, 28 insertions(+), 2 deletions(-) diff -puN fs/namespace.c~copy_mount_options-size-fix fs/namespace.c --- 25/fs/namespace.c~copy_mount_options-size-fix Fri Aug 20 15:43:26 2004 +++ 25-akpm/fs/namespace.c Fri Aug 20 15:46:05 2004 @@ -930,7 +930,33 @@ void mark_mounts_for_expiry(struct list_ EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); -int copy_mount_options (const void __user *data, unsigned long *where) +/* + * Some copy_from_user() implementations do not return the exact number of + * bytes remaining to copy on a fault. But copy_mount_options() requires that. + * Note that this function differs from copy_from_user() in that it will oops + * on bad values of `to', rather than returning a short copy. + */ +static long +exact_copy_from_user(void *to, const void __user *from, unsigned long n) +{ + char *t = to; + const char __user *f = from; + char c; + + if (!access_ok(VERIFY_READ, from, n)) + return n; + + while (n) { + if (__get_user(c, f)) + break; + *t++ = c; + f++; + n--; + } + return n; +} + +int copy_mount_options(const void __user *data, unsigned long *where) { int i; unsigned long page; @@ -952,7 +978,7 @@ int copy_mount_options (const void __use if (size > PAGE_SIZE) size = PAGE_SIZE; - i = size - copy_from_user((void *)page, data, size); + i = size - exact_copy_from_user((void *)page, data, size); if (!i) { free_page(page); return -EFAULT; _ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 22:47 ` copy_mount_options() Andrew Morton @ 2004-08-20 23:18 ` Anton Blanchard 2004-08-20 23:51 ` copy_mount_options() David S. Miller 0 siblings, 1 reply; 12+ messages in thread From: Anton Blanchard @ 2004-08-20 23:18 UTC (permalink / raw) To: Andrew Morton; +Cc: David S. Miller, linux-arch, rusty > OK, here's #2, with enhanced comment and changelog! > > Untested though. That removes one more place that uses the return value of copy_to/from_user. Queue Rusty's "copy_from_user is a deathtrap" spiel: http://www.ussg.iu.edu/hypermail/linux/kernel/0205.2/0193.html I too hate that interface, Im continually getting it wrong. It would be nice to remove the possibility of having similar such subtle bugs. Anton > davem says that copy_mount_options is failing in obscure ways if the > architecture's copy_from_user() doesn't return an exact count of the number of > uncopied bytes. > > Fixing that up in each architecture is a pain - it involves falling back to > byte-at-a-time copies. > > It's simple to open-code this in namespace.c. If we find other places in the > kernel which care about this we can promote this to a global function. > > Signed-off-by: Andrew Morton <akpm@osdl.org> > --- > > 25-akpm/fs/namespace.c | 30 ++++++++++++++++++++++++++++-- > 1 files changed, 28 insertions(+), 2 deletions(-) > > diff -puN fs/namespace.c~copy_mount_options-size-fix fs/namespace.c > --- 25/fs/namespace.c~copy_mount_options-size-fix Fri Aug 20 15:43:26 2004 > +++ 25-akpm/fs/namespace.c Fri Aug 20 15:46:05 2004 > @@ -930,7 +930,33 @@ void mark_mounts_for_expiry(struct list_ > > EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); > > -int copy_mount_options (const void __user *data, unsigned long *where) > +/* > + * Some copy_from_user() implementations do not return the exact number of > + * bytes remaining to copy on a fault. But copy_mount_options() requires that. > + * Note that this function differs from copy_from_user() in that it will oops > + * on bad values of `to', rather than returning a short copy. > + */ > +static long > +exact_copy_from_user(void *to, const void __user *from, unsigned long n) > +{ > + char *t = to; > + const char __user *f = from; > + char c; > + > + if (!access_ok(VERIFY_READ, from, n)) > + return n; > + > + while (n) { > + if (__get_user(c, f)) > + break; > + *t++ = c; > + f++; > + n--; > + } > + return n; > +} > + > +int copy_mount_options(const void __user *data, unsigned long *where) > { > int i; > unsigned long page; > @@ -952,7 +978,7 @@ int copy_mount_options (const void __use > if (size > PAGE_SIZE) > size = PAGE_SIZE; > > - i = size - copy_from_user((void *)page, data, size); > + i = size - exact_copy_from_user((void *)page, data, size); > if (!i) { > free_page(page); > return -EFAULT; > _ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 23:18 ` copy_mount_options() Anton Blanchard @ 2004-08-20 23:51 ` David S. Miller 2004-08-21 0:07 ` copy_mount_options() Andrew Morton 0 siblings, 1 reply; 12+ messages in thread From: David S. Miller @ 2004-08-20 23:51 UTC (permalink / raw) To: Anton Blanchard; +Cc: akpm, linux-arch, rusty On Sat, 21 Aug 2004 09:18:33 +1000 Anton Blanchard <anton@samba.org> wrote: > I too hate that interface, Im continually getting it wrong. It would be > nice to remove the possibility of having similar such subtle bugs. I agree. There used to be clever code that would truncate the pipe write and stuff like that, and TCP even used to do something similar at one point. But I go look now and neither of them do that any more. It would even make the kernel smaller because all of these silly: return copy_*_user(...) ? -EFAULT : 0; would just expand to a direct use of the return value. So many arch user copy routines would have enormous amounts of complexity removed, and I saw this quite well when doing the sparc64 stuff I did yesterday. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 23:51 ` copy_mount_options() David S. Miller @ 2004-08-21 0:07 ` Andrew Morton 2004-08-21 7:50 ` copy_mount_options() Rusty Russell 0 siblings, 1 reply; 12+ messages in thread From: Andrew Morton @ 2004-08-21 0:07 UTC (permalink / raw) To: David S. Miller; +Cc: anton, linux-arch, rusty "David S. Miller" <davem@redhat.com> wrote: > > On Sat, 21 Aug 2004 09:18:33 +1000 > Anton Blanchard <anton@samba.org> wrote: > > > I too hate that interface, Im continually getting it wrong. It would be > > nice to remove the possibility of having similar such subtle bugs. > > I agree. > > There used to be clever code that would truncate the pipe write > and stuff like that, and TCP even used to do something similar > at one point. But I go look now and neither of them do that > any more. > > It would even make the kernel smaller because all of these silly: > > return copy_*_user(...) ? -EFAULT : 0; > > would just expand to a direct use of the return value. > > So many arch user copy routines would have enormous amounts > of complexity removed, and I saw this quite well when doing > the sparc64 stuff I did yesterday. I'm all for it. I'll sneak the below patch into -mm, see what breaks. It's a bit weird that the usercopy functions return unsigned long. If they are to return -EFAULT they really should be changed to return an int. --- 25/arch/i386/lib/usercopy.c~usercopy-return-EFAULT Fri Aug 20 17:04:37 2004 +++ 25-akpm/arch/i386/lib/usercopy.c Fri Aug 20 17:05:52 2004 @@ -560,14 +560,14 @@ survive: to += len; n -= len; } - return n; + return n ? -EFAULT : 0; } #endif if (movsl_is_ok(to, from, n)) __copy_user(to, from, n); else n = __copy_user_intel(to, from, n); - return n; + return n ? -EFAULT : 0; } unsigned long @@ -577,7 +577,7 @@ __copy_from_user_ll(void *to, const void __copy_user_zeroing(to, from, n); else n = __copy_user_zeroing_intel(to, from, n); - return n; + return n ? -EFAULT : 0; } /** @@ -599,7 +599,7 @@ copy_to_user(void __user *to, const void might_sleep(); if (access_ok(VERIFY_WRITE, to, n)) n = __copy_to_user(to, from, n); - return n; + return n ? -EFAULT : 0; } EXPORT_SYMBOL(copy_to_user); @@ -627,6 +627,6 @@ copy_from_user(void *to, const void __us n = __copy_from_user(to, from, n); else memset(to, 0, n); - return n; + return n ? -EFAULT : 0; } EXPORT_SYMBOL(copy_from_user); _ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-21 0:07 ` copy_mount_options() Andrew Morton @ 2004-08-21 7:50 ` Rusty Russell 0 siblings, 0 replies; 12+ messages in thread From: Rusty Russell @ 2004-08-21 7:50 UTC (permalink / raw) To: Andrew Morton; +Cc: David S. Miller, Anton Blanchard, linux-arch On Sat, 2004-08-21 at 10:07, Andrew Morton wrote: > I'm all for it. I'll sneak the below patch into -mm, see what breaks. You should zero out the whole buffer in copy_from_user() if you return -EFAULT. We had bugs where people didn't check the returns and you could read random junk. As to who uses the value, generic file read and write still use it, as did some of the serial code last I checked. Linus was of the belief that they should do a short read/write up to the fault boundary rather than return -EFAULT. There's evidence that noone relies on such behaviour, though, since at the time of the debate, doing such a thing would cause an OOPS on ppc. Also, this behaviour silently changed after 2.0 and noone noticed. Finally, other unixes are varied in their approaches in this case (some downright buggy, lying about how much they'd written). I would humbly suggest an additional option which sent a SEGV to the process, as well. If you were playing mprotect games you'd expect them, and if you weren't, you're probably buggy. After a while, we can simply forget about checking returns from copy_to/from_user, which would be a blissful simplification of kernel code. Cheers, Rusty. -- Anyone who quotes me in their signature is an idiot -- Rusty Russell ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 20:01 copy_mount_options() David S. Miller 2004-08-20 20:10 ` copy_mount_options() Andrew Morton @ 2004-08-20 23:15 ` Benjamin Herrenschmidt 2004-08-22 11:50 ` copy_mount_options() Ralf Baechle 1 sibling, 1 reply; 12+ messages in thread From: Benjamin Herrenschmidt @ 2004-08-20 23:15 UTC (permalink / raw) To: David S. Miller; +Cc: Linux Arch list On Sat, 2004-08-21 at 06:01, David S. Miller wrote: > So the sparc64 user copy bug I fixed recently is pretty > much present on every platform. > > Basically, copy_mount_options() requires exact byte granularity > to exception reporting from copy_from_user(). If you don't > do this it can break things like busybox's mount(). Yup, old problem, reported before several times. Another issue that pops up with it and busybox is that copy_mount_options will touch an entire page from the passed pointer. If the pointer you pass is near the end of your data/bss area, you end up touching the area between stack and bss, and potentially cause the stack to grow all the way down to the bss. We have saveguards against that now on ppc, I suppose x86 has too, but it may be worth reminding other archs to have a look. > Even reporting on a word boundary is illegal. On sparc64 > it was quite poignant because we can report on a 64-byte > boundary for large copies because that is the granularity > of the load/store we use. > > Other platforms will need to fix this. I recommend a two > stage exception handling scheme. Basically, on the first > exception, you merely note that an exception occurred > and you retry the user copy a byte at a time until you > hit the exact address that fails. You cannot optimize > this to just check a page at a time, because copy_mount_options > wants all the data to be there. -- Benjamin Herrenschmidt <benh@kernel.crashing.org> ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: copy_mount_options() 2004-08-20 23:15 ` copy_mount_options() Benjamin Herrenschmidt @ 2004-08-22 11:50 ` Ralf Baechle 0 siblings, 0 replies; 12+ messages in thread From: Ralf Baechle @ 2004-08-22 11:50 UTC (permalink / raw) To: Benjamin Herrenschmidt; +Cc: David S. Miller, Linux Arch list On Sat, Aug 21, 2004 at 09:15:46AM +1000, Benjamin Herrenschmidt wrote: > > Basically, copy_mount_options() requires exact byte granularity > > to exception reporting from copy_from_user(). If you don't > > do this it can break things like busybox's mount(). > > Yup, old problem, reported before several times. Another issue > that pops up with it and busybox is that copy_mount_options will > touch an entire page from the passed pointer. If the pointer you > pass is near the end of your data/bss area, you end up touching > the area between stack and bss, and potentially cause the stack > to grow all the way down to the bss. On MIPS it's been triggering a special case in verify_area which make it return -EFAULT if the arguments area includes the last byte of the 2GB userspace as busybox does. A proper fix of verify_area would have inflated the verify_area() code by one instruction so I reduced TASK_SIZE a bit ... Ralf ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2004-08-22 11:50 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2004-08-20 20:01 copy_mount_options() David S. Miller 2004-08-20 20:10 ` copy_mount_options() Andrew Morton 2004-08-20 21:11 ` copy_mount_options() David S. Miller 2004-08-20 21:31 ` copy_mount_options() Andrew Morton 2004-08-20 21:40 ` copy_mount_options() David S. Miller 2004-08-20 22:47 ` copy_mount_options() Andrew Morton 2004-08-20 23:18 ` copy_mount_options() Anton Blanchard 2004-08-20 23:51 ` copy_mount_options() David S. Miller 2004-08-21 0:07 ` copy_mount_options() Andrew Morton 2004-08-21 7:50 ` copy_mount_options() Rusty Russell 2004-08-20 23:15 ` copy_mount_options() Benjamin Herrenschmidt 2004-08-22 11:50 ` copy_mount_options() Ralf Baechle
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox