--- old/usr/src/pkg/manifests/system-test-ostest.mf	Thu Jun 14 17:15:57 2018
+++ new/usr/src/pkg/manifests/system-test-ostest.mf	Thu Jun 14 17:15:57 2018
@@ -12,7 +12,7 @@
 #
 # Copyright (c) 2012, 2016 by Delphix. All rights reserved.
 # Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
-# Copyright 2017 Joyent, Inc.
+# Copyright 2018 Joyent, Inc.
 #
 
 set name=pkg.fmri value=pkg:/system/test/ostest@$(PKGVERS)
@@ -26,6 +26,7 @@
 dir path=opt/os-tests/runfiles
 dir path=opt/os-tests/tests
 dir path=opt/os-tests/tests/file-locking
+$(i386_ONLY)dir path=opt/os-tests/tests/i386
 dir path=opt/os-tests/tests/pf_key
 dir path=opt/os-tests/tests/sdevfs
 dir path=opt/os-tests/tests/secflags
@@ -40,6 +41,7 @@
 file path=opt/os-tests/tests/file-locking/acquire-lock.64 mode=0555
 file path=opt/os-tests/tests/file-locking/runtests.32 mode=0555
 file path=opt/os-tests/tests/file-locking/runtests.64 mode=0555
+$(i386_ONLY)file path=opt/os-tests/tests/i386/ldt mode=0555
 file path=opt/os-tests/tests/pf_key/acquire-compare mode=0555
 file path=opt/os-tests/tests/pf_key/acquire-spray mode=0555
 file path=opt/os-tests/tests/pf_key/eacq-enabler mode=0555
--- old/usr/src/test/os-tests/runfiles/default.run	Thu Jun 14 17:15:57 2018
+++ new/usr/src/test/os-tests/runfiles/default.run	Thu Jun 14 17:15:57 2018
@@ -11,7 +11,7 @@
 
 #
 # Copyright (c) 2012 by Delphix. All rights reserved.
-# Copyright 2017 Joyent, Inc.
+# Copyright 2018 Joyent, Inc.
 #
 
 [DEFAULT]
@@ -62,3 +62,8 @@
 [/opt/os-tests/tests/pf_key]
 user = root
 tests = ['acquire-compare', 'acquire-spray']
+
+[/opt/os-tests/tests/i386]
+user = root
+arch = i86pc
+tests = ['ldt']
--- old/usr/src/test/os-tests/tests/Makefile	Thu Jun 14 17:15:58 2018
+++ new/usr/src/test/os-tests/tests/Makefile	Thu Jun 14 17:15:58 2018
@@ -11,10 +11,12 @@
 
 #
 # Copyright (c) 2012, 2016 by Delphix. All rights reserved.
-# Copyright 2017 Joyent, Inc.
+# Copyright 2018 Joyent, Inc.
 #
 
+SUBDIRS_i386 = i386
+
 SUBDIRS = poll secflags sigqueue spoof-ras sdevfs sockfs stress file-locking \
-	pf_key
+	pf_key $(SUBDIRS_$(MACH))
 
 include $(SRC)/test/Makefile.com
--- /dev/null	Thu Jun 14 17:15:58 2018
+++ new/usr/src/test/os-tests/tests/i386/Makefile	Thu Jun 14 17:15:58 2018
@@ -0,0 +1,46 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Joyent, Inc.
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+PROG +=	ldt
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests/i386
+
+CSTD = $(CSTD_GNU99)
+
+CMDS = $(PROG:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+all: $(PROG)
+
+install: all $(CMDS)
+
+lint:
+
+clobber: clean
+	-$(RM) $(PROG)
+
+clean:
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(TESTDIR):
+	$(INS.dir)
+
+$(TESTDIR)/%: %
+	$(INS.file)
--- /dev/null	Thu Jun 14 17:15:58 2018
+++ new/usr/src/test/os-tests/tests/i386/ldt.c	Thu Jun 14 17:15:58 2018
@@ -0,0 +1,80 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sysi86.h>
+#include <sys/segments.h>
+#include <sys/segment.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <err.h>
+
+char foo[4096];
+
+static void *
+donothing(void *nothing)
+{
+	sleep(5);
+	return (NULL);
+}
+
+int
+main(void)
+{
+	pthread_t tid;
+
+	/*
+	 * This first is similar to what sbcl does in some variants.  Note the
+	 * SDT_MEMRW (not SDT_MEMRWA) so we check that the kernel is forcing the
+	 * 'accessed' bit too.
+	 */
+	int sel = SEL_LDT(7);
+
+	struct ssd ssd = { sel, (unsigned long)&foo, 4096,
+	    SDT_MEMRW | (SEL_UPL << 5) | (1 << 7), 0x4 };
+
+	if (sysi86(SI86DSCR, &ssd) < 0)
+		err(-1, "failed to setup segment");
+
+	__asm__ __volatile__("mov %0, %%fs" : : "r" (sel));
+
+	ssd.acc1 = 0;
+
+	if (sysi86(SI86DSCR, &ssd) == 0)
+		errx(-1, "removed in-use segment?");
+
+	__asm__ __volatile__("mov %0, %%fs" : : "r" (0));
+
+	if (sysi86(SI86DSCR, &ssd) < 0)
+		err(-1, "failed to remove segment");
+
+	for (int i = 0; i < MAXNLDT; i++) {
+		ssd.sel = SEL_LDT(i);
+		(void) sysi86(SI86DSCR, &ssd);
+	}
+
+	for (int i = 0; i < 10; i++)
+		pthread_create(&tid, NULL, donothing, NULL);
+
+	if (forkall() == 0) {
+		sleep(2);
+		_exit(0);
+	}
+
+	sleep(6);
+	return (0);
+}
--- old/usr/src/test/test-runner/cmd/run	Thu Jun 14 17:15:58 2018
+++ new/usr/src/test/test-runner/cmd/run	Thu Jun 14 17:15:58 2018
@@ -14,11 +14,13 @@
 #
 # Copyright (c) 2012, 2016 by Delphix. All rights reserved.
 # Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
+# Copyright 2018 Joyent, Inc.
 #
 
 import ConfigParser
 import os
 import logging
+import platform
 from logging.handlers import WatchedFileHandler
 from datetime import datetime
 from optparse import OptionParser
@@ -564,6 +566,10 @@
         self.outputdir = os.path.join(self.outputdir, self.timestamp)
 
         for section in config.sections():
+            if ('arch' in config.options(section) and
+                platform.machine() != config.get(section, 'arch')):
+                continue
+
             if 'tests' in config.options(section):
                 testgroup = TestGroup(section)
                 for prop in TestGroup.props:
--- old/usr/src/uts/common/sys/proc.h	Thu Jun 14 17:15:58 2018
+++ new/usr/src/uts/common/sys/proc.h	Thu Jun 14 17:15:58 2018
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Joyent, Inc.
+ * Copyright 2018 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -301,7 +301,8 @@
 	 */
 	kmutex_t	p_ldtlock;	/* protects the following fields */
 	user_desc_t	*p_ldt;		/* Pointer to private LDT */
-	system_desc_t	p_ldt_desc;	/* segment descriptor for private LDT */
+	uint64_t	p_unused1;	/* no longer used */
+	uint64_t	p_unused2;	/* no longer used */
 	ushort_t	p_ldtlimit;	/* highest selector used */
 #endif
 	size_t p_swrss;			/* resident set size before last swap */
--- old/usr/src/uts/i86pc/ml/offsets.in	Thu Jun 14 17:15:58 2018
+++ new/usr/src/uts/i86pc/ml/offsets.in	Thu Jun 14 17:15:58 2018
@@ -75,8 +75,6 @@
 	p_as
 	p_lockp
 	p_user
-	p_ldt
-	p_ldt_desc
 	p_model
 	p_pctx
 	p_agenttp
--- old/usr/src/uts/i86pc/os/mlsetup.c	Thu Jun 14 17:15:59 2018
+++ new/usr/src/uts/i86pc/os/mlsetup.c	Thu Jun 14 17:15:59 2018
@@ -362,11 +362,6 @@
 	CPU->cpu_pri = 12;		/* initial PIL for the boot CPU */
 
 	/*
-	 * The kernel doesn't use LDTs unless a process explicitly requests one.
-	 */
-	p0.p_ldt_desc = null_sdesc;
-
-	/*
 	 * Initialize thread/cpu microstate accounting
 	 */
 	init_mstate(&t0, LMS_SYSTEM);
--- old/usr/src/uts/i86pc/os/mp_startup.c	Thu Jun 14 17:15:59 2018
+++ new/usr/src/uts/i86pc/os/mp_startup.c	Thu Jun 14 17:15:59 2018
@@ -2054,9 +2054,8 @@
  * syscall features.
  */
 
-/*ARGSUSED*/
 void
-cpu_fast_syscall_disable(void *arg)
+cpu_fast_syscall_disable(void)
 {
 	if (is_x86_feature(x86_featureset, X86FSET_MSR) &&
 	    is_x86_feature(x86_featureset, X86FSET_SEP))
@@ -2066,9 +2065,8 @@
 		cpu_asysc_disable();
 }
 
-/*ARGSUSED*/
 void
-cpu_fast_syscall_enable(void *arg)
+cpu_fast_syscall_enable(void)
 {
 	if (is_x86_feature(x86_featureset, X86FSET_MSR) &&
 	    is_x86_feature(x86_featureset, X86FSET_SEP))
--- old/usr/src/uts/intel/ia32/os/desctbls.c	Thu Jun 14 17:15:59 2018
+++ new/usr/src/uts/intel/ia32/os/desctbls.c	Thu Jun 14 17:15:59 2018
@@ -178,8 +178,6 @@
  * can understand.
  */
 
-#if defined(__amd64)
-
 /*
  * In long mode we have the new L or long mode attribute bit
  * for code segments. Only the conforming bit in type is used along
@@ -193,6 +191,8 @@
     uint_t type, uint_t dpl, uint_t gran, uint_t defopsz)
 {
 	ASSERT(lmode == SDP_SHORT || lmode == SDP_LONG);
+	/* This should never be a "system" segment. */
+	ASSERT3U(type & SDT_S, !=, 0);
 
 	/*
 	 * 64-bit long mode.
@@ -205,6 +205,14 @@
 		 */
 		dp->usd_def32 = defopsz;	/* 0 = 16, 1 = 32-bit ops */
 
+	/*
+	 * We should always set the "accessed" bit (SDT_A), otherwise the CPU
+	 * will write to the GDT whenever we change segment registers around.
+	 * With KPTI on, the GDT is read-only in the user page table, which
+	 * causes crashes if we don't set this.
+	 */
+	ASSERT3U(type & SDT_A, !=, 0);
+
 	dp->usd_long = lmode;	/* 64-bit mode */
 	dp->usd_type = type;
 	dp->usd_dpl = dpl;
@@ -218,37 +226,10 @@
 	dp->usd_hilimit = (uintptr_t)size >> 16;
 }
 
-#elif defined(__i386)
-
 /*
- * Install user segment descriptor for code and data.
- */
-void
-set_usegd(user_desc_t *dp, void *base, size_t size, uint_t type,
-    uint_t dpl, uint_t gran, uint_t defopsz)
-{
-	dp->usd_lolimit = size;
-	dp->usd_hilimit = (uintptr_t)size >> 16;
-
-	dp->usd_lobase = (uintptr_t)base;
-	dp->usd_midbase = (uintptr_t)base >> 16;
-	dp->usd_hibase = (uintptr_t)base >> (16 + 8);
-
-	dp->usd_type = type;
-	dp->usd_dpl = dpl;
-	dp->usd_p = 1;
-	dp->usd_def32 = defopsz;	/* 0 = 16, 1 = 32 bit operands */
-	dp->usd_gran = gran;		/* 0 = bytes, 1 = pages */
-}
-
-#endif	/* __i386 */
-
-/*
  * Install system segment descriptor for LDT and TSS segments.
  */
 
-#if defined(__amd64)
-
 void
 set_syssegd(system_desc_t *dp, void *base, size_t size, uint_t type,
     uint_t dpl)
@@ -281,39 +262,6 @@
 	return ((void *)base);
 }
 
-#elif defined(__i386)
-
-void
-set_syssegd(system_desc_t *dp, void *base, size_t size, uint_t type,
-    uint_t dpl)
-{
-	dp->ssd_lolimit = size;
-	dp->ssd_hilimit = (uintptr_t)size >> 16;
-
-	dp->ssd_lobase = (uintptr_t)base;
-	dp->ssd_midbase = (uintptr_t)base >> 16;
-	dp->ssd_hibase = (uintptr_t)base >> (16 + 8);
-
-	dp->ssd_type = type;
-	dp->ssd_zero = 0;	/* must be zero */
-	dp->ssd_dpl = dpl;
-	dp->ssd_p = 1;
-	dp->ssd_gran = 0;	/* force byte units */
-}
-
-void *
-get_ssd_base(system_desc_t *dp)
-{
-	uintptr_t	base;
-
-	base = (uintptr_t)dp->ssd_lobase |
-	    (uintptr_t)dp->ssd_midbase << 16 |
-	    (uintptr_t)dp->ssd_hibase << (16 + 8);
-	return ((void *)base);
-}
-
-#endif	/* __i386 */
-
 /*
  * Install gate segment descriptor for interrupt, trap, call and task gates.
  *
@@ -391,8 +339,23 @@
 void
 gdt_update_usegd(uint_t sidx, user_desc_t *udp)
 {
-#if defined(__xpv)
+#if defined(DEBUG)
+	/* This should never be a "system" segment, but it might be null. */
+	if (udp->usd_p != 0 || udp->usd_type != 0) {
+		ASSERT3U(udp->usd_type & SDT_S, !=, 0);
+	}
+	/*
+	 * We should always set the "accessed" bit (SDT_A), otherwise the CPU
+	 * will write to the GDT whenever we change segment registers around.
+	 * With KPTI on, the GDT is read-only in the user page table, which
+	 * causes crashes if we don't set this.
+	 */
+	if (udp->usd_p != 0 || udp->usd_type != 0) {
+		ASSERT3U(udp->usd_type & SDT_A, !=, 0);
+	}
+#endif
 
+#if defined(__xpv)
 	uint64_t dpa = CPU->cpu_m.mcpu_gdtpa + sizeof (*udp) * sidx;
 
 	if (HYPERVISOR_update_descriptor(pa_to_ma(dpa), *(uint64_t *)udp))
@@ -399,9 +362,7 @@
 		panic("gdt_update_usegd: HYPERVISOR_update_descriptor");
 
 #else	/* __xpv */
-
 	CPU->cpu_gdt[sidx] = *udp;
-
 #endif	/* __xpv */
 }
 
@@ -412,8 +373,23 @@
 int
 ldt_update_segd(user_desc_t *ldp, user_desc_t *udp)
 {
-#if defined(__xpv)
+#if defined(DEBUG)
+	/* This should never be a "system" segment, but it might be null. */
+	if (udp->usd_p != 0 || udp->usd_type != 0) {
+		ASSERT3U(udp->usd_type & SDT_S, !=, 0);
+	}
+	/*
+	 * We should always set the "accessed" bit (SDT_A), otherwise the CPU
+	 * will write to the LDT whenever we change segment registers around.
+	 * With KPTI on, the LDT is read-only in the user page table, which
+	 * causes crashes if we don't set this.
+	 */
+	if (udp->usd_p != 0 || udp->usd_type != 0) {
+		ASSERT3U(udp->usd_type & SDT_A, !=, 0);
+	}
+#endif
 
+#if defined(__xpv)
 	uint64_t dpa;
 
 	dpa = mmu_ptob(hat_getpfnum(kas.a_hat, (caddr_t)ldp)) |
@@ -427,7 +403,6 @@
 		return (EINVAL);
 
 #else	/* __xpv */
-
 	*ldp = *udp;
 
 #endif	/* __xpv */
--- old/usr/src/uts/intel/ia32/os/sysi86.c	Thu Jun 14 17:15:59 2018
+++ new/usr/src/uts/intel/ia32/os/sysi86.c	Thu Jun 14 17:15:59 2018
@@ -284,9 +284,12 @@
 	USEGD_SETLIMIT(usd, ssd->ls);
 
 	/*
-	 * set type, dpl and present bits.
+	 * 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;
+	usd->usd_type = ssd->acc1 | SDT_A;
 	usd->usd_dpl = ssd->acc1 >> 5;
 	usd->usd_p = ssd->acc1 >> (5 + 2);
 
@@ -345,8 +348,7 @@
 ldt_load(void)
 {
 #if defined(__xpv)
-	xen_set_ldt(get_ssd_base(&curproc->p_ldt_desc),
-	    curproc->p_ldtlimit + 1);
+	xen_set_ldt(curproc->p_ldt, curproc->p_ldtlimit + 1);
 #else
 	size_t len;
 	system_desc_t desc;
@@ -412,7 +414,7 @@
 #endif
 
 	ldt_unload();
-	cpu_fast_syscall_enable(NULL);
+	cpu_fast_syscall_enable();
 }
 
 static void
@@ -422,30 +424,34 @@
 	ASSERT(p == curproc);
 
 	ldt_load();
-	cpu_fast_syscall_disable(NULL);
+	cpu_fast_syscall_disable();
 }
 
 /*
- * When a process with a private LDT execs, fast syscalls must be enabled for
- * the new process image.
+ * 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);
+	ASSERT(p->p_ldt != NULL);
+	ASSERT(p == curproc);
 
-	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.
-	 */
+	kpreempt_disable();
 	ldt_free(p);
+	cpu_fast_syscall_enable();
+	kpreempt_enable();
 }
 
 /*
@@ -497,10 +503,10 @@
 int
 setdscr(struct ssd *ssd)
 {
-	ushort_t seli; 		/* selector index */
+	ushort_t seli;		/* selector index */
 	user_desc_t *ldp;	/* descriptor pointer */
 	user_desc_t ndesc;	/* new descriptor */
-	proc_t	*pp = ttoproc(curthread);
+	proc_t	*pp = curproc;
 	int	rc = 0;
 
 	/*
@@ -541,11 +547,12 @@
 		 */
 		kpreempt_disable();
 		ldt_installctx(pp, NULL);
-		cpu_fast_syscall_disable(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.
@@ -647,10 +654,14 @@
 	}
 
 	/*
-	 * If acc1 is zero, clear the descriptor (including the 'present' bit)
+	 * 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);
 	}
@@ -664,7 +675,6 @@
 		return (EINVAL);
 	}
 
-#if defined(__amd64)
 	/*
 	 * Do not allow 32-bit applications to create 64-bit mode code
 	 * segments.
@@ -674,50 +684,34 @@
 		mutex_exit(&pp->p_ldtlock);
 		return (EINVAL);
 	}
-#endif /* __amd64 */
 
 	/*
-	 * Set up a code or data user segment descriptor.
+	 * 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);
 	}
 
-#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.
+ * 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)
@@ -748,13 +742,6 @@
 
 	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
@@ -773,7 +760,6 @@
 
 	pp->p_ldt = NULL;
 	pp->p_ldtlimit = 0;
-	pp->p_ldt_desc = null_sdesc;
 	mutex_exit(&pp->p_ldtlock);
 
 	if (pp == curproc) {
@@ -838,6 +824,14 @@
 
 }
 
+/*
+ * 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)
 {
@@ -888,17 +882,5 @@
 	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);
 }
--- old/usr/src/uts/intel/sys/segments.h	Thu Jun 14 17:15:59 2018
+++ new/usr/src/uts/intel/sys/segments.h	Thu Jun 14 17:16:00 2018
@@ -482,6 +482,10 @@
 #define	SDT_MEMERC	30	/* execute read conforming */
 #define	SDT_MEMERAC	31	/* execute read accessed conforming */
 
+/* These bits are within the "type" field, like the values above. */
+#define	SDT_A		0x01	/* accessed bit */
+#define	SDT_S		0x10	/* S-bit at the top of "type" for usegs */
+
 /*
  * Entries in the Interrupt Descriptor Table (IDT)
  */
--- old/usr/src/uts/intel/sys/x86_archext.h	Thu Jun 14 17:16:00 2018
+++ new/usr/src/uts/intel/sys/x86_archext.h	Thu Jun 14 17:16:00 2018
@@ -751,8 +751,8 @@
 
 extern void mtrr_sync(void);
 
-extern void cpu_fast_syscall_enable(void *);
-extern void cpu_fast_syscall_disable(void *);
+extern void cpu_fast_syscall_enable(void);
+extern void cpu_fast_syscall_disable(void);
 
 struct cpu;
 
