1 /* 2 * ioperm.c - Test case for ioperm(2) 3 * Copyright (c) 2015 Andrew Lutomirski 4 */ 5 6 #define _GNU_SOURCE 7 #include <err.h> 8 #include <stdio.h> 9 #include <stdint.h> 10 #include <signal.h> 11 #include <setjmp.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <errno.h> 15 #include <unistd.h> 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 #include <stdbool.h> 19 #include <sched.h> 20 #include <sys/io.h> 21 22 static int nerrs = 0; 23 24 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 25 int flags) 26 { 27 struct sigaction sa; 28 memset(&sa, 0, sizeof(sa)); 29 sa.sa_sigaction = handler; 30 sa.sa_flags = SA_SIGINFO | flags; 31 sigemptyset(&sa.sa_mask); 32 if (sigaction(sig, &sa, 0)) 33 err(1, "sigaction"); 34 35 } 36 37 static void clearhandler(int sig) 38 { 39 struct sigaction sa; 40 memset(&sa, 0, sizeof(sa)); 41 sa.sa_handler = SIG_DFL; 42 sigemptyset(&sa.sa_mask); 43 if (sigaction(sig, &sa, 0)) 44 err(1, "sigaction"); 45 } 46 47 static jmp_buf jmpbuf; 48 49 static void sigsegv(int sig, siginfo_t *si, void *ctx_void) 50 { 51 siglongjmp(jmpbuf, 1); 52 } 53 54 static bool try_outb(unsigned short port) 55 { 56 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 57 if (sigsetjmp(jmpbuf, 1) != 0) { 58 return false; 59 } else { 60 asm volatile ("outb %%al, %w[port]" 61 : : [port] "Nd" (port), "a" (0)); 62 return true; 63 } 64 clearhandler(SIGSEGV); 65 } 66 67 static void expect_ok(unsigned short port) 68 { 69 if (!try_outb(port)) { 70 printf("[FAIL]\toutb to 0x%02hx failed\n", port); 71 exit(1); 72 } 73 74 printf("[OK]\toutb to 0x%02hx worked\n", port); 75 } 76 77 static void expect_gp(unsigned short port) 78 { 79 if (try_outb(port)) { 80 printf("[FAIL]\toutb to 0x%02hx worked\n", port); 81 exit(1); 82 } 83 84 printf("[OK]\toutb to 0x%02hx failed\n", port); 85 } 86 87 int main(void) 88 { 89 cpu_set_t cpuset; 90 CPU_ZERO(&cpuset); 91 CPU_SET(0, &cpuset); 92 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 93 err(1, "sched_setaffinity to CPU 0"); 94 95 expect_gp(0x80); 96 expect_gp(0xed); 97 98 /* 99 * Probe for ioperm support. Note that clearing ioperm bits 100 * works even as nonroot. 101 */ 102 printf("[RUN]\tenable 0x80\n"); 103 if (ioperm(0x80, 1, 1) != 0) { 104 printf("[OK]\tioperm(0x80, 1, 1) failed (%d) -- try running as root\n", 105 errno); 106 return 0; 107 } 108 expect_ok(0x80); 109 expect_gp(0xed); 110 111 printf("[RUN]\tdisable 0x80\n"); 112 if (ioperm(0x80, 1, 0) != 0) { 113 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 114 return 1; 115 } 116 expect_gp(0x80); 117 expect_gp(0xed); 118 119 /* Make sure that fork() preserves ioperm. */ 120 if (ioperm(0x80, 1, 1) != 0) { 121 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 122 return 1; 123 } 124 125 pid_t child = fork(); 126 if (child == -1) 127 err(1, "fork"); 128 129 if (child == 0) { 130 printf("[RUN]\tchild: check that we inherited permissions\n"); 131 expect_ok(0x80); 132 expect_gp(0xed); 133 return 0; 134 } else { 135 int status; 136 if (waitpid(child, &status, 0) != child || 137 !WIFEXITED(status)) { 138 printf("[FAIL]\tChild died\n"); 139 nerrs++; 140 } else if (WEXITSTATUS(status) != 0) { 141 printf("[FAIL]\tChild failed\n"); 142 nerrs++; 143 } else { 144 printf("[OK]\tChild succeeded\n"); 145 } 146 } 147 148 /* Test the capability checks. */ 149 150 printf("\tDrop privileges\n"); 151 if (setresuid(1, 1, 1) != 0) { 152 printf("[WARN]\tDropping privileges failed\n"); 153 return 0; 154 } 155 156 printf("[RUN]\tdisable 0x80\n"); 157 if (ioperm(0x80, 1, 0) != 0) { 158 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 159 return 1; 160 } 161 printf("[OK]\tit worked\n"); 162 163 printf("[RUN]\tenable 0x80 again\n"); 164 if (ioperm(0x80, 1, 1) == 0) { 165 printf("[FAIL]\tit succeeded but should have failed.\n"); 166 return 1; 167 } 168 printf("[OK]\tit failed\n"); 169 return 0; 170 } 171