* [PATCH] compat: Fix RT signal mask corruption via sigprocmask
@ 2012-05-09 22:09 Jan Kiszka
2012-05-09 22:09 ` Jan Kiszka
2012-05-10 0:00 ` Linus Torvalds
0 siblings, 2 replies; 9+ messages in thread
From: Jan Kiszka @ 2012-05-09 22:09 UTC (permalink / raw)
To: Linux Kernel Mailing List, linux-arch
Cc: Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Linus Torvalds,
Andrew Morton, Michael Tokarev, Anthony Liguori, Kevin Wolf
compat_sys_sigprocmask reads a smaller signal mask from userspace than
sigprogmask accepts for setting. So the high word of blocked.sig[0] will
be cleared, releasing any potentially blocked RT signal.
This was discovered via userspace code that relies on get/setcontext.
glibc's i386 versions of those functions use sigprogmask instead of
rt_sigprogmask to save/restore the signal mask and caused RT signal
unblocking this way.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
kernel/compat.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/kernel/compat.c b/kernel/compat.c
index 74ff849..03e491d 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -381,6 +381,8 @@ asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set,
if (set && get_user(s, set))
return -EFAULT;
+ s |= current->blocked.sig[0] &
+ ~((old_sigset_t)(compat_old_sigset_t)-1);
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_sigprocmask(how,
--
1.7.3.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH] compat: Fix RT signal mask corruption via sigprocmask
2012-05-09 22:09 [PATCH] compat: Fix RT signal mask corruption via sigprocmask Jan Kiszka
@ 2012-05-09 22:09 ` Jan Kiszka
2012-05-10 0:00 ` Linus Torvalds
1 sibling, 0 replies; 9+ messages in thread
From: Jan Kiszka @ 2012-05-09 22:09 UTC (permalink / raw)
To: Linux Kernel Mailing List, linux-arch
Cc: Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Linus Torvalds,
Andrew Morton, Michael Tokarev, Anthony Liguori, Kevin Wolf
compat_sys_sigprocmask reads a smaller signal mask from userspace than
sigprogmask accepts for setting. So the high word of blocked.sig[0] will
be cleared, releasing any potentially blocked RT signal.
This was discovered via userspace code that relies on get/setcontext.
glibc's i386 versions of those functions use sigprogmask instead of
rt_sigprogmask to save/restore the signal mask and caused RT signal
unblocking this way.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
kernel/compat.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/kernel/compat.c b/kernel/compat.c
index 74ff849..03e491d 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -381,6 +381,8 @@ asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set,
if (set && get_user(s, set))
return -EFAULT;
+ s |= current->blocked.sig[0] &
+ ~((old_sigset_t)(compat_old_sigset_t)-1);
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_sigprocmask(how,
--
1.7.3.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] compat: Fix RT signal mask corruption via sigprocmask
2012-05-09 22:09 [PATCH] compat: Fix RT signal mask corruption via sigprocmask Jan Kiszka
2012-05-09 22:09 ` Jan Kiszka
@ 2012-05-10 0:00 ` Linus Torvalds
2012-05-10 13:04 ` [PATCH v2] " Jan Kiszka
1 sibling, 1 reply; 9+ messages in thread
From: Linus Torvalds @ 2012-05-10 0:00 UTC (permalink / raw)
To: Jan Kiszka
Cc: Linux Kernel Mailing List, linux-arch, Ingo Molnar,
Thomas Gleixner, H. Peter Anvin, Andrew Morton, Michael Tokarev,
Anthony Liguori, Kevin Wolf
On Wed, May 9, 2012 at 3:09 PM, Jan Kiszka <jan.kiszka@siemens.com> wrote:
> compat_sys_sigprocmask reads a smaller signal mask from userspace than
> sigprogmask accepts for setting. So the high word of blocked.sig[0] will
> be cleared, releasing any potentially blocked RT signal.
I see what you're trying to do, but I think your patch is wrong.
It may work for SIG_BLOCK and SIG_SETMASK to use the current blocked
bits in the high bits, but I'm pretty sure it doesn't work at all for
SIG_UNBLOCK.
I suspect that compat_sys_sigprocmask() should simply be rewritten to
*not* use sys_sigprocmask() at all, but simply open-code the three
cases directly. IOW, copy the code from sys_sigprocmask(), and modify
it for an incoming/outgoing compat_old_sigset_t.
Hmm?
Linus
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] compat: Fix RT signal mask corruption via sigprocmask
2012-05-10 0:00 ` Linus Torvalds
@ 2012-05-10 13:04 ` Jan Kiszka
2012-05-10 13:04 ` Jan Kiszka
2012-05-10 15:44 ` Linus Torvalds
0 siblings, 2 replies; 9+ messages in thread
From: Jan Kiszka @ 2012-05-10 13:04 UTC (permalink / raw)
To: Linus Torvalds, Linux Kernel Mailing List,
linux-arch@vger.kernel.org
Cc: Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
Michael Tokarev, Anthony Liguori, Kevin Wolf
compat_sys_sigprocmask reads a smaller signal mask from userspace than
sigprogmask accepts for setting. So the high word of blocked.sig[0] will
be cleared, releasing any potentially blocked RT signal.
This was discovered via userspace code that relies on get/setcontext.
glibc's i386 versions of those functions use sigprogmask instead of
rt_sigprogmask to save/restore signal mask and caused RT signal
unblocking this way.
As suggested by Linus, this replaces the sys_sigprocmask based compat
version with one that open-codes the required logic, including the merge
of the existing blocked set with the new one provided on SIG_SETMASK.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
kernel/compat.c | 56 ++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/kernel/compat.c b/kernel/compat.c
index 74ff849..39c164e 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -372,25 +372,47 @@ asmlinkage long compat_sys_sigpending(compat_old_sigset_t __user *set)
#ifdef __ARCH_WANT_SYS_SIGPROCMASK
-asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set,
- compat_old_sigset_t __user *oset)
+asmlinkage long compat_sys_sigprocmask(int how,
+ compat_old_sigset_t __user *nset,
+ compat_old_sigset_t __user *oset)
{
- old_sigset_t s;
- long ret;
- mm_segment_t old_fs;
+ old_sigset_t old_set, new_set;
+ sigset_t new_blocked;
- if (set && get_user(s, set))
- return -EFAULT;
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_sigprocmask(how,
- set ? (old_sigset_t __user *) &s : NULL,
- oset ? (old_sigset_t __user *) &s : NULL);
- set_fs(old_fs);
- if (ret == 0)
- if (oset)
- ret = put_user(s, oset);
- return ret;
+ old_set = current->blocked.sig[0];
+
+ if (nset) {
+ if (get_user(new_set, nset))
+ return -EFAULT;
+ new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));
+
+ new_blocked = current->blocked;
+
+ switch (how) {
+ case SIG_BLOCK:
+ sigaddsetmask(&new_blocked, new_set);
+ break;
+ case SIG_UNBLOCK:
+ sigdelsetmask(&new_blocked, new_set);
+ break;
+ case SIG_SETMASK:
+ new_blocked.sig[0] &=
+ ~((old_sigset_t)(compat_old_sigset_t)-1);
+ new_blocked.sig[0] |= new_set;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ set_current_blocked(&new_blocked);
+ }
+
+ if (oset) {
+ if (put_user(old_set, oset))
+ return -EFAULT;
+ }
+
+ return 0;
}
#endif
--
1.7.3.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2] compat: Fix RT signal mask corruption via sigprocmask
2012-05-10 13:04 ` [PATCH v2] " Jan Kiszka
@ 2012-05-10 13:04 ` Jan Kiszka
2012-05-10 15:44 ` Linus Torvalds
1 sibling, 0 replies; 9+ messages in thread
From: Jan Kiszka @ 2012-05-10 13:04 UTC (permalink / raw)
To: Linus Torvalds, Linux Kernel Mailing List,
linux-arch@vger.kernel.org
Cc: Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
Michael Tokarev, Anthony Liguori, Kevin Wolf
compat_sys_sigprocmask reads a smaller signal mask from userspace than
sigprogmask accepts for setting. So the high word of blocked.sig[0] will
be cleared, releasing any potentially blocked RT signal.
This was discovered via userspace code that relies on get/setcontext.
glibc's i386 versions of those functions use sigprogmask instead of
rt_sigprogmask to save/restore signal mask and caused RT signal
unblocking this way.
As suggested by Linus, this replaces the sys_sigprocmask based compat
version with one that open-codes the required logic, including the merge
of the existing blocked set with the new one provided on SIG_SETMASK.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
kernel/compat.c | 56 ++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/kernel/compat.c b/kernel/compat.c
index 74ff849..39c164e 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -372,25 +372,47 @@ asmlinkage long compat_sys_sigpending(compat_old_sigset_t __user *set)
#ifdef __ARCH_WANT_SYS_SIGPROCMASK
-asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t __user *set,
- compat_old_sigset_t __user *oset)
+asmlinkage long compat_sys_sigprocmask(int how,
+ compat_old_sigset_t __user *nset,
+ compat_old_sigset_t __user *oset)
{
- old_sigset_t s;
- long ret;
- mm_segment_t old_fs;
+ old_sigset_t old_set, new_set;
+ sigset_t new_blocked;
- if (set && get_user(s, set))
- return -EFAULT;
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- ret = sys_sigprocmask(how,
- set ? (old_sigset_t __user *) &s : NULL,
- oset ? (old_sigset_t __user *) &s : NULL);
- set_fs(old_fs);
- if (ret == 0)
- if (oset)
- ret = put_user(s, oset);
- return ret;
+ old_set = current->blocked.sig[0];
+
+ if (nset) {
+ if (get_user(new_set, nset))
+ return -EFAULT;
+ new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));
+
+ new_blocked = current->blocked;
+
+ switch (how) {
+ case SIG_BLOCK:
+ sigaddsetmask(&new_blocked, new_set);
+ break;
+ case SIG_UNBLOCK:
+ sigdelsetmask(&new_blocked, new_set);
+ break;
+ case SIG_SETMASK:
+ new_blocked.sig[0] &=
+ ~((old_sigset_t)(compat_old_sigset_t)-1);
+ new_blocked.sig[0] |= new_set;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ set_current_blocked(&new_blocked);
+ }
+
+ if (oset) {
+ if (put_user(old_set, oset))
+ return -EFAULT;
+ }
+
+ return 0;
}
#endif
--
1.7.3.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] compat: Fix RT signal mask corruption via sigprocmask
2012-05-10 13:04 ` [PATCH v2] " Jan Kiszka
2012-05-10 13:04 ` Jan Kiszka
@ 2012-05-10 15:44 ` Linus Torvalds
2012-05-10 15:58 ` Linus Torvalds
1 sibling, 1 reply; 9+ messages in thread
From: Linus Torvalds @ 2012-05-10 15:44 UTC (permalink / raw)
To: Jan Kiszka
Cc: Linux Kernel Mailing List, linux-arch@vger.kernel.org,
Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
Michael Tokarev, Anthony Liguori, Kevin Wolf
On Thu, May 10, 2012 at 6:04 AM, Jan Kiszka <jan.kiszka@siemens.com> wrote:
> + case SIG_BLOCK:
> + sigaddsetmask(&new_blocked, new_set);
> + break;
> + case SIG_UNBLOCK:
> + sigdelsetmask(&new_blocked, new_set);
> + break;
Ok, I think SIG_[UN]BLOCK are now clearly right. However:
> + case SIG_SETMASK:
> + new_blocked.sig[0] &=
> + ~((old_sigset_t)(compat_old_sigset_t)-1);
> + new_blocked.sig[0] |= new_set;
> + break;
I don't think this is clear.
The semantics for the *native* SIG_SETMASK has been to only change the
lower word of the sigset_t.
And that was actually defined in terms of "compat_sigset_word", not
"compat_old_sigset_t".
Now, they are both generally the same, and so I think your code does
the right thing, but I have to say that I really had to look closely
to make sure that yes, your code was right.
Anyway, my *gut* feel is that it would be much clearer to write the above as
compat_sigset_word x = new_set;
memcpy(new_blocked.sig, &x, sizeof(x));
together with a comment to the effect that sigprocmask(SIG_SETMASK..)
only changes the first word of the structure.
That said, I think your patch does look technically correct, so maybe
it's just me who thinks it is very non-obvious and hard to read.
The memcpy approach will also generate better code. This is the "mask-and-set":
movabsq $-4294967296, %rax #, tmp89
andq -32(%rbp), %rax # new_blocked.sig, tmp89
orq %rdx, %rax # new_set, tmp89
movq %rax, -32(%rbp) # tmp89, new_blocked.sig
and this is the memcpy:
movl %edx, -32(%rbp) # new_set,
ie it is done as a simple 32-bit store.
I think I'll just edit your patch directly, no need to send me a new version.
Linus
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] compat: Fix RT signal mask corruption via sigprocmask
2012-05-10 15:44 ` Linus Torvalds
@ 2012-05-10 15:58 ` Linus Torvalds
2012-05-10 17:38 ` Jan Kiszka
2012-05-10 18:51 ` Michael Tokarev
0 siblings, 2 replies; 9+ messages in thread
From: Linus Torvalds @ 2012-05-10 15:58 UTC (permalink / raw)
To: Jan Kiszka
Cc: Linux Kernel Mailing List, linux-arch@vger.kernel.org,
Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
Michael Tokarev, Anthony Liguori, Kevin Wolf
[-- Attachment #1: Type: text/plain, Size: 539 bytes --]
On Thu, May 10, 2012 at 8:44 AM, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> I think I'll just edit your patch directly, no need to send me a new version.
Ok, just FYI, this is the incremental diff I ended up with. I'll
commit it as this, but I'm sending it out for people to verify before
I do. Although just from looking at the code generation, it really
does just change those four instructions that do a mask to a single
'movl', so I think it's pretty safe even though I haven't tested it.
Linus
[-- Attachment #2: patch.diff --]
[-- Type: application/octet-stream, Size: 1097 bytes --]
kernel/compat.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/kernel/compat.c b/kernel/compat.c
index 39c164e02d05..d2c67aa49ae6 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -372,6 +372,15 @@ asmlinkage long compat_sys_sigpending(compat_old_sigset_t __user *set)
#ifdef __ARCH_WANT_SYS_SIGPROCMASK
+/*
+ * sys_sigprocmask SIG_SETMASK sets the first (compat) word of the
+ * blocked set of signals to the supplied signal set
+ */
+static inline void compat_sig_setmask(sigset_t *blocked, compat_sigset_word set)
+{
+ memcpy(blocked->sig, &set, sizeof(set));
+}
+
asmlinkage long compat_sys_sigprocmask(int how,
compat_old_sigset_t __user *nset,
compat_old_sigset_t __user *oset)
@@ -396,9 +405,7 @@ asmlinkage long compat_sys_sigprocmask(int how,
sigdelsetmask(&new_blocked, new_set);
break;
case SIG_SETMASK:
- new_blocked.sig[0] &=
- ~((old_sigset_t)(compat_old_sigset_t)-1);
- new_blocked.sig[0] |= new_set;
+ compat_sig_setmask(&new_blocked, new_set);
break;
default:
return -EINVAL;
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] compat: Fix RT signal mask corruption via sigprocmask
2012-05-10 15:58 ` Linus Torvalds
@ 2012-05-10 17:38 ` Jan Kiszka
2012-05-10 18:51 ` Michael Tokarev
1 sibling, 0 replies; 9+ messages in thread
From: Jan Kiszka @ 2012-05-10 17:38 UTC (permalink / raw)
To: Linus Torvalds
Cc: Linux Kernel Mailing List, linux-arch@vger.kernel.org,
Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
Michael Tokarev, Anthony Liguori, Kevin Wolf
On 2012-05-10 12:58, Linus Torvalds wrote:
> On Thu, May 10, 2012 at 8:44 AM, Linus Torvalds
> <torvalds@linux-foundation.org> wrote:
>>
>> I think I'll just edit your patch directly, no need to send me a new version.
>
> Ok, just FYI, this is the incremental diff I ended up with. I'll
> commit it as this, but I'm sending it out for people to verify before
> I do. Although just from looking at the code generation, it really
> does just change those four instructions that do a mask to a single
> 'movl', so I think it's pretty safe even though I haven't tested it.
I did, and it looks and works fine.
Thanks,
Jan
--
Siemens AG, Corporate Technology, CT T DE IT 1
Corporate Competence Center Embedded Linux
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] compat: Fix RT signal mask corruption via sigprocmask
2012-05-10 15:58 ` Linus Torvalds
2012-05-10 17:38 ` Jan Kiszka
@ 2012-05-10 18:51 ` Michael Tokarev
1 sibling, 0 replies; 9+ messages in thread
From: Michael Tokarev @ 2012-05-10 18:51 UTC (permalink / raw)
To: Linus Torvalds
Cc: Jan Kiszka, Linux Kernel Mailing List, linux-arch@vger.kernel.org,
Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
Anthony Liguori, Kevin Wolf
On 10.05.2012 19:58, Linus Torvalds wrote:
> On Thu, May 10, 2012 at 8:44 AM, Linus Torvalds
> <torvalds@linux-foundation.org> wrote:
>>
>> I think I'll just edit your patch directly, no need to send me a new version.
>
> Ok, just FYI, this is the incremental diff I ended up with. I'll
> commit it as this, but I'm sending it out for people to verify before
> I do. Although just from looking at the code generation, it really
> does just change those four instructions that do a mask to a single
> 'movl', so I think it's pretty safe even though I haven't tested it.
I tested the changes (from Jan and Linus's patches) and
the result a) looks sane, and b) actually works and fixes
our long-standing issues.
So you can add my
Tested-By: Michael Tokarev <mjt@tls.msk.ru>
or, if you prefer,
Signed-off-By: Michael Tokarev <mjt@tls.msk.ru>
for the combined patch.
Thank you for the excellent catch Jan!
/mjt
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-05-10 18:51 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-05-09 22:09 [PATCH] compat: Fix RT signal mask corruption via sigprocmask Jan Kiszka
2012-05-09 22:09 ` Jan Kiszka
2012-05-10 0:00 ` Linus Torvalds
2012-05-10 13:04 ` [PATCH v2] " Jan Kiszka
2012-05-10 13:04 ` Jan Kiszka
2012-05-10 15:44 ` Linus Torvalds
2012-05-10 15:58 ` Linus Torvalds
2012-05-10 17:38 ` Jan Kiszka
2012-05-10 18:51 ` Michael Tokarev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox