1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ioperm.c - Test case for ioperm(2) 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(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(unsigned short port) 79 { 80 if (try_outb(port)) { 81 printf("[FAIL]\toutb to 0x%02hx worked\n", port); 82 exit(1); 83 } 84 85 printf("[OK]\toutb to 0x%02hx failed\n", port); 86 } 87 88 int main(void) 89 { 90 cpu_set_t cpuset; 91 CPU_ZERO(&cpuset); 92 CPU_SET(0, &cpuset); 93 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 94 err(1, "sched_setaffinity to CPU 0"); 95 96 expect_gp(0x80); 97 expect_gp(0xed); 98 99 /* 100 * Probe for ioperm support. Note that clearing ioperm bits 101 * works even as nonroot. 102 */ 103 printf("[RUN]\tenable 0x80\n"); 104 if (ioperm(0x80, 1, 1) != 0) { 105 printf("[OK]\tioperm(0x80, 1, 1) failed (%d) -- try running as root\n", 106 errno); 107 return 0; 108 } 109 expect_ok(0x80); 110 expect_gp(0xed); 111 112 printf("[RUN]\tdisable 0x80\n"); 113 if (ioperm(0x80, 1, 0) != 0) { 114 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 115 return 1; 116 } 117 expect_gp(0x80); 118 expect_gp(0xed); 119 120 /* Make sure that fork() preserves ioperm. */ 121 if (ioperm(0x80, 1, 1) != 0) { 122 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 123 return 1; 124 } 125 126 pid_t child = fork(); 127 if (child == -1) 128 err(1, "fork"); 129 130 if (child == 0) { 131 printf("[RUN]\tchild: check that we inherited permissions\n"); 132 expect_ok(0x80); 133 expect_gp(0xed); 134 printf("[RUN]\tchild: Extend permissions to 0x81\n"); 135 if (ioperm(0x81, 1, 1) != 0) { 136 printf("[FAIL]\tioperm(0x81, 1, 1) failed (%d)", errno); 137 return 1; 138 } 139 printf("[RUN]\tchild: Drop permissions to 0x80\n"); 140 if (ioperm(0x80, 1, 0) != 0) { 141 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 142 return 1; 143 } 144 expect_gp(0x80); 145 return 0; 146 } else { 147 int status; 148 if (waitpid(child, &status, 0) != child || 149 !WIFEXITED(status)) { 150 printf("[FAIL]\tChild died\n"); 151 nerrs++; 152 } else if (WEXITSTATUS(status) != 0) { 153 printf("[FAIL]\tChild failed\n"); 154 nerrs++; 155 } else { 156 printf("[OK]\tChild succeeded\n"); 157 } 158 } 159 160 /* Verify that the child dropping 0x80 did not affect the parent */ 161 printf("\tVerify that unsharing the bitmap worked\n"); 162 expect_ok(0x80); 163 164 /* Test the capability checks. */ 165 printf("\tDrop privileges\n"); 166 if (setresuid(1, 1, 1) != 0) { 167 printf("[WARN]\tDropping privileges failed\n"); 168 return 0; 169 } 170 171 printf("[RUN]\tdisable 0x80\n"); 172 if (ioperm(0x80, 1, 0) != 0) { 173 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 174 return 1; 175 } 176 printf("[OK]\tit worked\n"); 177 178 printf("[RUN]\tenable 0x80 again\n"); 179 if (ioperm(0x80, 1, 1) == 0) { 180 printf("[FAIL]\tit succeeded but should have failed.\n"); 181 return 1; 182 } 183 printf("[OK]\tit failed\n"); 184 return 0; 185 } 186