DF-5673 / sysarch_surface.c
/* * sysarch_surface.c — unprivileged probe of the pc64 sysarch(2) surface * and the IOPL state machine's user-reachable fences. * * Run as: unprivileged user (maxx). * * Checks: * 1. syscall 165 (sysarch) with assorted cmd values — on pc64 * sys/platform/pc64/x86_64/sysarch.c:46-49 is a hard EOPNOTSUPP stub: * no dispatch, no copyin, no IOPERM/LDT handling at all. * 2. open("/dev/io") must fail EPERM for unprivileged users * (kern/kern_memio.c:184-195 gates with SYSCAP_RESTRICTEDROOT). * 3. Forge attempt: setcontext() with uc_mcontext.mc_rflags |= PSL_IOPL * (routes through SYS_sigreturn). machdep.c:1023 EFL_SECURE must * reject it with EINVAL (PSL_USERCHANGE excludes PSL_IOPL, psl.h:81). * 4. Report actual IOPL via pushfq at each step. */ #include <sys/types.h> #include <sys/syscall.h> #include <sys/ucontext.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #ifndef SYS_sysarch #define SYS_sysarch 165 #endif static unsigned iopl_cur(void) { unsigned long rf; __asm __volatile("pushfq; popq %0" : "=r"(rf)); return (unsigned)((rf >> 12) & 3); } int main(void) { int i, r, fd; long cmds[] = { 0, 1, 2, 3, 101 /* IOPERM-ish */, 102, 103, 0x7f /* LDT-ish */, 0x1234 }; setvbuf(stdout, NULL, _IONBF, 0); printf("== sysarch surface probe (uid=%d) ==\n", getuid()); for (i = 0; i < (int)(sizeof(cmds) / sizeof(cmds[0])); i++) { errno = 0; r = syscall(SYS_sysarch, cmds[i], (void *)0x4141414141414141UL); printf("sysarch(cmd=%5ld, garbage ptr) = %d errno=%d (%s)\n", cmds[i], r, errno, strerror(errno)); } printf("\n== /dev/io open as unprivileged ==\n"); fd = open("/dev/io", O_RDWR); if (fd < 0) printf("open(/dev/io, O_RDWR) = -1 errno=%d (%s) [expected EPERM/ACCE]\n", errno, strerror(errno)); else { printf("open(/dev/io, O_RDWR) = %d *** UNPRIV OPEN SUCCEEDED ***\n", fd); close(fd); } fd = open("/dev/io", O_RDONLY); if (fd < 0) printf("open(/dev/io, O_RDONLY)= -1 errno=%d (%s) [expected EPERM/ACCE]\n", errno, strerror(errno)); else { printf("open(/dev/io, O_RDONLY)= %d *** UNPRIV OPEN SUCCEEDED ***\n", fd); close(fd); } printf("\n== sigreturn rflags forge attempt (expect EINVAL, IOPL stays 0) ==\n"); { ucontext_t uc; getcontext(&uc); uc.uc_mcontext.mc_rflags |= 0x3000UL; /* PSL_IOPL */ errno = 0; r = setcontext(&uc); /* returns only on failure */ printf("setcontext(forged mc_rflags|0x3000) returned %d errno=%d (%s)\n", r, errno, strerror(errno)); printf("IOPL now = %u (want 0)\n", iopl_cur()); } printf("\n== final IOPL = %u ==\n", iopl_cur()); return 0; } |