Reset the fs and gs bases on exec(2).

The values from the old address space do not make sense for the new
program.  In particular, gsbase might be the TLS base for the old
program but the new program has no TLS now.

amd64 already handles this correctly.

Reported and reviewed by:	bde
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2017-10-09 15:39:43 +00:00
parent 4f7c931a11
commit 787f835bf0
3 changed files with 51 additions and 18 deletions

View File

@ -1132,6 +1132,15 @@ exec_setregs(struct thread *td, struct image_params *imgp, u_long stack)
else
mtx_unlock_spin(&dt_lock);
/*
* Reset the fs and gs bases. The values from the old address
* space do not make sense for the new program. In particular,
* gsbase might be the TLS base for the old program but the new
* program has no TLS now.
*/
set_fsbase(td, 0);
set_gsbase(td, 0);
bzero((char *)regs, sizeof(struct trapframe));
regs->tf_eip = imgp->entry_addr;
regs->tf_esp = stack;

View File

@ -91,6 +91,37 @@ fill_based_sd(struct segment_descriptor *sdp, uint32_t base)
sdp->sd_gran = 1;
}
/*
* Construct special descriptors for "base" selectors. Store them in
* the PCB for later use by cpu_switch(). Store them in the GDT for
* more immediate use. The GDT entries are part of the current
* context. Callers must load related segment registers to complete
* setting up the current context.
*/
void
set_fsbase(struct thread *td, uint32_t base)
{
struct segment_descriptor sd;
fill_based_sd(&sd, base);
critical_enter();
td->td_pcb->pcb_fsd = sd;
PCPU_GET(fsgs_gdt)[0] = sd;
critical_exit();
}
void
set_gsbase(struct thread *td, uint32_t base)
{
struct segment_descriptor sd;
fill_based_sd(&sd, base);
critical_enter();
td->td_pcb->pcb_gsd = sd;
PCPU_GET(fsgs_gdt)[1] = sd;
critical_exit();
}
#ifndef _SYS_SYSPROTO_H_
struct sysarch_args {
int op;
@ -109,7 +140,7 @@ sysarch(struct thread *td, struct sysarch_args *uap)
struct i386_get_xfpustate xfpu;
} kargs;
uint32_t base;
struct segment_descriptor sd, *sdp;
struct segment_descriptor *sdp;
AUDIT_ARG_CMD(uap->op);
@ -204,16 +235,11 @@ sysarch(struct thread *td, struct sysarch_args *uap)
error = copyin(uap->parms, &base, sizeof(base));
if (error == 0) {
/*
* Construct a descriptor and store it in the pcb for
* the next context switch. Also store it in the gdt
* so that the load of tf_fs into %fs will activate it
* at return to userland.
* Construct the special descriptor for fsbase
* and arrange for doreti to load its selector
* soon enough.
*/
fill_based_sd(&sd, base);
critical_enter();
td->td_pcb->pcb_fsd = sd;
PCPU_GET(fsgs_gdt)[0] = sd;
critical_exit();
set_fsbase(td, base);
td->td_frame->tf_fs = GSEL(GUFS_SEL, SEL_UPL);
}
break;
@ -226,15 +252,11 @@ sysarch(struct thread *td, struct sysarch_args *uap)
error = copyin(uap->parms, &base, sizeof(base));
if (error == 0) {
/*
* Construct a descriptor and store it in the pcb for
* the next context switch. Also store it in the gdt
* because we have to do a load_gs() right now.
* Construct the special descriptor for gsbase.
* The selector is loaded immediately, since we
* normally only reload %gs on context switches.
*/
fill_based_sd(&sd, base);
critical_enter();
td->td_pcb->pcb_gsd = sd;
PCPU_GET(fsgs_gdt)[1] = sd;
critical_exit();
set_gsbase(td, base);
load_gs(GSEL(GUGS_SEL, SEL_UPL));
}
break;

View File

@ -66,6 +66,8 @@ void init_AMD_Elan_sc520(void);
vm_paddr_t kvtop(void *addr);
void panicifcpuunsupported(void);
void ppro_reenable_apic(void);
void set_fsbase(struct thread *td, uint32_t base);
void set_gsbase(struct thread *td, uint32_t base);
void setidt(int idx, alias_for_inthand_t *func, int typ, int dpl, int selec);
union savefpu *get_pcb_user_save_td(struct thread *td);
union savefpu *get_pcb_user_save_pcb(struct pcb *pcb);