Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.11-rc5 243 lines 4.9 kB view raw
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 23static int nerrs = 0; 24 25static 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 38static 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 48static jmp_buf jmpbuf; 49 50static void sigsegv(int sig, siginfo_t *si, void *ctx_void) 51{ 52 siglongjmp(jmpbuf, 1); 53} 54 55static 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 68static 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 78static 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 88static bool try_cli(void) 89{ 90 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 91 if (sigsetjmp(jmpbuf, 1) != 0) { 92 return false; 93 } else { 94 asm volatile ("cli"); 95 return true; 96 } 97 clearhandler(SIGSEGV); 98} 99 100static bool try_sti(void) 101{ 102 sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 103 if (sigsetjmp(jmpbuf, 1) != 0) { 104 return false; 105 } else { 106 asm volatile ("sti"); 107 return true; 108 } 109 clearhandler(SIGSEGV); 110} 111 112static void expect_gp_sti(void) 113{ 114 if (try_sti()) { 115 printf("[FAIL]\tSTI worked\n"); 116 nerrs++; 117 } else { 118 printf("[OK]\tSTI faulted\n"); 119 } 120} 121 122static void expect_gp_cli(void) 123{ 124 if (try_cli()) { 125 printf("[FAIL]\tCLI worked\n"); 126 nerrs++; 127 } else { 128 printf("[OK]\tCLI faulted\n"); 129 } 130} 131 132int main(void) 133{ 134 cpu_set_t cpuset; 135 136 CPU_ZERO(&cpuset); 137 CPU_SET(0, &cpuset); 138 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 139 err(1, "sched_setaffinity to CPU 0"); 140 141 /* Probe for iopl support. Note that iopl(0) works even as nonroot. */ 142 switch(iopl(3)) { 143 case 0: 144 break; 145 case -ENOSYS: 146 printf("[OK]\tiopl() nor supported\n"); 147 return 0; 148 default: 149 printf("[OK]\tiopl(3) failed (%d) -- try running as root\n", 150 errno); 151 return 0; 152 } 153 154 /* Make sure that CLI/STI are blocked even with IOPL level 3 */ 155 expect_gp_cli(); 156 expect_gp_sti(); 157 expect_ok_outb(0x80); 158 159 /* Establish an I/O bitmap to test the restore */ 160 if (ioperm(0x80, 1, 1) != 0) 161 err(1, "ioperm(0x80, 1, 1) failed\n"); 162 163 /* Restore our original state prior to starting the fork test. */ 164 if (iopl(0) != 0) 165 err(1, "iopl(0)"); 166 167 /* 168 * Verify that IOPL emulation is disabled and the I/O bitmap still 169 * works. 170 */ 171 expect_ok_outb(0x80); 172 expect_gp_outb(0xed); 173 /* Drop the I/O bitmap */ 174 if (ioperm(0x80, 1, 0) != 0) 175 err(1, "ioperm(0x80, 1, 0) failed\n"); 176 177 pid_t child = fork(); 178 if (child == -1) 179 err(1, "fork"); 180 181 if (child == 0) { 182 printf("\tchild: set IOPL to 3\n"); 183 if (iopl(3) != 0) 184 err(1, "iopl"); 185 186 printf("[RUN]\tchild: write to 0x80\n"); 187 asm volatile ("outb %%al, $0x80" : : "a" (0)); 188 189 return 0; 190 } else { 191 int status; 192 if (waitpid(child, &status, 0) != child || 193 !WIFEXITED(status)) { 194 printf("[FAIL]\tChild died\n"); 195 nerrs++; 196 } else if (WEXITSTATUS(status) != 0) { 197 printf("[FAIL]\tChild failed\n"); 198 nerrs++; 199 } else { 200 printf("[OK]\tChild succeeded\n"); 201 } 202 } 203 204 printf("[RUN]\tparent: write to 0x80 (should fail)\n"); 205 206 expect_gp_outb(0x80); 207 expect_gp_cli(); 208 expect_gp_sti(); 209 210 /* Test the capability checks. */ 211 printf("\tiopl(3)\n"); 212 if (iopl(3) != 0) 213 err(1, "iopl(3)"); 214 215 printf("\tDrop privileges\n"); 216 if (setresuid(1, 1, 1) != 0) { 217 printf("[WARN]\tDropping privileges failed\n"); 218 goto done; 219 } 220 221 printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n"); 222 if (iopl(3) != 0) { 223 printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n"); 224 nerrs++; 225 } 226 227 printf("[RUN]\tiopl(0) unprivileged\n"); 228 if (iopl(0) != 0) { 229 printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n"); 230 nerrs++; 231 } 232 233 printf("[RUN]\tiopl(3) unprivileged\n"); 234 if (iopl(3) == 0) { 235 printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n"); 236 nerrs++; 237 } else { 238 printf("[OK]\tFailed as expected\n"); 239 } 240 241done: 242 return nerrs ? 1 : 0; 243}