amd64: eliminate td_md.md_fpu_scratch

For signal send, copyout from the user FPU save area directly.

For sigreturn, we are in sleepable context and can do temporal
allocation of the transient save area.  We cannot copying from userspace
directly to user save area because XSAVE state needs to be validated,
also partial copyins can corrupt it.

Requested by:	jhb
Reviewed by:	jhb, markj
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D31954
This commit is contained in:
Konstantin Belousov 2021-09-15 21:37:47 +03:00
parent df8dd6025a
commit bd9e0f5df6
5 changed files with 40 additions and 56 deletions

View File

@ -96,7 +96,7 @@ __FBSDID("$FreeBSD$");
#include <machine/trap.h>
static void get_fpcontext(struct thread *td, mcontext_t *mcp,
char *xfpusave, size_t xfpusave_len);
char **xfpusave, size_t *xfpusave_len);
static int set_fpcontext(struct thread *td, mcontext_t *mcp,
char *xfpustate, size_t xfpustate_len);
@ -133,14 +133,6 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
regs = td->td_frame;
oonstack = sigonstack(regs->tf_rsp);
if (cpu_max_ext_state_size > sizeof(struct savefpu) && use_xsave) {
xfpusave_len = cpu_max_ext_state_size - sizeof(struct savefpu);
xfpusave = (char *)td->td_md.md_fpu_scratch;
} else {
xfpusave_len = 0;
xfpusave = NULL;
}
/* Save user context. */
bzero(&sf, sizeof(sf));
sf.sf_uc.uc_sigmask = *mask;
@ -150,7 +142,7 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0;
bcopy(regs, &sf.sf_uc.uc_mcontext.mc_rdi, sizeof(*regs));
sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */
get_fpcontext(td, &sf.sf_uc.uc_mcontext, xfpusave, xfpusave_len);
get_fpcontext(td, &sf.sf_uc.uc_mcontext, &xfpusave, &xfpusave_len);
fpstate_drop(td);
update_pcb_bases(pcb);
sf.sf_uc.uc_mcontext.mc_fsbase = pcb->pcb_fsbase;
@ -301,10 +293,11 @@ sys_sigreturn(struct thread *td, struct sigreturn_args *uap)
p->p_pid, td->td_name, xfpustate_len);
return (EINVAL);
}
xfpustate = __builtin_alloca(xfpustate_len);
xfpustate = (char *)fpu_save_area_alloc();
error = copyin((const void *)uc.uc_mcontext.mc_xfpustate,
xfpustate, xfpustate_len);
if (error != 0) {
fpu_save_area_free((struct savefpu *)xfpustate);
uprintf(
"pid %d (%s): sigreturn copying xfpustate failed\n",
p->p_pid, td->td_name);
@ -315,6 +308,7 @@ sys_sigreturn(struct thread *td, struct sigreturn_args *uap)
xfpustate_len = 0;
}
ret = set_fpcontext(td, &ucp->uc_mcontext, xfpustate, xfpustate_len);
fpu_save_area_free((struct savefpu *)xfpustate);
if (ret != 0) {
uprintf("pid %d (%s): sigreturn set_fpcontext err %d\n",
p->p_pid, td->td_name, ret);
@ -674,14 +668,17 @@ set_mcontext(struct thread *td, mcontext_t *mcp)
if (mcp->mc_xfpustate_len > cpu_max_ext_state_size -
sizeof(struct savefpu))
return (EINVAL);
xfpustate = (char *)td->td_md.md_fpu_scratch;
xfpustate = (char *)fpu_save_area_alloc();
ret = copyin((void *)mcp->mc_xfpustate, xfpustate,
mcp->mc_xfpustate_len);
if (ret != 0)
if (ret != 0) {
fpu_save_area_free((struct savefpu *)xfpustate);
return (ret);
}
} else
xfpustate = NULL;
ret = set_fpcontext(td, mcp, xfpustate, mcp->mc_xfpustate_len);
fpu_save_area_free((struct savefpu *)xfpustate);
if (ret != 0)
return (ret);
tp->tf_r15 = mcp->mc_r15;
@ -719,26 +716,24 @@ set_mcontext(struct thread *td, mcontext_t *mcp)
}
static void
get_fpcontext(struct thread *td, mcontext_t *mcp, char *xfpusave,
size_t xfpusave_len)
get_fpcontext(struct thread *td, mcontext_t *mcp, char **xfpusave,
size_t *xfpusave_len)
{
size_t max_len, len;
mcp->mc_ownedfp = fpugetregs(td);
bcopy(get_pcb_user_save_td(td), &mcp->mc_fpstate[0],
sizeof(mcp->mc_fpstate));
mcp->mc_fpformat = fpuformat();
if (!use_xsave || xfpusave_len == 0)
if (xfpusave == NULL)
return;
max_len = cpu_max_ext_state_size - sizeof(struct savefpu);
len = xfpusave_len;
if (len > max_len) {
len = max_len;
bzero(xfpusave + max_len, len - max_len);
if (!use_xsave || cpu_max_ext_state_size <= sizeof(struct savefpu)) {
*xfpusave_len = 0;
*xfpusave = NULL;
} else {
mcp->mc_flags |= _MC_HASFPXSTATE;
*xfpusave_len = mcp->mc_xfpustate_len =
cpu_max_ext_state_size - sizeof(struct savefpu);
*xfpusave = (char *)(get_pcb_user_save_td(td) + 1);
}
mcp->mc_flags |= _MC_HASFPXSTATE;
mcp->mc_xfpustate_len = len;
bcopy(get_pcb_user_save_td(td) + 1, xfpusave, len);
}
static int

View File

@ -392,7 +392,6 @@ cpu_thread_alloc(struct thread *td)
td->td_pcb = pcb = get_pcb_td(td);
td->td_frame = (struct trapframe *)td->td_md.md_stack_base - 1;
td->td_md.md_usr_fpu_save = fpu_save_area_alloc();
td->td_md.md_fpu_scratch = fpu_save_area_alloc();
pcb->pcb_save = get_pcb_user_save_pcb(pcb);
if (use_xsave) {
xhdr = (struct xstate_hdr *)(pcb->pcb_save + 1);
@ -408,8 +407,6 @@ cpu_thread_free(struct thread *td)
fpu_save_area_free(td->td_md.md_usr_fpu_save);
td->td_md.md_usr_fpu_save = NULL;
fpu_save_area_free(td->td_md.md_fpu_scratch);
td->td_md.md_fpu_scratch = NULL;
}
bool

View File

@ -87,10 +87,8 @@ static void freebsd4_ia32_sendsig(sig_t, ksiginfo_t *, sigset_t *);
static void
ia32_get_fpcontext(struct thread *td, struct ia32_mcontext *mcp,
char *xfpusave, size_t xfpusave_len)
char **xfpusave, size_t *xfpusave_len)
{
size_t max_len, len;
/*
* XXX Format of 64bit and 32bit FXSAVE areas differs. FXSAVE
* in 32bit mode saves %cs and %ds, while on 64bit it saves
@ -101,17 +99,15 @@ ia32_get_fpcontext(struct thread *td, struct ia32_mcontext *mcp,
bcopy(get_pcb_user_save_td(td), &mcp->mc_fpstate[0],
sizeof(mcp->mc_fpstate));
mcp->mc_fpformat = fpuformat();
if (!use_xsave || xfpusave_len == 0)
return;
max_len = cpu_max_ext_state_size - sizeof(struct savefpu);
len = xfpusave_len;
if (len > max_len) {
len = max_len;
bzero(xfpusave + max_len, len - max_len);
if (!use_xsave || cpu_max_ext_state_size <= sizeof(struct savefpu)) {
*xfpusave_len = 0;
*xfpusave = NULL;
} else {
mcp->mc_flags |= _MC_IA32_HASFPXSTATE;
*xfpusave_len = mcp->mc_xfpustate_len =
cpu_max_ext_state_size - sizeof(struct savefpu);
*xfpusave = (char *)(get_pcb_user_save_td(td) + 1);
}
mcp->mc_flags |= _MC_IA32_HASFPXSTATE;
mcp->mc_xfpustate_len = len;
bcopy(get_pcb_user_save_td(td) + 1, xfpusave, len);
}
static int
@ -210,14 +206,17 @@ ia32_set_mcontext(struct thread *td, struct ia32_mcontext *mcp)
if (mcp->mc_xfpustate_len > cpu_max_ext_state_size -
sizeof(struct savefpu))
return (EINVAL);
xfpustate = (char *)td->td_md.md_fpu_scratch;
xfpustate = (char *)fpu_save_area_alloc();
ret = copyin(PTRIN(mcp->mc_xfpustate), xfpustate,
mcp->mc_xfpustate_len);
if (ret != 0)
if (ret != 0) {
fpu_save_area_free((struct savefpu *)xfpustate);
return (ret);
}
} else
xfpustate = NULL;
ret = ia32_set_fpcontext(td, mcp, xfpustate, mcp->mc_xfpustate_len);
fpu_save_area_free((struct savefpu *)xfpustate);
if (ret != 0)
return (ret);
tp->tf_gs = mcp->mc_gs;
@ -577,14 +576,6 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
regs = td->td_frame;
oonstack = sigonstack(regs->tf_rsp);
if (cpu_max_ext_state_size > sizeof(struct savefpu) && use_xsave) {
xfpusave_len = cpu_max_ext_state_size - sizeof(struct savefpu);
xfpusave = (char *)td->td_md.md_fpu_scratch;
} else {
xfpusave_len = 0;
xfpusave = NULL;
}
/* Save user context. */
bzero(&sf, sizeof(sf));
sf.sf_uc.uc_sigmask = *mask;
@ -613,7 +604,7 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
sf.sf_uc.uc_mcontext.mc_fs = regs->tf_fs;
sf.sf_uc.uc_mcontext.mc_gs = regs->tf_gs;
sf.sf_uc.uc_mcontext.mc_len = sizeof(sf.sf_uc.uc_mcontext); /* magic */
ia32_get_fpcontext(td, &sf.sf_uc.uc_mcontext, xfpusave, xfpusave_len);
ia32_get_fpcontext(td, &sf.sf_uc.uc_mcontext, &xfpusave, &xfpusave_len);
fpstate_drop(td);
sf.sf_uc.uc_mcontext.mc_fsbase = td->td_pcb->pcb_fsbase;
sf.sf_uc.uc_mcontext.mc_gsbase = td->td_pcb->pcb_gsbase;
@ -882,10 +873,11 @@ freebsd32_sigreturn(td, uap)
td->td_proc->p_pid, td->td_name, xfpustate_len);
return (EINVAL);
}
xfpustate = (char *)td->td_md.md_fpu_scratch;
xfpustate = (char *)fpu_save_area_alloc();
error = copyin(PTRIN(ucp->uc_mcontext.mc_xfpustate),
xfpustate, xfpustate_len);
if (error != 0) {
fpu_save_area_free((struct savefpu *)xfpustate);
uprintf(
"pid %d (%s): sigreturn copying xfpustate failed\n",
td->td_proc->p_pid, td->td_name);
@ -897,6 +889,7 @@ freebsd32_sigreturn(td, uap)
}
ret = ia32_set_fpcontext(td, &ucp->uc_mcontext, xfpustate,
xfpustate_len);
fpu_save_area_free((struct savefpu *)xfpustate);
if (ret != 0) {
uprintf("pid %d (%s): sigreturn set_fpcontext err %d\n",
td->td_proc->p_pid, td->td_name, ret);

View File

@ -76,7 +76,6 @@ struct mdthread {
struct pcb md_pcb;
vm_offset_t md_stack_base;
struct savefpu *md_usr_fpu_save;
struct savefpu *md_fpu_scratch;
};
struct mdproc {

View File

@ -91,7 +91,7 @@ _Static_assert(offsetof(struct thread, td_pflags) == 0x110,
"struct thread KBI td_pflags");
_Static_assert(offsetof(struct thread, td_frame) == 0x4a8,
"struct thread KBI td_frame");
_Static_assert(offsetof(struct thread, td_emuldata) == 0x6c0,
_Static_assert(offsetof(struct thread, td_emuldata) == 0x6b0,
"struct thread KBI td_emuldata");
_Static_assert(offsetof(struct proc, p_flag) == 0xb8,
"struct proc KBI p_flag");