From patchwork Thu Aug 13 20:18:50 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Lutomirski X-Patchwork-Id: 7010731 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AF262C05AC for ; Thu, 13 Aug 2015 20:19:05 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 789CC205FE for ; Thu, 13 Aug 2015 20:19:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3C38420621 for ; Thu, 13 Aug 2015 20:19:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754059AbbHMUS7 (ORCPT ); Thu, 13 Aug 2015 16:18:59 -0400 Received: from mail.kernel.org ([198.145.29.136]:33536 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753989AbbHMUS5 (ORCPT ); Thu, 13 Aug 2015 16:18:57 -0400 Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 60B7320661; Thu, 13 Aug 2015 20:18:56 +0000 (UTC) Received: from localhost (173-228-29-49.dsl.static.fusionbroadband.com [173.228.29.49]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 409CF2066F; Thu, 13 Aug 2015 20:18:55 +0000 (UTC) From: Andy Lutomirski To: Linus Torvalds , Stas Sergeev , x86@kernel.org Cc: Cyrill Gorcunov , Pavel Emelyanov , kvm list , Andy Lutomirski Subject: [RFC/PATCH 3/3] x86/signal/64: Add explicit controls for sigcontext SS handling Date: Thu, 13 Aug 2015 13:18:50 -0700 Message-Id: X-Mailer: git-send-email 2.4.3 In-Reply-To: References: In-Reply-To: References: X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Cc: Linus Torvalds Cc: Cyrill Gorcunov Cc: Pavel Emelyanov Signed-off-by: Andy Lutomirski --- 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(-) 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 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 /* + * 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; }