1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * iopl.c - Test case for a Linux on Xen 64-bit bug 4 * Copyright (c) 2015 Andrew Lutomirski 5 */ 6 7 #define _GNU_SOURCE 8 #include <err.h> 9 #include <stdio.h> 10 #include <stdint.h> 11 #include <signal.h> 12 #include <setjmp.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <errno.h> 16 #include <unistd.h> 17 #include <sys/types.h> 18 #include <sys/wait.h> 19 #include <stdbool.h> 20 #include <sched.h> 21 #include <sys/io.h> 22 23 static int nerrs = 0; 24 25 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 26 int flags) 27 { 28 struct sigaction sa; 29 memset(&sa, 0, sizeof(sa)); 30 sa.sa_sigaction = handler; 31 sa.sa_flags = SA_SIGINFO | flags; 32 sigemptyset(&sa.sa_mask); 33 if (sigaction(sig, &sa, 0)) 34 err(1, "sigaction"); 35 36 } 37 38 static jmp_buf jmpbuf; 39 40 static void sigsegv(int sig, siginfo_t *si, void *ctx_void) 41 { 42 siglongjmp(jmpbuf, 1); 43 } 44 45 int main(void) 46 { 47 cpu_set_t cpuset; 48 CPU_ZERO(&cpuset); 49 CPU_SET(0, &cpuset); 50 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 51 err(1, "sched_setaffinity to CPU 0"); 52 53 /* Probe for iopl support. Note that iopl(0) works even as nonroot. */ 54 if (iopl(3) != 0) { 55 printf("[OK]\tiopl(3) failed (%d) -- try running as root\n", 56 errno); 57 return 0; 58 } 59 60 /* Restore our original state prior to starting the test. */ 61 if (iopl(0) != 0) 62 err(1, "iopl(0)"); 63 64 pid_t child = fork(); 65 if (child == -1) 66 err(1, "fork"); 67 68 if (child == 0) { 69 printf("\tchild: set IOPL to 3\n"); 70 if (iopl(3) != 0) 71 err(1, "iopl"); 72 73 printf("[RUN]\tchild: write to 0x80\n"); 74 asm volatile ("outb %%al, $0x80" : : "a" (0)); 75 76 return 0; 77 } else { 78 int status; 79 if (waitpid(child, &status, 0) != child || 80 !WIFEXITED(status)) { 81 printf("[FAIL]\tChild died\n"); 82 nerrs++; 83 } else if (WEXITSTATUS(status) != 0) { 84 printf("[FAIL]\tChild failed\n"); 85 nerrs++; 86 } else { 87 printf("[OK]\tChild succeeded\n"); 88 } 89 } 90 91 printf("[RUN]\tparent: write to 0x80 (should fail)\n"); 92 93 sethandler(SIGSEGV, sigsegv, 0); 94 if (sigsetjmp(jmpbuf, 1) != 0) { 95 printf("[OK]\twrite was denied\n"); 96 } else { 97 asm volatile ("outb %%al, $0x80" : : "a" (0)); 98 printf("[FAIL]\twrite was allowed\n"); 99 nerrs++; 100 } 101 102 /* Test the capability checks. */ 103 printf("\tiopl(3)\n"); 104 if (iopl(3) != 0) 105 err(1, "iopl(3)"); 106 107 printf("\tDrop privileges\n"); 108 if (setresuid(1, 1, 1) != 0) { 109 printf("[WARN]\tDropping privileges failed\n"); 110 goto done; 111 } 112 113 printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n"); 114 if (iopl(3) != 0) { 115 printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n"); 116 nerrs++; 117 } 118 119 printf("[RUN]\tiopl(0) unprivileged\n"); 120 if (iopl(0) != 0) { 121 printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n"); 122 nerrs++; 123 } 124 125 printf("[RUN]\tiopl(3) unprivileged\n"); 126 if (iopl(3) == 0) { 127 printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n"); 128 nerrs++; 129 } else { 130 printf("[OK]\tFailed as expected\n"); 131 } 132 133 done: 134 return nerrs ? 1 : 0; 135 } 136 137