⬢ DragonFlyBSD Kernel Audit
DF-5639 / fix.diff
← back to finding ↓ download raw
--- a/sys/platform/pc64/x86_64/tls.c
+++ b/sys/platform/pc64/x86_64/tls.c
@@ -101,6 +101,20 @@
 		return (EINVAL);
 
 	/*
+	 * The base is programmed directly into MSR_FSBASE / MSR_KGSBASE by
+	 * set_user_TLS().  WRMSR raises #GP(0) in kernel mode when handed
+	 * a non-canonical FS.base/GS.base value (Intel SDM Vol.3 17.17),
+	 * which would be a fatal kernel trap triggerable by any user, so
+	 * the base must be canonical before we accept it.
+	 *
+	 * Canonical 48-bit: bits 63..47 all zero or all one.  The add/shift
+	 * trick maps the two canonical ranges onto a value whose top 16
+	 * bits are clear.
+	 */
+	if (((uint64_t)(register_t)info.base + 0x0000800000000000ULL) >> 48)
+		return (EINVAL);
+
+	/*
 	 * For x86_64 we can only adjust FSBASE and GSBASE
 	 */
 	curthread->td_tls.info[i] = info;
--- a/sys/platform/pc64/x86_64/machdep.c
+++ b/sys/platform/pc64/x86_64/machdep.c
@@ -923,6 +923,22 @@
 int
 cpu_sanitize_tls(struct savetls *tls)
 {
+	int i;
+
+	/*
+	 * The emulated TLS descriptors supplied by a virtual kernel via
+	 * vmspace_ctl(VMSPACE_CTL_RUN) are programmed into MSR_FSBASE /
+	 * MSR_KGSBASE by set_user_TLS().  WRMSR raises #GP(0) in kernel
+	 * mode for a non-canonical value (Intel SDM Vol.3 17.17), so the
+	 * bases must be validated before the context switch installs them.
+	 */
+	for (i = 0; i < 2; ++i) {
+		uint64_t base = (uint64_t)(register_t)tls->info[i].base;
+
+		if ((base + 0x0000800000000000ULL) >> 48)
+			return (EINVAL);
+	}
+
 	return(0);
 }