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 void clearhandler(int sig) 39 { 40 struct sigaction sa; 41 memset(&sa, 0, sizeof(sa)); 42 sa.sa_handler = SIG_DFL; 43 sigemptyset(&sa.sa_mask); 44 if (sigaction(sig, &sa, 0)) 45 err(1, "sigaction"); 46 } 47 48 static jmp_buf jmpbuf; 49 50 static void sigsegv(int sig, siginfo_t *si, void *ctx_void) 51 { 52 siglongjmp(jmpbuf, 1); 53 } 54 55 static bool try_outb(unsigned short port) 56 { 57 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 58 if (sigsetjmp(jmpbuf, 1) != 0) { 59 return false; 60 } else { 61 asm volatile ("outb %%al, %w[port]" 62 : : [port] "Nd" (port), "a" (0)); 63 return true; 64 } 65 clearhandler(SIGSEGV); 66 } 67 68 static void expect_ok_outb(unsigned short port) 69 { 70 if (!try_outb(port)) { 71 printf("[FAIL]\toutb to 0x%02hx failed\n", port); 72 exit(1); 73 } 74 75 printf("[OK]\toutb to 0x%02hx worked\n", port); 76 } 77 78 static void expect_gp_outb(unsigned short port) 79 { 80 if (try_outb(port)) { 81 printf("[FAIL]\toutb to 0x%02hx worked\n", port); 82 nerrs++; 83 } 84 85 printf("[OK]\toutb to 0x%02hx failed\n", port); 86 } 87 88 #define RET_FAULTED 0 89 #define RET_FAIL 1 90 #define RET_EMUL 2 91 92 static int try_cli(void) 93 { 94 unsigned long flags; 95 96 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 97 if (sigsetjmp(jmpbuf, 1) != 0) { 98 return RET_FAULTED; 99 } else { 100 asm volatile("cli; pushf; pop %[flags]" 101 : [flags] "=rm" (flags)); 102 103 /* X86_FLAGS_IF */ 104 if (!(flags & (1 << 9))) 105 return RET_FAIL; 106 else 107 return RET_EMUL; 108 } 109 clearhandler(SIGSEGV); 110 } 111 112 static int try_sti(bool irqs_off) 113 { 114 unsigned long flags; 115 116 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 117 if (sigsetjmp(jmpbuf, 1) != 0) { 118 return RET_FAULTED; 119 } else { 120 asm volatile("sti; pushf; pop %[flags]" 121 : [flags] "=rm" (flags)); 122 123 /* X86_FLAGS_IF */ 124 if (irqs_off && (flags & (1 << 9))) 125 return RET_FAIL; 126 else 127 return RET_EMUL; 128 } 129 clearhandler(SIGSEGV); 130 } 131 132 static void expect_gp_sti(bool irqs_off) 133 { 134 int ret = try_sti(irqs_off); 135 136 switch (ret) { 137 case RET_FAULTED: 138 printf("[OK]\tSTI faulted\n"); 139 break; 140 case RET_EMUL: 141 printf("[OK]\tSTI NOPped\n"); 142 break; 143 default: 144 printf("[FAIL]\tSTI worked\n"); 145 nerrs++; 146 } 147 } 148 149 /* 150 * Returns whether it managed to disable interrupts. 151 */ 152 static bool test_cli(void) 153 { 154 int ret = try_cli(); 155 156 switch (ret) { 157 case RET_FAULTED: 158 printf("[OK]\tCLI faulted\n"); 159 break; 160 case RET_EMUL: 161 printf("[OK]\tCLI NOPped\n"); 162 break; 163 default: 164 printf("[FAIL]\tCLI worked\n"); 165 nerrs++; 166 return true; 167 } 168 169 return false; 170 } 171 172 int main(void) 173 { 174 cpu_set_t cpuset; 175 176 CPU_ZERO(&cpuset); 177 CPU_SET(0, &cpuset); 178 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 179 err(1, "sched_setaffinity to CPU 0"); 180 181 /* Probe for iopl support. Note that iopl(0) works even as nonroot. */ 182 switch(iopl(3)) { 183 case 0: 184 break; 185 case -ENOSYS: 186 printf("[OK]\tiopl() nor supported\n"); 187 return 0; 188 default: 189 printf("[OK]\tiopl(3) failed (%d) -- try running as root\n", 190 errno); 191 return 0; 192 } 193 194 /* Make sure that CLI/STI are blocked even with IOPL level 3 */ 195 expect_gp_sti(test_cli()); 196 expect_ok_outb(0x80); 197 198 /* Establish an I/O bitmap to test the restore */ 199 if (ioperm(0x80, 1, 1) != 0) 200 err(1, "ioperm(0x80, 1, 1) failed\n"); 201 202 /* Restore our original state prior to starting the fork test. */ 203 if (iopl(0) != 0) 204 err(1, "iopl(0)"); 205 206 /* 207 * Verify that IOPL emulation is disabled and the I/O bitmap still 208 * works. 209 */ 210 expect_ok_outb(0x80); 211 expect_gp_outb(0xed); 212 /* Drop the I/O bitmap */ 213 if (ioperm(0x80, 1, 0) != 0) 214 err(1, "ioperm(0x80, 1, 0) failed\n"); 215 216 pid_t child = fork(); 217 if (child == -1) 218 err(1, "fork"); 219 220 if (child == 0) { 221 printf("\tchild: set IOPL to 3\n"); 222 if (iopl(3) != 0) 223 err(1, "iopl"); 224 225 printf("[RUN]\tchild: write to 0x80\n"); 226 asm volatile ("outb %%al, $0x80" : : "a" (0)); 227 228 return 0; 229 } else { 230 int status; 231 if (waitpid(child, &status, 0) != child || 232 !WIFEXITED(status)) { 233 printf("[FAIL]\tChild died\n"); 234 nerrs++; 235 } else if (WEXITSTATUS(status) != 0) { 236 printf("[FAIL]\tChild failed\n"); 237 nerrs++; 238 } else { 239 printf("[OK]\tChild succeeded\n"); 240 } 241 } 242 243 printf("[RUN]\tparent: write to 0x80 (should fail)\n"); 244 245 expect_gp_outb(0x80); 246 expect_gp_sti(test_cli()); 247 248 /* Test the capability checks. */ 249 printf("\tiopl(3)\n"); 250 if (iopl(3) != 0) 251 err(1, "iopl(3)"); 252 253 printf("\tDrop privileges\n"); 254 if (setresuid(1, 1, 1) != 0) { 255 printf("[WARN]\tDropping privileges failed\n"); 256 goto done; 257 } 258 259 printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n"); 260 if (iopl(3) != 0) { 261 printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n"); 262 nerrs++; 263 } 264 265 printf("[RUN]\tiopl(0) unprivileged\n"); 266 if (iopl(0) != 0) { 267 printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n"); 268 nerrs++; 269 } 270 271 printf("[RUN]\tiopl(3) unprivileged\n"); 272 if (iopl(3) == 0) { 273 printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n"); 274 nerrs++; 275 } else { 276 printf("[OK]\tFailed as expected\n"); 277 } 278 279 done: 280 return nerrs ? 1 : 0; 281 } 282