x86/fpu/signal: Prepare for variable sigframe length

The software reserved portion of the fxsave frame in the signal frame
is copied from structures which have been set up at boot time. With
dynamically enabled features the content of these structures is no
longer correct because the xfeatures and size can be different per task.

Calculate the software reserved portion at runtime and fill in the
xfeatures and size values from the tasks active fpstate.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20211021225527.10184-10-chang.seok.bae@intel.com
This commit is contained in:
Chang S. Bae 2021-10-21 15:55:13 -07:00 committed by Borislav Petkov
parent 4b7ca609a3
commit 53599b4d54
3 changed files with 26 additions and 40 deletions

View File

@ -21,9 +21,6 @@ static __always_inline __pure bool use_fxsr(void)
# define WARN_ON_FPU(x) ({ (void)(x); 0; }) # define WARN_ON_FPU(x) ({ (void)(x); 0; })
#endif #endif
/* Init functions */
extern void fpu__init_prepare_fx_sw_frame(void);
/* Used in init.c */ /* Used in init.c */
extern void fpstate_init_user(struct fpstate *fpstate); extern void fpstate_init_user(struct fpstate *fpstate);
extern void fpstate_reset(struct fpu *fpu); extern void fpstate_reset(struct fpu *fpu);

View File

@ -20,9 +20,6 @@
#include "legacy.h" #include "legacy.h"
#include "xstate.h" #include "xstate.h"
static struct _fpx_sw_bytes fx_sw_reserved __ro_after_init;
static struct _fpx_sw_bytes fx_sw_reserved_ia32 __ro_after_init;
/* /*
* Check for the presence of extended state information in the * Check for the presence of extended state information in the
* user fpstate pointer in the sigcontext. * user fpstate pointer in the sigcontext.
@ -98,23 +95,42 @@ static inline bool save_fsave_header(struct task_struct *tsk, void __user *buf)
return true; return true;
} }
/*
* Prepare the SW reserved portion of the fxsave memory layout, indicating
* the presence of the extended state information in the memory layout
* pointed to by the fpstate pointer in the sigcontext.
* This is saved when ever the FP and extended state context is
* saved on the user stack during the signal handler delivery to the user.
*/
static inline void save_sw_bytes(struct _fpx_sw_bytes *sw_bytes, bool ia32_frame,
struct fpstate *fpstate)
{
sw_bytes->magic1 = FP_XSTATE_MAGIC1;
sw_bytes->extended_size = fpstate->user_size + FP_XSTATE_MAGIC2_SIZE;
sw_bytes->xfeatures = fpstate->user_xfeatures;
sw_bytes->xstate_size = fpstate->user_size;
if (ia32_frame)
sw_bytes->extended_size += sizeof(struct fregs_state);
}
static inline bool save_xstate_epilog(void __user *buf, int ia32_frame, static inline bool save_xstate_epilog(void __user *buf, int ia32_frame,
unsigned int usize) struct fpstate *fpstate)
{ {
struct xregs_state __user *x = buf; struct xregs_state __user *x = buf;
struct _fpx_sw_bytes *sw_bytes; struct _fpx_sw_bytes sw_bytes;
u32 xfeatures; u32 xfeatures;
int err; int err;
/* Setup the bytes not touched by the [f]xsave and reserved for SW. */ /* Setup the bytes not touched by the [f]xsave and reserved for SW. */
sw_bytes = ia32_frame ? &fx_sw_reserved_ia32 : &fx_sw_reserved; save_sw_bytes(&sw_bytes, ia32_frame, fpstate);
err = __copy_to_user(&x->i387.sw_reserved, sw_bytes, sizeof(*sw_bytes)); err = __copy_to_user(&x->i387.sw_reserved, &sw_bytes, sizeof(sw_bytes));
if (!use_xsave()) if (!use_xsave())
return !err; return !err;
err |= __put_user(FP_XSTATE_MAGIC2, err |= __put_user(FP_XSTATE_MAGIC2,
(__u32 __user *)(buf + usize)); (__u32 __user *)(buf + fpstate->user_size));
/* /*
* Read the xfeatures which we copied (directly from the cpu or * Read the xfeatures which we copied (directly from the cpu or
@ -173,7 +189,7 @@ bool copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)
{ {
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct fpstate *fpstate = tsk->thread.fpu.fpstate; struct fpstate *fpstate = tsk->thread.fpu.fpstate;
int ia32_fxstate = (buf != buf_fx); bool ia32_fxstate = (buf != buf_fx);
int ret; int ret;
ia32_fxstate &= (IS_ENABLED(CONFIG_X86_32) || ia32_fxstate &= (IS_ENABLED(CONFIG_X86_32) ||
@ -226,8 +242,7 @@ retry:
if ((ia32_fxstate || !use_fxsr()) && !save_fsave_header(tsk, buf)) if ((ia32_fxstate || !use_fxsr()) && !save_fsave_header(tsk, buf))
return false; return false;
if (use_fxsr() && if (use_fxsr() && !save_xstate_epilog(buf_fx, ia32_fxstate, fpstate))
!save_xstate_epilog(buf_fx, ia32_fxstate, fpstate->user_size))
return false; return false;
return true; return true;
@ -523,28 +538,3 @@ unsigned long __init fpu__get_fpstate_size(void)
return ret; return ret;
} }
/*
* Prepare the SW reserved portion of the fxsave memory layout, indicating
* the presence of the extended state information in the memory layout
* pointed by the fpstate pointer in the sigcontext.
* This will be saved when ever the FP and extended state context is
* saved on the user stack during the signal handler delivery to the user.
*/
void __init fpu__init_prepare_fx_sw_frame(void)
{
int size = fpu_user_cfg.default_size + FP_XSTATE_MAGIC2_SIZE;
fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
fx_sw_reserved.extended_size = size;
fx_sw_reserved.xfeatures = fpu_user_cfg.default_features;
fx_sw_reserved.xstate_size = fpu_user_cfg.default_size;
if (IS_ENABLED(CONFIG_IA32_EMULATION) ||
IS_ENABLED(CONFIG_X86_32)) {
int fsave_header_size = sizeof(struct fregs_state);
fx_sw_reserved_ia32 = fx_sw_reserved;
fx_sw_reserved_ia32.extended_size = size + fsave_header_size;
}
}

View File

@ -830,7 +830,6 @@ void __init fpu__init_system_xstate(unsigned int legacy_size)
update_regset_xstate_info(fpu_user_cfg.max_size, update_regset_xstate_info(fpu_user_cfg.max_size,
fpu_user_cfg.max_features); fpu_user_cfg.max_features);
fpu__init_prepare_fx_sw_frame();
setup_init_fpu_buf(); setup_init_fpu_buf();
setup_xstate_comp_offsets(); setup_xstate_comp_offsets();
setup_supervisor_only_offsets(); setup_supervisor_only_offsets();