--- 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); }