A different fix for the issue from r323722.

Split the handlers for pop of invalid selectors from the trap frame
into usermode and kernel variants.  Usermode handler is kept as is, it
restores the already loaded parts of the trap frame and jumps to set
up a signal delivery to the user process.

New kernel part of the handler emulates IRET treatment of the segments
which would violate access right.  It loads NUL selector in the
segment register which load causes the fault, and then continues the
return to interrupted kernel code.  Since invalid selectors in the
segment registers in the kernel mode can only exist while kernel still
enters or exits from userspace, we only zero invalid userspace
selectors.  If userspace tries to use the segment register, it gets a
signal, as if the processor segment descriptor cache was reloaded.

Reported by:	Maxime Villard <max@m00nbsd.net>
Suggested and reviewed by:	bde
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
kib 2017-09-28 09:01:28 +00:00
parent 071c00f495
commit 50cb59e230
2 changed files with 31 additions and 2 deletions

View File

@ -425,8 +425,16 @@ doreti_iret:
* doreti_iret_fault and friends. Alternative return code for
* the case where we get a fault in the doreti_exit code
* above. trap() (i386/i386/trap.c) catches this specific
* case, sends the process a signal and continues in the
* corresponding place in the code below.
* case, and continues in the corresponding place in the code
* below.
*
* If the fault occured during return to usermode, we recreate
* the trap frame and call trap() to send a signal. Otherwise
* the kernel was tricked into fault by attempt to restore invalid
* usermode segment selectors on return from nested fault or
* interrupt, where interrupted kernel entry code not yet loaded
* kernel selectors. In the latter case, emulate iret and zero
* the invalid selector.
*/
ALIGN_TEXT
.globl doreti_iret_fault
@ -437,18 +445,35 @@ doreti_iret_fault:
movw %ds,(%esp)
.globl doreti_popl_ds_fault
doreti_popl_ds_fault:
testb $SEL_RPL_MASK,TF_CS-TF_DS(%esp)
jz doreti_popl_ds_kfault
pushl $0
movw %es,(%esp)
.globl doreti_popl_es_fault
doreti_popl_es_fault:
testb $SEL_RPL_MASK,TF_CS-TF_ES(%esp)
jz doreti_popl_es_kfault
pushl $0
movw %fs,(%esp)
.globl doreti_popl_fs_fault
doreti_popl_fs_fault:
testb $SEL_RPL_MASK,TF_CS-TF_FS(%esp)
jz doreti_popl_fs_kfault
sti
movl $0,TF_ERR(%esp) /* XXX should be the error code */
movl $T_PROTFLT,TF_TRAPNO(%esp)
jmp alltraps_with_regs_pushed
doreti_popl_ds_kfault:
movl $0,(%esp)
jmp doreti_popl_ds
doreti_popl_es_kfault:
movl $0,(%esp)
jmp doreti_popl_es
doreti_popl_fs_kfault:
movl $0,(%esp)
jmp doreti_popl_fs
#ifdef HWPMC_HOOKS
doreti_nmi:
/*

View File

@ -156,11 +156,15 @@ ASSYM(PCB_IDT, offsetof(struct pcb, pcb_idt));
ASSYM(PCB_LDT, offsetof(struct pcb, pcb_ldt));
ASSYM(PCB_TR, offsetof(struct pcb, pcb_tr));
ASSYM(TF_FS, offsetof(struct trapframe, tf_fs));
ASSYM(TF_ES, offsetof(struct trapframe, tf_es));
ASSYM(TF_DS, offsetof(struct trapframe, tf_ds));
ASSYM(TF_TRAPNO, offsetof(struct trapframe, tf_trapno));
ASSYM(TF_ERR, offsetof(struct trapframe, tf_err));
ASSYM(TF_EIP, offsetof(struct trapframe, tf_eip));
ASSYM(TF_CS, offsetof(struct trapframe, tf_cs));
ASSYM(TF_EFLAGS, offsetof(struct trapframe, tf_eflags));
ASSYM(SIGF_HANDLER, offsetof(struct sigframe, sf_ahu.sf_handler));
#ifdef COMPAT_43
ASSYM(SIGF_SC, offsetof(struct osigframe, sf_siginfo.si_sc));