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 #include "helpers.h" 24 25 static int nerrs = 0; 26 27 static jmp_buf jmpbuf; 28 29 static void sigsegv(int sig, siginfo_t *si, void *ctx_void) 30 { 31 siglongjmp(jmpbuf, 1); 32 } 33 34 static bool try_outb(unsigned short port) 35 { 36 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 37 if (sigsetjmp(jmpbuf, 1) != 0) { 38 return false; 39 } else { 40 asm volatile ("outb %%al, %w[port]" 41 : : [port] "Nd" (port), "a" (0)); 42 return true; 43 } 44 clearhandler(SIGSEGV); 45 } 46 47 static void expect_ok(unsigned short port) 48 { 49 if (!try_outb(port)) { 50 printf("[FAIL]\toutb to 0x%02hx failed\n", port); 51 exit(1); 52 } 53 54 printf("[OK]\toutb to 0x%02hx worked\n", port); 55 } 56 57 static void expect_gp(unsigned short port) 58 { 59 if (try_outb(port)) { 60 printf("[FAIL]\toutb to 0x%02hx worked\n", port); 61 exit(1); 62 } 63 64 printf("[OK]\toutb to 0x%02hx failed\n", port); 65 } 66 67 int main(void) 68 { 69 cpu_set_t cpuset; 70 CPU_ZERO(&cpuset); 71 CPU_SET(0, &cpuset); 72 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 73 err(1, "sched_setaffinity to CPU 0"); 74 75 expect_gp(0x80); 76 expect_gp(0xed); 77 78 /* 79 * Probe for ioperm support. Note that clearing ioperm bits 80 * works even as nonroot. 81 */ 82 printf("[RUN]\tenable 0x80\n"); 83 if (ioperm(0x80, 1, 1) != 0) { 84 printf("[OK]\tioperm(0x80, 1, 1) failed (%d) -- try running as root\n", 85 errno); 86 return 0; 87 } 88 expect_ok(0x80); 89 expect_gp(0xed); 90 91 printf("[RUN]\tdisable 0x80\n"); 92 if (ioperm(0x80, 1, 0) != 0) { 93 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 94 return 1; 95 } 96 expect_gp(0x80); 97 expect_gp(0xed); 98 99 /* Make sure that fork() preserves ioperm. */ 100 if (ioperm(0x80, 1, 1) != 0) { 101 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 102 return 1; 103 } 104 105 pid_t child = fork(); 106 if (child == -1) 107 err(1, "fork"); 108 109 if (child == 0) { 110 printf("[RUN]\tchild: check that we inherited permissions\n"); 111 expect_ok(0x80); 112 expect_gp(0xed); 113 printf("[RUN]\tchild: Extend permissions to 0x81\n"); 114 if (ioperm(0x81, 1, 1) != 0) { 115 printf("[FAIL]\tioperm(0x81, 1, 1) failed (%d)", errno); 116 return 1; 117 } 118 printf("[RUN]\tchild: Drop permissions to 0x80\n"); 119 if (ioperm(0x80, 1, 0) != 0) { 120 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 121 return 1; 122 } 123 expect_gp(0x80); 124 return 0; 125 } else { 126 int status; 127 if (waitpid(child, &status, 0) != child || 128 !WIFEXITED(status)) { 129 printf("[FAIL]\tChild died\n"); 130 nerrs++; 131 } else if (WEXITSTATUS(status) != 0) { 132 printf("[FAIL]\tChild failed\n"); 133 nerrs++; 134 } else { 135 printf("[OK]\tChild succeeded\n"); 136 } 137 } 138 139 /* Verify that the child dropping 0x80 did not affect the parent */ 140 printf("\tVerify that unsharing the bitmap worked\n"); 141 expect_ok(0x80); 142 143 /* Test the capability checks. */ 144 printf("\tDrop privileges\n"); 145 if (setresuid(1, 1, 1) != 0) { 146 printf("[WARN]\tDropping privileges failed\n"); 147 return 0; 148 } 149 150 printf("[RUN]\tdisable 0x80\n"); 151 if (ioperm(0x80, 1, 0) != 0) { 152 printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); 153 return 1; 154 } 155 printf("[OK]\tit worked\n"); 156 157 printf("[RUN]\tenable 0x80 again\n"); 158 if (ioperm(0x80, 1, 1) == 0) { 159 printf("[FAIL]\tit succeeded but should have failed.\n"); 160 return 1; 161 } 162 printf("[OK]\tit failed\n"); 163 return 0; 164 } 165