Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp1738643imu; Wed, 28 Nov 2018 14:23:01 -0800 (PST) X-Google-Smtp-Source: AFSGD/VVyOf+Ku2pMWYwKMpTBenZdvX9VlRuwKNvpkwkyApmIyhKzYCs4YzSSHU5zaDWnn1waF8o X-Received: by 2002:a17:902:103:: with SMTP id 3-v6mr37634998plb.87.1543443781821; Wed, 28 Nov 2018 14:23:01 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543443781; cv=none; d=google.com; s=arc-20160816; b=b9UE/UswMI1pHNkcl3IyY/7o1eNv6i5Ue8iDafBnbIjSRCa5udWk+8rfLFxMW/WYC9 /lppvFWMWWgLleAZHF8OafNSxW0QWq9X/63kvyO9ncbnUQnsqTWW3O9GF6Fw54VHAhLh kpCCCA4oBvuvrHcCrYZAk6w58yWql8iQaxP0uao1nlZAxV1DQGkIebZaF3k7Y6CNKipG JG338CFw2L2ZTYE8N63DHcyucUNjKfI4ZoRT+T3le9k2PqsXfDw99+U0JL4f2ne2+JZE IPimwz8DVBSdEZ90SUb1eFlLzme5mm6r8Img8dFIGAqiWUmvq6aLZ8deOs7LIemol9ZX Zu3Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=WXZgZMHzWoL/l4ATlKNyA6fIHe/REaQ13jz2Us+HZ54=; b=goMo+63aDrkLTnsLkx/B6MJSMjuzaqBdGLElJWBKLA1fV7RMLmH5tEJmu1yBlBwLwD hWs+wrovamsQcLelsFmDZfehVDSPf2fosDwkw6oOur5JKobjm/wgMB5bcPBa21JWBbD7 jsuisSPW7O5kIbrvCkneO6tbdLOlcKFA9i5iPDMjNiofKAfXzkIe6W62KhXvTBeccwS4 JOx6Tm+nQY/7OK/B6DZA6o2xVF7YG5xk5WKLT6nRI2M5fNYPnoEQ3He8Lt/wqkO4yiOX dqa4sOz+QoH8ZNjSAJ+1PEae+HnHxe0TNF4p3iGU7TFrocgAuFaKQELNzvTp+SM8dapu YL5g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z13si8399596pgh.31.2018.11.28.14.22.47; Wed, 28 Nov 2018 14:23:01 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727402AbeK2JYp (ORCPT + 99 others); Thu, 29 Nov 2018 04:24:45 -0500 Received: from Galois.linutronix.de ([146.0.238.70]:33247 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727223AbeK2JYo (ORCPT ); Thu, 29 Nov 2018 04:24:44 -0500 Received: from localhost ([127.0.0.1] helo=bazinga.breakpoint.cc) by Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from ) id 1gS8D2-0001GX-W6; Wed, 28 Nov 2018 23:21:29 +0100 From: Sebastian Andrzej Siewior To: linux-kernel@vger.kernel.org Cc: x86@kernel.org, Andy Lutomirski , Paolo Bonzini , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= , kvm@vger.kernel.org, "Jason A. Donenfeld" , Rik van Riel , Dave Hansen , Sebastian Andrzej Siewior Subject: [PATCH 27/29] x86/fpu: Let __fpu__restore_sig() restore the !32bit+fxsr frame from kernel memory Date: Wed, 28 Nov 2018 23:20:33 +0100 Message-Id: <20181128222035.2996-28-bigeasy@linutronix.de> X-Mailer: git-send-email 2.20.0.rc1 In-Reply-To: <20181128222035.2996-1-bigeasy@linutronix.de> References: <20181128222035.2996-1-bigeasy@linutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The !32bit+fxsr case loads the new state from user memory. In case we restore the FPU state on return to userland we can't do this. It would be required to disable preemption in order to avoid a context switch which would set TIF_NEED_FPU_LOAD. If this happens before the "restore" operation then the loaded registers would become volatile. Disabling preemption while accessing user memory requires to disable the pagefault handler. An error during XRSTOR would then mean that either a page fault occured (and we have to retry with enabled page fault handler) or a #GP occured because the xstate is bogus (after all the sig-handler can modify it). In order to avoid that mess, copy the FPU state from userland, validate it and then load it. The copy_users_…() helper are basically the old helper except that they operate on kernel memory and the fault handler just sets the error value and the caller handles it. Signed-off-by: Sebastian Andrzej Siewior --- arch/x86/include/asm/fpu/internal.h | 32 ++++++++++----- arch/x86/kernel/fpu/signal.c | 62 +++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h index 1e038b7357485..9fb2b8b811d22 100644 --- a/arch/x86/include/asm/fpu/internal.h +++ b/arch/x86/include/asm/fpu/internal.h @@ -118,6 +118,21 @@ extern void fpstate_sanitize_xstate(struct fpu *fpu); err; \ }) +#define kernel_insn_norestore(insn, output, input...) \ +({ \ + int err; \ + asm volatile("1:" #insn "\n\t" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movl $-1,%[err]\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE(1b, 3b) \ + : [err] "=r" (err), output \ + : "0"(0), input); \ + err; \ +}) + #define kernel_insn(insn, output, input...) \ asm volatile("1:" #insn "\n\t" \ "2:\n" \ @@ -138,15 +153,15 @@ static inline void copy_kernel_to_fxregs(struct fxregs_state *fx) } } -static inline int copy_user_to_fxregs(struct fxregs_state __user *fx) +static inline int copy_users_to_fxregs(struct fxregs_state *fx) { if (IS_ENABLED(CONFIG_X86_32)) - return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx)); + return kernel_insn_norestore(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx)); else if (IS_ENABLED(CONFIG_AS_FXSAVEQ)) - return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx)); + return kernel_insn_norestore(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx)); /* See comment in copy_fxregs_to_kernel() below. */ - return user_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), + return kernel_insn_norestore(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), "m" (*fx)); } @@ -155,9 +170,9 @@ static inline void copy_kernel_to_fregs(struct fregs_state *fx) kernel_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); } -static inline int copy_user_to_fregs(struct fregs_state __user *fx) +static inline int copy_users_to_fregs(struct fregs_state *fx) { - return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); + return kernel_insn_norestore(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); } static inline void copy_fxregs_to_kernel(struct fpu *fpu) @@ -337,16 +352,13 @@ static inline void copy_kernel_to_xregs(struct xregs_state *xstate, u64 mask) /* * Restore xstate from user space xsave area. */ -static inline int copy_user_to_xregs(struct xregs_state __user *buf, u64 mask) +static inline int copy_users_to_xregs(struct xregs_state *xstate, u64 mask) { - struct xregs_state *xstate = ((__force struct xregs_state *)buf); u32 lmask = mask; u32 hmask = mask >> 32; int err; - stac(); XSTATE_OP(XRSTOR, xstate, lmask, hmask, err); - clac(); return err; } diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index c66356b168b39..339a8c113517e 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -217,7 +217,8 @@ sanitize_restored_xstate(union fpregs_state *state, */ xsave->i387.mxcsr &= mxcsr_feature_mask; - convert_to_fxsr(&state->fxsave, ia32_env); + if (ia32_env) + convert_to_fxsr(&state->fxsave, ia32_env); } } @@ -299,28 +300,63 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) kfree(tmp); return err; } else { + union fpregs_state *state; + void *tmp; int ret; + tmp = kzalloc(sizeof(*state) + fpu_kernel_xstate_size + 64, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + state = PTR_ALIGN(tmp, 64); + /* * For 64-bit frames and 32-bit fsave frames, restore the user * state to the registers directly (with exceptions handled). */ - if (use_xsave()) { - if ((unsigned long)buf_fx % 64 || fx_only) { + if ((unsigned long)buf_fx % 64) + fx_only = 1; + + if (use_xsave() && !fx_only) { + u64 init_bv = xfeatures_mask & ~xfeatures; + + if (using_compacted_format()) { + ret = copy_user_to_xstate(&state->xsave, buf_fx); + } else { + ret = __copy_from_user(&state->xsave, buf_fx, state_size); + + if (!ret && state_size > offsetof(struct xregs_state, header)) + ret = validate_xstate_header(&state->xsave.header); + } + if (ret) + goto err_out; + sanitize_restored_xstate(state, NULL, xfeatures, + fx_only); + + if (unlikely(init_bv)) + copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); + ret = copy_users_to_xregs(&state->xsave, xfeatures); + + } else if (use_fxsr()) { + ret = __copy_from_user(&state->fxsave, buf_fx, state_size); + if (ret) + goto err_out; + + if (use_xsave()) { u64 init_bv = xfeatures_mask & ~XFEATURE_MASK_FPSSE; copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); - ret = copy_user_to_fxregs(buf_fx); - } else { - u64 init_bv = xfeatures_mask & ~xfeatures; - if (unlikely(init_bv)) - copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); - ret = copy_user_to_xregs(buf_fx, xfeatures); } - } else if (use_fxsr()) { - ret = copy_user_to_fxregs(buf_fx); - } else - ret = copy_user_to_fregs(buf_fx); + state->fxsave.mxcsr &= mxcsr_feature_mask; + ret = copy_users_to_fxregs(&state->fxsave); + } else { + ret = __copy_from_user(&state->fsave, buf_fx, state_size); + if (ret) + goto err_out; + ret = copy_users_to_fregs(buf_fx); + } + +err_out: + kfree(tmp); if (ret) { fpu__clear(fpu); return -1; -- 2.20.0.rc1