Commit 4339d0c6 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Borislav Petkov

x86/fpu/signal: Clarify exception handling in restore_fpregs_from_user()

FPU restore from a signal frame can trigger various exceptions. The
exceptions are caught with an exception table entry. The handler of this
entry stores the trap number in EAX. The FPU specific fixup negates that
trap number to convert it into an negative error code.

Any other exception than #PF is fatal and recovery is not possible. This
relies on the fact that the #PF exception number is the same as EFAULT, but
that's not really obvious.

Remove the negation from the exception fixup as it really has no value and
check for X86_TRAP_PF at the call site.

There is still confusion due to the return code conversion for the error
case which will be cleaned up separately.
Suggested-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20210908132525.506192488@linutronix.de
parent 0c2e62ba
...@@ -88,7 +88,10 @@ static inline void fpstate_init_soft(struct swregs_state *soft) {} ...@@ -88,7 +88,10 @@ static inline void fpstate_init_soft(struct swregs_state *soft) {}
#endif #endif
extern void save_fpregs_to_fpstate(struct fpu *fpu); extern void save_fpregs_to_fpstate(struct fpu *fpu);
/* Returns 0 or the negated trap number, which results in -EFAULT for #PF */ /*
* Returns 0 on success or the trap number when the operation raises an
* exception.
*/
#define user_insn(insn, output, input...) \ #define user_insn(insn, output, input...) \
({ \ ({ \
int err; \ int err; \
...@@ -98,11 +101,7 @@ extern void save_fpregs_to_fpstate(struct fpu *fpu); ...@@ -98,11 +101,7 @@ extern void save_fpregs_to_fpstate(struct fpu *fpu);
asm volatile(ASM_STAC "\n" \ asm volatile(ASM_STAC "\n" \
"1: " #insn "\n" \ "1: " #insn "\n" \
"2: " ASM_CLAC "\n" \ "2: " ASM_CLAC "\n" \
".section .fixup,\"ax\"\n" \ _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_FAULT_MCE_SAFE) \
"3: negl %%eax\n" \
" jmp 2b\n" \
".previous\n" \
_ASM_EXTABLE_TYPE(1b, 3b, EX_TYPE_FAULT_MCE_SAFE) \
: [err] "=a" (err), output \ : [err] "=a" (err), output \
: "0"(0), input); \ : "0"(0), input); \
err; \ err; \
...@@ -198,18 +197,14 @@ static inline void fxsave(struct fxregs_state *fx) ...@@ -198,18 +197,14 @@ static inline void fxsave(struct fxregs_state *fx)
#define XRSTORS ".byte " REX_PREFIX "0x0f,0xc7,0x1f" #define XRSTORS ".byte " REX_PREFIX "0x0f,0xc7,0x1f"
/* /*
* After this @err contains 0 on success or the negated trap number when * After this @err contains 0 on success or the trap number when the
* the operation raises an exception. For faults this results in -EFAULT. * operation raises an exception.
*/ */
#define XSTATE_OP(op, st, lmask, hmask, err) \ #define XSTATE_OP(op, st, lmask, hmask, err) \
asm volatile("1:" op "\n\t" \ asm volatile("1:" op "\n\t" \
"xor %[err], %[err]\n" \ "xor %[err], %[err]\n" \
"2:\n\t" \ "2:\n\t" \
".pushsection .fixup,\"ax\"\n\t" \ _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_FAULT_MCE_SAFE) \
"3: negl %%eax\n\t" \
"jmp 2b\n\t" \
".popsection\n\t" \
_ASM_EXTABLE_TYPE(1b, 3b, EX_TYPE_FAULT_MCE_SAFE) \
: [err] "=a" (err) \ : [err] "=a" (err) \
: "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \ : "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
: "memory") : "memory")
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <asm/fpu/xstate.h> #include <asm/fpu/xstate.h>
#include <asm/sigframe.h> #include <asm/sigframe.h>
#include <asm/trapnr.h>
#include <asm/trace/fpu.h> #include <asm/trace/fpu.h>
static struct _fpx_sw_bytes fx_sw_reserved __ro_after_init; static struct _fpx_sw_bytes fx_sw_reserved __ro_after_init;
...@@ -275,7 +276,7 @@ static int restore_fpregs_from_user(void __user *buf, u64 xrestore, ...@@ -275,7 +276,7 @@ static int restore_fpregs_from_user(void __user *buf, u64 xrestore,
fpregs_unlock(); fpregs_unlock();
/* Try to handle #PF, but anything else is fatal. */ /* Try to handle #PF, but anything else is fatal. */
if (ret != -EFAULT) if (ret != X86_TRAP_PF)
return -EINVAL; return -EINVAL;
ret = fault_in_pages_readable(buf, size); ret = fault_in_pages_readable(buf, size);
...@@ -405,7 +406,7 @@ static int __fpu_restore_sig(void __user *buf, void __user *buf_fx, ...@@ -405,7 +406,7 @@ static int __fpu_restore_sig(void __user *buf, void __user *buf_fx,
u64 mask = user_xfeatures | xfeatures_mask_supervisor(); u64 mask = user_xfeatures | xfeatures_mask_supervisor();
fpu->state.xsave.header.xfeatures &= mask; fpu->state.xsave.header.xfeatures &= mask;
ret = os_xrstor_safe(&fpu->state.xsave, xfeatures_mask_all); ret = os_xrstor_safe(&fpu->state.xsave, xfeatures_mask_all) ? -EINVAL : 0;
} else { } else {
ret = fxrstor_safe(&fpu->state.fxsave); ret = fxrstor_safe(&fpu->state.fxsave);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment