diff options
Diffstat (limited to 'sys/arch/i386')
-rw-r--r-- | sys/arch/i386/i386/pmap.c | 19 |
1 files changed, 17 insertions, 2 deletions
diff --git a/sys/arch/i386/i386/pmap.c b/sys/arch/i386/i386/pmap.c index f8f05cb8b88..1fc52617d3f 100644 --- a/sys/arch/i386/i386/pmap.c +++ b/sys/arch/i386/i386/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.157 2012/03/09 13:01:28 ariane Exp $ */ +/* $OpenBSD: pmap.c,v 1.158 2013/02/13 20:45:41 kurt Exp $ */ /* $NetBSD: pmap.c,v 1.91 2000/06/02 17:46:37 thorpej Exp $ */ /* @@ -589,6 +589,8 @@ pmap_exec_account(struct pmap *pm, vaddr_t va, } } +#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. @@ -600,6 +602,7 @@ pmap_exec_fixup(struct vm_map *map, struct trapframe *tf, 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; vm_map_lock(map); RB_FOREACH_REVERSE(ent, uvm_map_addr, &map->addr) { @@ -614,7 +617,19 @@ pmap_exec_fixup(struct vm_map *map, struct trapframe *tf, struct pcb *pcb) va = trunc_page(ent->end - 1); vm_map_unlock(map); - if (va <= pm->pm_hiexec) { + 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 + * pm_hiexec and pm_codeseg. If this has happened + * during our timeslice, our gdt code segment will + * be stale. So only allow the fault through if the + * faulting address is less then pm_hiexec and our + * gdt code segment is not stale. + */ + if (va <= pm->pm_hiexec && pm_cs == pm->pm_hiexec && + gdt_cs == pm->pm_hiexec) { return (0); } |