diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2020-09-24 11:18:38 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2020-09-24 11:18:38 +0000 |
commit | db229aeca27693b33b3422a1cb583e6c068c8876 (patch) | |
tree | b8b01b9cbc13e070f4dc602b6228fb17c74feb0e | |
parent | 9dad4670ac02e309a821e43c8ad05853a2ded912 (diff) |
Make sure we fetch the CS limit of the CPU the trap happened on. It is
possible to switch CPUs when handling a trap since we need to lock the vm_map
of the process. In that case the CS limit would be wrong an we incorrectly
decide that there has been an execute privilige violation.
Also make sure we only modify the variables that keep track of the CS
limit and segment descriptor while holding the kernel lock.
Fixes builds of go ports on i386.
tested by sthen@
ok deraadt@
-rw-r--r-- | sys/arch/i386/i386/pmap.c | 14 | ||||
-rw-r--r-- | sys/arch/i386/i386/trap.c | 5 | ||||
-rw-r--r-- | sys/arch/i386/include/pmap.h | 4 | ||||
-rw-r--r-- | sys/arch/i386/include/segments.h | 4 |
4 files changed, 16 insertions, 11 deletions
diff --git a/sys/arch/i386/i386/pmap.c b/sys/arch/i386/i386/pmap.c index ef280cc7c19..4f6025e5851 100644 --- a/sys/arch/i386/i386/pmap.c +++ b/sys/arch/i386/i386/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.207 2020/09/23 15:13:26 deraadt Exp $ */ +/* $OpenBSD: pmap.c,v 1.208 2020/09/24 11:18:37 kettenis Exp $ */ /* $NetBSD: pmap.c,v 1.91 2000/06/02 17:46:37 thorpej Exp $ */ /* @@ -594,25 +594,26 @@ pmap_exec_account(struct pmap *pm, vaddr_t va, struct trapframe *tf = curproc->p_md.md_regs; struct pcb *pcb = &curproc->p_addr->u_pcb; + KERNEL_LOCK(); pm->pm_hiexec = I386_MAX_EXE_ADDR; setcslimit(pm, tf, pcb, I386_MAX_EXE_ADDR); + KERNEL_UNLOCK(); } } -#define SEGDESC_LIMIT(sd) (ptoa(((sd).sd_hilimit << 16) | (sd).sd_lolimit)) - /* * Fixup the code segment to cover all potential executable mappings. * Called by kernel SEGV trap handler. * returns 0 if no changes to the code segment were made. */ int -pmap_exec_fixup(struct vm_map *map, struct trapframe *tf, struct pcb *pcb) +pmap_exec_fixup(struct vm_map *map, struct trapframe *tf, vaddr_t gdt_cs, + struct pcb *pcb) { struct vm_map_entry *ent; struct pmap *pm = vm_map_pmap(map); vaddr_t va = 0; - vaddr_t pm_cs, gdt_cs; + vaddr_t pm_cs; vm_map_lock(map); RBT_FOREACH_REVERSE(ent, uvm_map_addr, &map->addr) { @@ -627,8 +628,9 @@ pmap_exec_fixup(struct vm_map *map, struct trapframe *tf, struct pcb *pcb) va = trunc_page(ent->end - 1); vm_map_unlock(map); + KERNEL_ASSERT_LOCKED(); + pm_cs = SEGDESC_LIMIT(pm->pm_codeseg); - gdt_cs = SEGDESC_LIMIT(curcpu()->ci_gdt[GUCODE_SEL].sd); /* * Another thread running on another cpu can change diff --git a/sys/arch/i386/i386/trap.c b/sys/arch/i386/i386/trap.c index 64ec96958dc..edb6ff82189 100644 --- a/sys/arch/i386/i386/trap.c +++ b/sys/arch/i386/i386/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.145 2020/09/15 09:30:31 deraadt Exp $ */ +/* $OpenBSD: trap.c,v 1.146 2020/09/24 11:18:37 kettenis Exp $ */ /* $NetBSD: trap.c,v 1.95 1996/05/05 06:50:02 mycroft Exp $ */ /*- @@ -119,6 +119,7 @@ trap(struct trapframe *frame) vm_prot_t ftype; union sigval sv; caddr_t onfault; + vaddr_t gdt_cs = SEGDESC_LIMIT(curcpu()->ci_gdt[GUCODE_SEL].sd); uint32_t cr2 = rcr2(); uvmexp.traps++; @@ -261,7 +262,7 @@ trap(struct trapframe *frame) KERNEL_LOCK(); /* If pmap_exec_fixup does something, let's retry the trap. */ - if (pmap_exec_fixup(&p->p_vmspace->vm_map, frame, + if (pmap_exec_fixup(&p->p_vmspace->vm_map, frame, gdt_cs, &p->p_addr->u_pcb)) { KERNEL_UNLOCK(); goto out; diff --git a/sys/arch/i386/include/pmap.h b/sys/arch/i386/include/pmap.h index 1fdf5048130..31bac2eb9de 100644 --- a/sys/arch/i386/include/pmap.h +++ b/sys/arch/i386/include/pmap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.h,v 1.87 2019/12/19 17:46:32 mpi Exp $ */ +/* $OpenBSD: pmap.h,v 1.88 2020/09/24 11:18:37 kettenis Exp $ */ /* $NetBSD: pmap.h,v 1.44 2000/04/24 17:18:18 thorpej Exp $ */ /* @@ -250,7 +250,7 @@ int pmap_test_attrs(struct vm_page *, int); void pmap_write_protect(struct pmap *, vaddr_t, vaddr_t, vm_prot_t); int pmap_exec_fixup(struct vm_map *, struct trapframe *, - struct pcb *); + vaddr_t, struct pcb *); void pmap_exec_account(struct pmap *, vaddr_t, u_int32_t, u_int32_t); struct pv_entry *pmap_remove_pv(struct vm_page *, struct pmap *, vaddr_t); diff --git a/sys/arch/i386/include/segments.h b/sys/arch/i386/include/segments.h index 10a28f8a175..d74c27d307e 100644 --- a/sys/arch/i386/include/segments.h +++ b/sys/arch/i386/include/segments.h @@ -1,4 +1,4 @@ -/* $OpenBSD: segments.h,v 1.26 2018/07/09 19:20:30 guenther Exp $ */ +/* $OpenBSD: segments.h,v 1.27 2020/09/24 11:18:37 kettenis Exp $ */ /* $NetBSD: segments.h,v 1.23 1996/02/01 22:31:03 mycroft Exp $ */ /*- @@ -115,6 +115,8 @@ extern union descriptor *gdt; extern struct gate_descriptor idt_region[]; extern struct gate_descriptor *idt; +#define SEGDESC_LIMIT(sd) (ptoa(((sd).sd_hilimit << 16) | (sd).sd_lolimit)) + void setgate(struct gate_descriptor *, void *, int, int, int, int); void setregion(struct region_descriptor *, void *, size_t); void setsegment(struct segment_descriptor *, void *, size_t, int, int, |