public inbox for linux-arch@vger.kernel.org
 help / color / mirror / Atom feed
* [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