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 return 0; 135 } else { 136 int status; 137 if (waitpid(child, &status, 0) != child || 138 !WIFEXITED(status)) { 139 printf("[FAIL]\tChild died\n"); 140 nerrs++; 141 } else if (WEXITSTATUS(status) != 0) { 142 printf("[FAIL]\tChild failed\n"); 143 nerrs++; 144 } else { 145 printf("[OK]\tChild succeeded\n"); 146 } 147 } 148 149 /* Test the capability checks. */ 150 151 printf("\tDrop privileges\n"); 152 if (setresuid(1, 1, 1) != 0) { 153 printf("[WARN]\tDropping privileges failed\n"); 154 return 0; 155 } 156 157 printf("[RUN]\tdisable 0x80\n"); 158 if (ioperm(0x80, 1, 0) != 0) { 159 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 160 return 1; 161 } 162 printf("[OK]\tit worked\n"); 163 164 printf("[RUN]\tenable 0x80 again\n"); 165 if (ioperm(0x80, 1, 1) == 0) { 166 printf("[FAIL]\tit succeeded but should have failed.\n"); 167 return 1; 168 } 169 printf("[OK]\tit failed\n"); 170 return 0; 171 } 172