* [PATCH] information leak in sigaltstack @ 2009-07-31 19:48 Ulrich Drepper 2009-07-31 21:15 ` Linus Torvalds 0 siblings, 1 reply; 8+ messages in thread From: Ulrich Drepper @ 2009-07-31 19:48 UTC (permalink / raw) To: linux-kernel; +Cc: akpm, torvalds Unfortunately the stack_t data structure was defined before people cared much about 64-bit architectures. It has a hole in the middle. And this hole is creating an information leak in the sigaltstack syscall. When the second parameter to sigaltstack is non-NULL the current stack information is passed to the caller by filling in the members of the stack_t structure and then the whole structure is copied using copy_to_user. This unfortunately leaves the whole after flags uninitialized. The following patch should fix the issue. Signed-off-by: Ulrich Drepper <drepper@redhat.com> diff --git a/kernel/signal.c b/kernel/signal.c index ccf1cee..612d6b7 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2455,6 +2455,9 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s int error; if (uoss) { + if (offsetof(stack_t, ss_flags) + sizeof(oss.ss_flags) != + offsetof(stack_t, ss_size)) + memset(&oss, '\0', sizeof(oss)); oss.ss_sp = (void __user *) current->sas_ss_sp; oss.ss_size = current->sas_ss_size; oss.ss_flags = sas_ss_flags(sp); ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-07-31 19:48 [PATCH] information leak in sigaltstack Ulrich Drepper @ 2009-07-31 21:15 ` Linus Torvalds 2009-07-31 21:31 ` Linus Torvalds 0 siblings, 1 reply; 8+ messages in thread From: Linus Torvalds @ 2009-07-31 21:15 UTC (permalink / raw) To: Ulrich Drepper; +Cc: linux-kernel, akpm On Fri, 31 Jul 2009, Ulrich Drepper wrote: > > The following patch should fix the issue. Hmm. Is there any reason not to do an unconditional memset(), and then expect gcc to avoid the unnecessary stores? I realize gcc may not do that, but we could always _hope_. Also, is there really any reason to believe that the only hole can be after ss_flags, and that it's only the case when ss_flags is in the middle? Quite frankly, as far as I can tell, you could have an "int ss_flags" at the _end_ of the structure too, and have the same issue (padding out to the alignment of the struct). For an example of that "'int ss_flags' at the end" look at MIPS. Now, you'd end up with a memset() in that case (since it certainly won't match the offsetof), but my point is, the conditional really looks very arbitrary and rather strange. I'd rather see it unconditional, even if it costs three unnecessary writes or whatever. Linus ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-07-31 21:15 ` Linus Torvalds @ 2009-07-31 21:31 ` Linus Torvalds 2009-07-31 21:37 ` Ulrich Drepper 0 siblings, 1 reply; 8+ messages in thread From: Linus Torvalds @ 2009-07-31 21:31 UTC (permalink / raw) To: Ulrich Drepper; +Cc: linux-kernel, akpm On Fri, 31 Jul 2009, Linus Torvalds wrote: > > Now, you'd end up with a memset() in that case (since it certainly won't > match the offsetof), but my point is, the conditional really looks very > arbitrary and rather strange. I'd rather see it unconditional, even if it > costs three unnecessary writes or whatever. .. and if we really do want the conditional, maybe just make it something like /* * ss_flags is often generally 'int', and may cause * holes in the structure due to alignment. */ if (alignof(oss.ss_flags) != alignof(oss)) memset(&oss, 0, sizeof(oss)); instead? That would seem to be less subtle, and more to-the-point. Linus ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-07-31 21:31 ` Linus Torvalds @ 2009-07-31 21:37 ` Ulrich Drepper 2009-08-01 0:28 ` Linus Torvalds 0 siblings, 1 reply; 8+ messages in thread From: Ulrich Drepper @ 2009-07-31 21:37 UTC (permalink / raw) To: Linus Torvalds; +Cc: linux-kernel, akpm -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Linus Torvalds wrote: > if (alignof(oss.ss_flags) != alignof(oss)) > memset(&oss, 0, sizeof(oss)); > > instead? That would seem to be less subtle, and more to-the-point. I was just composing a reply with basically this. So you'll apply this and don't wait for me to send a new version of the patch, right? - -- ➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkpzZBwACgkQ2ijCOnn/RHSMUACeJkKaHTG97OcSkCa3jFCIFxvL 9JsAoK7IuTCiW+i1oR9Xlt3wo0YZPTCw =9A0Y -----END PGP SIGNATURE----- ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-07-31 21:37 ` Ulrich Drepper @ 2009-08-01 0:28 ` Linus Torvalds 2009-08-01 7:22 ` Jakub Jelinek 2009-08-01 17:52 ` Linus Torvalds 0 siblings, 2 replies; 8+ messages in thread From: Linus Torvalds @ 2009-08-01 0:28 UTC (permalink / raw) To: Ulrich Drepper; +Cc: Linux Kernel Mailing List, Andrew Morton, Jakub Jelinek [ Cc'ing jakub, since that code generation looks crappy, and I think he has worked on gcc memset(). I wonder if it's because we use -Os, and gcc tries to avoid one REX prefix on the 'stosq'. I also wonder why gcc doesn't just notice that it should really only initialize a single 4-byte word (no rep, no prefix, no nothing, just a single "movl $0,44(%ebp)") - so even with the -Os, that is just wrong, and it would have been better to do as multiple stores and then noticing that most of them end up dead ] On Fri, 31 Jul 2009, Ulrich Drepper wrote: > > I was just composing a reply with basically this. So you'll apply this > and don't wait for me to send a new version of the patch, right? Grr. Gcc creates truly crap code for this trivial 24-byte memset. Why does it do that? gcc knows the alignment is 8 bytes, but it still uses 6 4-byte stores instead of 3 8-byte ones. And it does it with this: xorl %eax, %eax # tmp88 leaq -48(%rbp), %rsi #, tmp86 movl $6, %ecx #, tmp89 movq %rsi, %rdi # tmp86, tmp87 rep stosl which is just incredibly lame in so many ways. And it doesn't optimize anything away, even though the next lines will then re-initialize 20 of the 24 bytes. Now, maybe this isn't performance-critical, but it just makes me feel that there has to be a better way to make gcc DTRT. Here's the patch I used, just for posterity. I can't decide if I really want to commit this crap. But at least on 32-bit architectures the "alignof" testing should remove the horrid code. I do wonder why gcc thinks that 32-bit writes are a good idea in this case, though. Linus --- kernel/signal.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index ccf1cee..b990dc8 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2455,6 +2455,9 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s int error; if (uoss) { + /* Fill cracks around 'ss_flags' */ + if (__alignof__(oss.ss_flags) != __alignof__(oss)) + memset(&oss, 0, sizeof(oss)); oss.ss_sp = (void __user *) current->sas_ss_sp; oss.ss_size = current->sas_ss_size; oss.ss_flags = sas_ss_flags(sp); ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-08-01 0:28 ` Linus Torvalds @ 2009-08-01 7:22 ` Jakub Jelinek 2009-08-01 16:13 ` Linus Torvalds 2009-08-01 17:52 ` Linus Torvalds 1 sibling, 1 reply; 8+ messages in thread From: Jakub Jelinek @ 2009-08-01 7:22 UTC (permalink / raw) To: Linus Torvalds Cc: Ulrich Drepper, Linux Kernel Mailing List, Andrew Morton, Richard Guenther On Fri, Jul 31, 2009 at 05:28:40PM -0700, Linus Torvalds wrote: > > [ Cc'ing jakub, since that code generation looks crappy, and I think he > has worked on gcc memset(). I wonder if it's because we use -Os, and gcc > tries to avoid one REX prefix on the 'stosq'. Assuming we are talking about: typedef __SIZE_TYPE__ size_t; typedef struct sigaltstack { void *ss_sp; int ss_flags; size_t ss_size; } stack_t; void foo (stack_t *oss, void *sp) { __builtin_memset (oss, 0, sizeof (*oss)); oss->ss_sp = sp; oss->ss_flags = 6; oss->ss_size = 2; } yes, it is because of -Os, rep stosq is longer than rep stosl. For -O2 it generates: movq $0, 8(%rdi) movq %rsi, (%rdi) movl $6, 8(%rdi) movq $2, 16(%rdi) which still isn't perfect, but is much better. GCC 4.4 newly has RTL DCE improvements, so that at least the memcpy is optimized away if the structure is filled completely after the memset, or is able to optimize away NULL/0 assignments after a memset to 0. At -O2 when GCC decides to do the memset piecewise it is easier to kill dead stores from the memset (the -O2 code perhaps could be improved by the http://gcc.gnu.org/PR22141 patch which hasn't been committed because it didn't show visible improvements and on power6 even degraded performance). At -Os when GCC decides during the expand to use arch specific pattern for the memset it would be much harder to handle it at the RTL level. So the above should be ideally optimized already at the tree level. > diff --git a/kernel/signal.c b/kernel/signal.c > index ccf1cee..b990dc8 100644 > --- a/kernel/signal.c > +++ b/kernel/signal.c > @@ -2455,6 +2455,9 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s > int error; > > if (uoss) { > + /* Fill cracks around 'ss_flags' */ > + if (__alignof__(oss.ss_flags) != __alignof__(oss)) > + memset(&oss, 0, sizeof(oss)); > oss.ss_sp = (void __user *) current->sas_ss_sp; > oss.ss_size = current->sas_ss_size; > oss.ss_flags = sas_ss_flags(sp); I'd say the test you want to do is if (sizeof (oss.ss_sp) + sizeof (oss.ss_size) + sizeof (oss.ss_flags) != sizeof oss) memset (&oss, 0, sizeof oss); (i.e. check whether the struct has any padding in it or not). Jakub ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-08-01 7:22 ` Jakub Jelinek @ 2009-08-01 16:13 ` Linus Torvalds 0 siblings, 0 replies; 8+ messages in thread From: Linus Torvalds @ 2009-08-01 16:13 UTC (permalink / raw) To: Jakub Jelinek Cc: Ulrich Drepper, Linux Kernel Mailing List, Andrew Morton, Richard Guenther On Sat, 1 Aug 2009, Jakub Jelinek wrote: > > yes, it is because of -Os, rep stosq is longer than rep stosl. For -O2 it > generates: > movq $0, 8(%rdi) > movq %rsi, (%rdi) > movl $6, 8(%rdi) > movq $2, 16(%rdi) Ok, so -Os actually generates _larger_ code due to this silly one-byte micro-optimization that then disables other optimizations. I suspect that the "rep stosl" choice is _particularly_ bad, since doing a 32-bit write actually is much more likely to cause subsequent stalls on the store buffer if any of the latter accesses are 64-bit reads. It tends to be much better to create larger stores (and equal or smaller loads) if there are any overlapping accesses. I think a lot of micro-architectures will stall if they try to do a read that crosses multiple stores in the store buffer. If the load hits in the store buffer and is entirely contained within one store, you have a much better chance of just bypassing the cache entirely. > which still isn't perfect, but is much better. Yes. It would have been nice if gcc optimized the overlapping accesses too, but it's already much better. > At -O2 when GCC decides to do the memset piecewise it is easier to kill > dead stores from the memset On 32-bit, we do memcpy() by hand because gcc is/was so bad at this (we do it for memset too, but only for really small areas, so it wouldn't trigger). On x86-64, we've trusted that gcc is better. Sadly, it's clearly not very good. I bet that for anything that is just a couple of stores (three, in this case), you'd almost always be better off using regular stores. The "rep stosl" may be small, but the register constraints and the inability to combine memory accesses are a big downer. > At -Os when GCC decides during the expand to use arch specific pattern > for the memset it would be much harder to handle it at the RTL level. > So the above should be ideally optimized already at the tree level. Yes. Or perhaps by just saying that for -Os you'd still expand it for a really _small_ number of accesses (just make it smaller than for -O2). > I'd say the test you want to do is > if (sizeof (oss.ss_sp) + sizeof (oss.ss_size) + sizeof (oss.ss_flags) > != sizeof oss) > memset (&oss, 0, sizeof oss); > (i.e. check whether the struct has any padding in it or not). Yeah, that sounds logical. I'd still like to figure out how to get gcc to generate better code in this case, though. Explicit padding bytes would do it, of course, but then we're talking changs to every single 64-bit architecture. Grr. Linus ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] information leak in sigaltstack 2009-08-01 0:28 ` Linus Torvalds 2009-08-01 7:22 ` Jakub Jelinek @ 2009-08-01 17:52 ` Linus Torvalds 1 sibling, 0 replies; 8+ messages in thread From: Linus Torvalds @ 2009-08-01 17:52 UTC (permalink / raw) To: Ulrich Drepper; +Cc: Linux Kernel Mailing List, Andrew Morton, Jakub Jelinek On Fri, 31 Jul 2009, Linus Torvalds wrote: > > Here's the patch I used, just for posterity. I can't decide if I really > want to commit this crap. I think I like this version better. It's not wonderfully pretty, but to some degree it actually improves on code generation, even if it generates a bigger exception table. It now also copies the stack_t back to user space the same way it copies it _from_ user space, so in that sense it's actually very consistent. Linus --- From: Linus Torvalds <torvalds@linux-foundation.org> Date: Sat, 1 Aug 2009 10:34:56 -0700 Subject: [PATCH] do_sigaltstack: avoid copying 'stack_t' as a structure to user space Ulrich Drepper correctly points out that there is generally padding in the structure on 64-bit hosts, and that copying the structure from kernel to user space can leak information from the kernel stack in those padding bytes. Avoid the whole issue by just copying the three members one by one instead, which also means that the function also can avoid the need for a stack frame. This also happens to match how we copy the new structure from user space, so it all even makes sense. [ The obvious solution of adding a memset() generates horrid code, gcc does really stupid things. ] Reported-by: Ulrich Drepper <drepper@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- kernel/signal.c | 15 ++++++++------- 1 files changed, 8 insertions(+), 7 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index ccf1cee..f268372 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2454,11 +2454,9 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s stack_t oss; int error; - if (uoss) { - oss.ss_sp = (void __user *) current->sas_ss_sp; - oss.ss_size = current->sas_ss_size; - oss.ss_flags = sas_ss_flags(sp); - } + oss.ss_sp = (void __user *) current->sas_ss_sp; + oss.ss_size = current->sas_ss_size; + oss.ss_flags = sas_ss_flags(sp); if (uss) { void __user *ss_sp; @@ -2501,13 +2499,16 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s current->sas_ss_size = ss_size; } + error = 0; if (uoss) { error = -EFAULT; - if (copy_to_user(uoss, &oss, sizeof(oss))) + if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss))) goto out; + error = __put_user(oss.ss_sp, &uoss->ss_sp) | + __put_user(oss.ss_size, &uoss->ss_size) | + __put_user(oss.ss_flags, &uoss->ss_flags); } - error = 0; out: return error; } ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2009-08-01 17:52 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-07-31 19:48 [PATCH] information leak in sigaltstack Ulrich Drepper 2009-07-31 21:15 ` Linus Torvalds 2009-07-31 21:31 ` Linus Torvalds 2009-07-31 21:37 ` Ulrich Drepper 2009-08-01 0:28 ` Linus Torvalds 2009-08-01 7:22 ` Jakub Jelinek 2009-08-01 16:13 ` Linus Torvalds 2009-08-01 17:52 ` Linus Torvalds
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.