diff mbox

[RFC/PATCH,3/3] x86/signal/64: Add explicit controls for sigcontext SS handling

Message ID ea81981de89c22262e7b9324144f0055cff8b955.1439496828.git.luto@kernel.org (mailing list archive)
State New, archived
Headers show

Commit Message

Andy Lutomirski Aug. 13, 2015, 8:18 p.m. UTC
This adds two new uc_flags flags.  UC_SAVED_SS will be set for all
64-bit signals (including x32).  It indicates that the saved SS field
is valid and that the kernel understands UC_RESTORE_SS.

The kernel will *not* set UC_RESTORE_SS.  User signal handlers can
set UC_RESTORE_SS themselves to indicate that sigreturn should
restore SS from the sigcontext.

64-bit programs that use segmentation are encouraged to check
UC_SAVED_SS and set UC_RESTORE_SS in their signal handlers.  This is
the only straightforward way to cause sigreturn to restore SS.  (The
only non-test program that I know of that uses segmentation in a
64-bit binary is DOSEMU, and DOSEMU currently uses a nasty
trampoline to work around the lack of this mechanism in old kernels.
It could detect UC_RESTORE_SS and use it to avoid needing a
trampoline.

Cc: Stas Sergeev <stsp@list.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
---
 arch/x86/include/asm/sighandling.h      |  1 -
 arch/x86/include/uapi/asm/ucontext.h    | 26 +++++++++++++++++----
 arch/x86/kernel/signal.c                | 41 ++++++++++++++++++++++++++-------
 tools/testing/selftests/x86/sigreturn.c | 26 +++++++++++++++++++++
 4 files changed, 80 insertions(+), 14 deletions(-)

Comments

Cyrill Gorcunov Aug. 14, 2015, 8:55 p.m. UTC | #1
On Thu, Aug 13, 2015 at 01:18:50PM -0700, Andy Lutomirski wrote:
> This adds two new uc_flags flags.  UC_SAVED_SS will be set for all
> 64-bit signals (including x32).  It indicates that the saved SS field
> is valid and that the kernel understands UC_RESTORE_SS.
> 
> The kernel will *not* set UC_RESTORE_SS.  User signal handlers can
> set UC_RESTORE_SS themselves to indicate that sigreturn should
> restore SS from the sigcontext.
> 
> 64-bit programs that use segmentation are encouraged to check
> UC_SAVED_SS and set UC_RESTORE_SS in their signal handlers.  This is
> the only straightforward way to cause sigreturn to restore SS.  (The
> only non-test program that I know of that uses segmentation in a
> 64-bit binary is DOSEMU, and DOSEMU currently uses a nasty
> trampoline to work around the lack of this mechanism in old kernels.
> It could detect UC_RESTORE_SS and use it to avoid needing a
> trampoline.
> 
> Cc: Stas Sergeev <stsp@list.ru>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Cyrill Gorcunov <gorcunov@gmail.com>
> Cc: Pavel Emelyanov <xemul@parallels.com>
> Signed-off-by: Andy Lutomirski <luto@kernel.org>

Looks reasonable to me. Andy, Linus, what the final conclusion --
are we about to introduce this flag or simply continue with
revert? Should I test this one? (from the code I don't excpect it
break criu anyhow but still).
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Lutomirski Aug. 14, 2015, 8:57 p.m. UTC | #2
On Fri, Aug 14, 2015 at 1:55 PM, Cyrill Gorcunov <gorcunov@gmail.com> wrote:
> On Thu, Aug 13, 2015 at 01:18:50PM -0700, Andy Lutomirski wrote:
>> This adds two new uc_flags flags.  UC_SAVED_SS will be set for all
>> 64-bit signals (including x32).  It indicates that the saved SS field
>> is valid and that the kernel understands UC_RESTORE_SS.
>>
>> The kernel will *not* set UC_RESTORE_SS.  User signal handlers can
>> set UC_RESTORE_SS themselves to indicate that sigreturn should
>> restore SS from the sigcontext.
>>
>> 64-bit programs that use segmentation are encouraged to check
>> UC_SAVED_SS and set UC_RESTORE_SS in their signal handlers.  This is
>> the only straightforward way to cause sigreturn to restore SS.  (The
>> only non-test program that I know of that uses segmentation in a
>> 64-bit binary is DOSEMU, and DOSEMU currently uses a nasty
>> trampoline to work around the lack of this mechanism in old kernels.
>> It could detect UC_RESTORE_SS and use it to avoid needing a
>> trampoline.
>>
>> Cc: Stas Sergeev <stsp@list.ru>
>> Cc: Linus Torvalds <torvalds@linux-foundation.org>
>> Cc: Cyrill Gorcunov <gorcunov@gmail.com>
>> Cc: Pavel Emelyanov <xemul@parallels.com>
>> Signed-off-by: Andy Lutomirski <luto@kernel.org>
>
> Looks reasonable to me. Andy, Linus, what the final conclusion --
> are we about to introduce this flag or simply continue with
> revert? Should I test this one? (from the code I don't excpect it
> break criu anyhow but still).

Don't bother testing yet.  I'm waffling between trying something like
this and adding SA_SAVE_SS.  I have partially written patches for the
latter.

--Andy
Cyrill Gorcunov Aug. 14, 2015, 9:05 p.m. UTC | #3
On Fri, Aug 14, 2015 at 01:57:42PM -0700, Andy Lutomirski wrote:
> 
> Don't bother testing yet.  I'm waffling between trying something like
> this and adding SA_SAVE_SS.  I have partially written patches for the
> latter.

ok, ping me if anything
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/x86/include/asm/sighandling.h b/arch/x86/include/asm/sighandling.h
index 89db46752a8f..452c88b8ad06 100644
--- a/arch/x86/include/asm/sighandling.h
+++ b/arch/x86/include/asm/sighandling.h
@@ -13,7 +13,6 @@ 
 			 X86_EFLAGS_CF | X86_EFLAGS_RF)
 
 void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
-int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc);
 int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
 		     struct pt_regs *regs, unsigned long mask);
 
diff --git a/arch/x86/include/uapi/asm/ucontext.h b/arch/x86/include/uapi/asm/ucontext.h
index b7c29c8017f2..964bc3b46ff3 100644
--- a/arch/x86/include/uapi/asm/ucontext.h
+++ b/arch/x86/include/uapi/asm/ucontext.h
@@ -1,11 +1,27 @@ 
 #ifndef _ASM_X86_UCONTEXT_H
 #define _ASM_X86_UCONTEXT_H
 
-#define UC_FP_XSTATE	0x1	/* indicates the presence of extended state
-				 * information in the memory layout pointed
-				 * by the fpstate pointer in the ucontext's
-				 * sigcontext struct (uc_mcontext).
-				 */
+/*
+ * indicates the presence of extended state
+ * information in the memory layout pointed
+ * by the fpstate pointer in the ucontext's
+ * sigcontext struct (uc_mcontext).
+ */
+#define UC_FP_XSTATE	0x1
+
+#ifdef __x86_64__
+/*
+ * UC_SAVED_SS will be set when delivering 64-bit or x32 signals on
+ * kernels that save SS in the sigcontext.  Kernels that set UC_SAVED_SS
+ * allow signal handlers to set UC_RESTORE_SS; if UC_RESTORE_SS is set,
+ * then sigreturn will restore SS.
+ *
+ * For compatibility with old programs, the kernel will *not* set
+ * UC_RESTORE_SS when delivering signals.
+ */
+#define UC_SAVED_SS	0x2
+#define UC_RESTORE_SS	0x4
+#endif
 
 #include <asm-generic/ucontext.h>
 
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 784af1e49fc1..746250e9bce1 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -61,7 +61,9 @@ 
 	regs->seg = GET_SEG(seg) | 3;			\
 } while (0)
 
-int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+static int restore_sigcontext(struct pt_regs *regs,
+			      struct sigcontext __user *sc,
+			      unsigned long uc_flags)
 {
 	void __user *buf;
 	unsigned int tmpflags;
@@ -94,7 +96,19 @@  int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
 #endif /* CONFIG_X86_64 */
 
 		COPY_SEG_CPL3(cs);
+
+#ifdef CONFIG_X86_64
+		/*
+		 * For the 64-bit ABI, we only restore SS if UC_RESTORE_SS
+		 * is set.  Otherwise we rely on the fact that regs->ss
+		 * is already set to __USER_DS by the SYSCALL entry code.
+		 */
+		if (uc_flags & UC_RESTORE_SS)
+			COPY_SEG_CPL3(ss);
+#else
+		/* For the 32-bit ABI, we always restore SS. */
 		COPY_SEG_CPL3(ss);
+#endif
 
 		get_user_ex(tmpflags, &sc->flags);
 		regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
@@ -336,6 +350,7 @@  static int __setup_rt_frame(int sig, struct ksignal *ksig,
 	void __user *restorer;
 	int err = 0;
 	void __user *fpstate = NULL;
+	unsigned long flags = 0;
 
 	frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate);
 
@@ -349,9 +364,12 @@  static int __setup_rt_frame(int sig, struct ksignal *ksig,
 
 		/* Create the ucontext.  */
 		if (cpu_has_xsave)
-			put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
-		else
-			put_user_ex(0, &frame->uc.uc_flags);
+			flags = UC_FP_XSTATE;
+#ifdef CONFIG_X86_64
+		flags |= UC_SAVED_SS;
+#endif
+		put_user_ex(flags, &frame->uc.uc_flags);
+
 		put_user_ex(0, &frame->uc.uc_link);
 		save_altstack_ex(&frame->uc.uc_stack, regs->sp);
 
@@ -517,9 +535,10 @@  static int x32_setup_rt_frame(struct ksignal *ksig,
 	put_user_try {
 		/* Create the ucontext.  */
 		if (cpu_has_xsave)
-			put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
+			put_user_ex(UC_FP_XSTATE | UC_SAVED_SS,
+				    &frame->uc.uc_flags);
 		else
-			put_user_ex(0, &frame->uc.uc_flags);
+			put_user_ex(UC_SAVED_SS, &frame->uc.uc_flags);
 		put_user_ex(0, &frame->uc.uc_link);
 		compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp);
 		put_user_ex(0, &frame->uc.uc__pad0);
@@ -597,16 +616,19 @@  asmlinkage long sys_rt_sigreturn(void)
 	struct pt_regs *regs = current_pt_regs();
 	struct rt_sigframe __user *frame;
 	sigset_t set;
+	unsigned long uc_flags;
 
 	frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
 	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
 		goto badframe;
 	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
 		goto badframe;
+	if (__get_user(uc_flags, &frame->uc.uc_flags))
+		goto badframe;
 
 	set_current_blocked(&set);
 
-	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+	if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
 		goto badframe;
 
 	if (restore_altstack(&frame->uc.uc_stack))
@@ -810,6 +832,7 @@  asmlinkage long sys32_x32_rt_sigreturn(void)
 	struct pt_regs *regs = current_pt_regs();
 	struct rt_sigframe_x32 __user *frame;
 	sigset_t set;
+	unsigned long uc_flags;
 
 	frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8);
 
@@ -817,10 +840,12 @@  asmlinkage long sys32_x32_rt_sigreturn(void)
 		goto badframe;
 	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
 		goto badframe;
+	if (__get_user(uc_flags, &frame->uc.uc_flags))
+		goto badframe;
 
 	set_current_blocked(&set);
 
-	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+	if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
 		goto badframe;
 
 	if (compat_restore_altstack(&frame->uc.uc_stack))
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c
index b5aa1bab7416..8f0176b91854 100644
--- a/tools/testing/selftests/x86/sigreturn.c
+++ b/tools/testing/selftests/x86/sigreturn.c
@@ -55,6 +55,24 @@ 
 #include <sys/user.h>
 
 /*
+ * Copies from asm/ucontext.h, as asm/ucontext.h conflicts badly with the glibc
+ * headers.
+ */
+#ifdef __x86_64__
+/*
+ * UC_SAVED_SS will be set when delivering 64-bit or x32 signals on
+ * kernels that save SS in the sigcontext.  Kernels that set UC_SAVED_SS
+ * allow signal handlers to set UC_RESTORE_SS; if UC_RESTORE_SS is set,
+ * then sigreturn will restore SS.
+ *
+ * For compatibility with old programs, the kernel will *not* set
+ * UC_RESTORE_SS when delivering signals.
+ */
+#define UC_SAVED_SS	0x2
+#define UC_RESTORE_SS	0x4
+#endif
+
+/*
  * In principle, this test can run on Linux emulation layers (e.g.
  * Illumos "LX branded zones").  Solaris-based kernels reserve LDT
  * entries 0-5 for their own internal purposes, so start our LDT
@@ -330,6 +348,10 @@  static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
 	memcpy(&requested_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
 	requested_regs[REG_AX] = *ssptr(ctx);	/* The asm code does this. */
 
+#ifdef __x86_64__
+	ctx->uc_flags |= UC_RESTORE_SS;
+#endif
+
 	return;
 }
 
@@ -358,6 +380,10 @@  static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
 	memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
 	memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
 
+#ifdef __x86_64__
+	ctx->uc_flags |= UC_RESTORE_SS;
+#endif
+
 	sig_trapped = sig;
 }