Print this page
9600 LDT still not happy under KPTI

*** 282,294 **** USEGD_SETBASE(usd, ssd->bo); USEGD_SETLIMIT(usd, ssd->ls); /* ! * set type, dpl and present bits. */ ! usd->usd_type = ssd->acc1; usd->usd_dpl = ssd->acc1 >> 5; usd->usd_p = ssd->acc1 >> (5 + 2); ASSERT(usd->usd_type >= SDT_MEMRO); ASSERT(usd->usd_dpl == SEL_UPL); --- 282,297 ---- USEGD_SETBASE(usd, ssd->bo); USEGD_SETLIMIT(usd, ssd->ls); /* ! * Set type, dpl and present bits. ! * ! * Force the "accessed" bit to on so that we don't run afoul of ! * KPTI. */ ! usd->usd_type = ssd->acc1 | SDT_A; usd->usd_dpl = ssd->acc1 >> 5; usd->usd_p = ssd->acc1 >> (5 + 2); ASSERT(usd->usd_type >= SDT_MEMRO); ASSERT(usd->usd_dpl == SEL_UPL);
*** 343,354 **** */ static void ldt_load(void) { #if defined(__xpv) ! xen_set_ldt(get_ssd_base(&curproc->p_ldt_desc), ! curproc->p_ldtlimit + 1); #else size_t len; system_desc_t desc; /* --- 346,356 ---- */ static void ldt_load(void) { #if defined(__xpv) ! xen_set_ldt(curproc->p_ldt, curproc->p_ldtlimit + 1); #else size_t len; system_desc_t desc; /*
*** 410,453 **** */ reset_sregs(); #endif ldt_unload(); ! cpu_fast_syscall_enable(NULL); } static void ldt_restorectx(proc_t *p) { ASSERT(p->p_ldt != NULL); ASSERT(p == curproc); ldt_load(); ! cpu_fast_syscall_disable(NULL); } /* ! * When a process with a private LDT execs, fast syscalls must be enabled for ! * the new process image. */ /* ARGSUSED */ static void ldt_freectx(proc_t *p, int isexec) { ! ASSERT(p->p_ldt); - if (isexec) { kpreempt_disable(); - cpu_fast_syscall_enable(NULL); - kpreempt_enable(); - } - - /* - * ldt_free() will free the memory used by the private LDT, reset the - * process's descriptor, and re-program the LDTR. - */ ldt_free(p); } /* * Install ctx op that ensures syscall/sysenter are disabled. * See comments below. --- 412,459 ---- */ reset_sregs(); #endif ldt_unload(); ! cpu_fast_syscall_enable(); } static void ldt_restorectx(proc_t *p) { ASSERT(p->p_ldt != NULL); ASSERT(p == curproc); ldt_load(); ! cpu_fast_syscall_disable(); } /* ! * At exec time, we need to clear up our LDT context and re-enable fast syscalls ! * for the new process image. ! * ! * The same is true for the other case, where we have: ! * ! * proc_exit() ! * ->exitpctx()->ldt_savectx() ! * ->freepctx()->ldt_freectx() ! * ! * Because pre-emption is not prevented between the two callbacks, we could have ! * come off CPU, and brought back LDT context when coming back on CPU via ! * ldt_restorectx(). */ /* ARGSUSED */ static void ldt_freectx(proc_t *p, int isexec) { ! ASSERT(p->p_ldt != NULL); ! ASSERT(p == curproc); kpreempt_disable(); ldt_free(p); + cpu_fast_syscall_enable(); + kpreempt_enable(); } /* * Install ctx op that ensures syscall/sysenter are disabled. * See comments below.
*** 498,508 **** setdscr(struct ssd *ssd) { ushort_t seli; /* selector index */ user_desc_t *ldp; /* descriptor pointer */ user_desc_t ndesc; /* new descriptor */ ! proc_t *pp = ttoproc(curthread); int rc = 0; /* * LDT segments: executable and data at DPL 3 only. */ --- 504,514 ---- setdscr(struct ssd *ssd) { ushort_t seli; /* selector index */ user_desc_t *ldp; /* descriptor pointer */ user_desc_t ndesc; /* new descriptor */ ! proc_t *pp = curproc; int rc = 0; /* * LDT segments: executable and data at DPL 3 only. */
*** 539,553 **** * thread to take the slow path (which doesn't make use * of sysenter or sysexit) back out. */ kpreempt_disable(); ldt_installctx(pp, NULL); ! cpu_fast_syscall_disable(NULL); ASSERT(curthread->t_post_sys != 0); kpreempt_enable(); } else if (seli > pp->p_ldtlimit) { /* * Increase size of ldt to include seli. */ ldt_grow(pp, seli); --- 545,560 ---- * thread to take the slow path (which doesn't make use * of sysenter or sysexit) back out. */ kpreempt_disable(); ldt_installctx(pp, NULL); ! cpu_fast_syscall_disable(); ASSERT(curthread->t_post_sys != 0); kpreempt_enable(); } else if (seli > pp->p_ldtlimit) { + ASSERT(pp->p_pctx != NULL); /* * Increase size of ldt to include seli. */ ldt_grow(pp, seli);
*** 645,658 **** return (EBUSY); } } /* ! * If acc1 is zero, clear the descriptor (including the 'present' bit) */ if (ssd->acc1 == 0) { rc = ldt_update_segd(ldp, &null_udesc); mutex_exit(&pp->p_ldtlock); return (rc); } /* --- 652,669 ---- return (EBUSY); } } /* ! * If acc1 is zero, clear the descriptor (including the 'present' bit). ! * Make sure we update the CPU-private copy of the LDT. */ if (ssd->acc1 == 0) { rc = ldt_update_segd(ldp, &null_udesc); + kpreempt_disable(); + ldt_load(); + kpreempt_enable(); mutex_exit(&pp->p_ldtlock); return (rc); } /*
*** 662,725 **** if (SI86SSD_DPL(ssd) != SEL_UPL) { mutex_exit(&pp->p_ldtlock); return (EINVAL); } - #if defined(__amd64) /* * Do not allow 32-bit applications to create 64-bit mode code * segments. */ if (SI86SSD_ISUSEG(ssd) && ((SI86SSD_TYPE(ssd) >> 3) & 1) == 1 && SI86SSD_ISLONG(ssd)) { mutex_exit(&pp->p_ldtlock); return (EINVAL); } - #endif /* __amd64 */ /* ! * Set up a code or data user segment descriptor. */ if (SI86SSD_ISUSEG(ssd)) { ssd_to_usd(ssd, &ndesc); rc = ldt_update_segd(ldp, &ndesc); mutex_exit(&pp->p_ldtlock); return (rc); } - #if defined(__i386) - /* - * Allow a call gate only if the destination is in the LDT - * and the system is running in 32-bit legacy mode. - * - * In long mode 32-bit call gates are redefined as 64-bit call - * gates and the hw enforces that the target code selector - * of the call gate must be 64-bit selector. A #gp fault is - * generated if otherwise. Since we do not allow 32-bit processes - * to switch themselves to 64-bits we never allow call gates - * on 64-bit system system. - */ - if (SI86SSD_TYPE(ssd) == SDT_SYSCGT && SELISLDT(ssd->ls)) { - - - ssd_to_sgd(ssd, (gate_desc_t *)&ndesc); - rc = ldt_update_segd(ldp, &ndesc); mutex_exit(&pp->p_ldtlock); - return (rc); - } - #endif /* __i386 */ - - mutex_exit(&pp->p_ldtlock); return (EINVAL); } /* ! * Allocate new LDT for process just large enough to contain seli. ! * Note we allocate and grow LDT in PAGESIZE chunks. We do this ! * to simplify the implementation and because on the hypervisor it's ! * required, since the LDT must live on pages that have PROT_WRITE ! * removed and which are given to the hypervisor. */ static void ldt_alloc(proc_t *pp, uint_t seli) { user_desc_t *ldt; --- 673,719 ---- if (SI86SSD_DPL(ssd) != SEL_UPL) { mutex_exit(&pp->p_ldtlock); return (EINVAL); } /* * Do not allow 32-bit applications to create 64-bit mode code * segments. */ if (SI86SSD_ISUSEG(ssd) && ((SI86SSD_TYPE(ssd) >> 3) & 1) == 1 && SI86SSD_ISLONG(ssd)) { mutex_exit(&pp->p_ldtlock); return (EINVAL); } /* ! * Set up a code or data user segment descriptor, making sure to update ! * the CPU-private copy of the LDT. */ if (SI86SSD_ISUSEG(ssd)) { ssd_to_usd(ssd, &ndesc); rc = ldt_update_segd(ldp, &ndesc); + kpreempt_disable(); + ldt_load(); + kpreempt_enable(); mutex_exit(&pp->p_ldtlock); return (rc); } mutex_exit(&pp->p_ldtlock); return (EINVAL); } /* ! * Allocate new LDT for process just large enough to contain seli. Note we ! * allocate and grow LDT in PAGESIZE chunks. We do this to simplify the ! * implementation and because on the hypervisor it's required, since the LDT ! * must live on pages that have PROT_WRITE removed and which are given to the ! * hypervisor. ! * ! * Note that we don't actually load the LDT into the current CPU here: it's done ! * later by our caller. */ static void ldt_alloc(proc_t *pp, uint_t seli) { user_desc_t *ldt;
*** 746,762 **** panic("ldt_alloc:xen_ldt_setprot(PROT_READ) failed"); #endif pp->p_ldt = ldt; pp->p_ldtlimit = nsels - 1; - set_syssegd(&pp->p_ldt_desc, ldt, ldtsz - 1, SDT_SYSLDT, SEL_KPL); - - if (pp == curproc) { - kpreempt_disable(); - ldt_load(); - kpreempt_enable(); - } } static void ldt_free(proc_t *pp) { --- 740,749 ----
*** 771,781 **** ASSERT(IS_P2ALIGNED(ldtsz, PAGESIZE)); pp->p_ldt = NULL; pp->p_ldtlimit = 0; - pp->p_ldt_desc = null_sdesc; mutex_exit(&pp->p_ldtlock); if (pp == curproc) { kpreempt_disable(); ldt_unload(); --- 758,767 ----
*** 836,845 **** --- 822,839 ---- mutex_exit(&cp->p_ldtlock); mutex_exit(&pp->p_ldtlock); } + /* + * Note that we don't actually load the LDT into the current CPU here: it's done + * later by our caller - unless we take an error. This works out because + * ldt_load() does a copy of ->p_ldt instead of directly loading it into the GDT + * (and therefore can't be using the freed old LDT), and by definition if the + * new entry didn't pass validation, then the proc shouldn't be referencing an + * entry in the extended region. + */ static void ldt_grow(proc_t *pp, uint_t seli) { user_desc_t *oldt, *nldt; uint_t nsels;
*** 886,904 **** #endif pp->p_ldt = nldt; pp->p_ldtlimit = nsels - 1; - /* - * write new ldt segment descriptor. - */ - set_syssegd(&pp->p_ldt_desc, nldt, nldtsz - 1, SDT_SYSLDT, SEL_KPL); - - /* - * load the new ldt. - */ - kpreempt_disable(); - ldt_load(); - kpreempt_enable(); - kmem_free(oldt, oldtsz); } --- 880,886 ----