18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * iopl.c - Test case for a Linux on Xen 64-bit bug 48c2ecf20Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define _GNU_SOURCE 88c2ecf20Sopenharmony_ci#include <err.h> 98c2ecf20Sopenharmony_ci#include <stdio.h> 108c2ecf20Sopenharmony_ci#include <stdint.h> 118c2ecf20Sopenharmony_ci#include <signal.h> 128c2ecf20Sopenharmony_ci#include <setjmp.h> 138c2ecf20Sopenharmony_ci#include <stdlib.h> 148c2ecf20Sopenharmony_ci#include <string.h> 158c2ecf20Sopenharmony_ci#include <errno.h> 168c2ecf20Sopenharmony_ci#include <unistd.h> 178c2ecf20Sopenharmony_ci#include <sys/types.h> 188c2ecf20Sopenharmony_ci#include <sys/wait.h> 198c2ecf20Sopenharmony_ci#include <stdbool.h> 208c2ecf20Sopenharmony_ci#include <sched.h> 218c2ecf20Sopenharmony_ci#include <sys/io.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int nerrs = 0; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 268c2ecf20Sopenharmony_ci int flags) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct sigaction sa; 298c2ecf20Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 308c2ecf20Sopenharmony_ci sa.sa_sigaction = handler; 318c2ecf20Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 328c2ecf20Sopenharmony_ci sigemptyset(&sa.sa_mask); 338c2ecf20Sopenharmony_ci if (sigaction(sig, &sa, 0)) 348c2ecf20Sopenharmony_ci err(1, "sigaction"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void clearhandler(int sig) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct sigaction sa; 418c2ecf20Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 428c2ecf20Sopenharmony_ci sa.sa_handler = SIG_DFL; 438c2ecf20Sopenharmony_ci sigemptyset(&sa.sa_mask); 448c2ecf20Sopenharmony_ci if (sigaction(sig, &sa, 0)) 458c2ecf20Sopenharmony_ci err(1, "sigaction"); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic jmp_buf jmpbuf; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void sigsegv(int sig, siginfo_t *si, void *ctx_void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci siglongjmp(jmpbuf, 1); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic bool try_outb(unsigned short port) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 588c2ecf20Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) != 0) { 598c2ecf20Sopenharmony_ci return false; 608c2ecf20Sopenharmony_ci } else { 618c2ecf20Sopenharmony_ci asm volatile ("outb %%al, %w[port]" 628c2ecf20Sopenharmony_ci : : [port] "Nd" (port), "a" (0)); 638c2ecf20Sopenharmony_ci return true; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci clearhandler(SIGSEGV); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void expect_ok_outb(unsigned short port) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (!try_outb(port)) { 718c2ecf20Sopenharmony_ci printf("[FAIL]\toutb to 0x%02hx failed\n", port); 728c2ecf20Sopenharmony_ci exit(1); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci printf("[OK]\toutb to 0x%02hx worked\n", port); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void expect_gp_outb(unsigned short port) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (try_outb(port)) { 818c2ecf20Sopenharmony_ci printf("[FAIL]\toutb to 0x%02hx worked\n", port); 828c2ecf20Sopenharmony_ci nerrs++; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci printf("[OK]\toutb to 0x%02hx failed\n", port); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define RET_FAULTED 0 898c2ecf20Sopenharmony_ci#define RET_FAIL 1 908c2ecf20Sopenharmony_ci#define RET_EMUL 2 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int try_cli(void) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci unsigned long flags; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 978c2ecf20Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) != 0) { 988c2ecf20Sopenharmony_ci return RET_FAULTED; 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci asm volatile("cli; pushf; pop %[flags]" 1018c2ecf20Sopenharmony_ci : [flags] "=rm" (flags)); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* X86_FLAGS_IF */ 1048c2ecf20Sopenharmony_ci if (!(flags & (1 << 9))) 1058c2ecf20Sopenharmony_ci return RET_FAIL; 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci return RET_EMUL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci clearhandler(SIGSEGV); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int try_sti(bool irqs_off) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci unsigned long flags; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci sethandler(SIGSEGV, sigsegv, SA_RESETHAND); 1178c2ecf20Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) != 0) { 1188c2ecf20Sopenharmony_ci return RET_FAULTED; 1198c2ecf20Sopenharmony_ci } else { 1208c2ecf20Sopenharmony_ci asm volatile("sti; pushf; pop %[flags]" 1218c2ecf20Sopenharmony_ci : [flags] "=rm" (flags)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* X86_FLAGS_IF */ 1248c2ecf20Sopenharmony_ci if (irqs_off && (flags & (1 << 9))) 1258c2ecf20Sopenharmony_ci return RET_FAIL; 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci return RET_EMUL; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci clearhandler(SIGSEGV); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void expect_gp_sti(bool irqs_off) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int ret = try_sti(irqs_off); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci switch (ret) { 1378c2ecf20Sopenharmony_ci case RET_FAULTED: 1388c2ecf20Sopenharmony_ci printf("[OK]\tSTI faulted\n"); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case RET_EMUL: 1418c2ecf20Sopenharmony_ci printf("[OK]\tSTI NOPped\n"); 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci default: 1448c2ecf20Sopenharmony_ci printf("[FAIL]\tSTI worked\n"); 1458c2ecf20Sopenharmony_ci nerrs++; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * Returns whether it managed to disable interrupts. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistatic bool test_cli(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int ret = try_cli(); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (ret) { 1578c2ecf20Sopenharmony_ci case RET_FAULTED: 1588c2ecf20Sopenharmony_ci printf("[OK]\tCLI faulted\n"); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case RET_EMUL: 1618c2ecf20Sopenharmony_ci printf("[OK]\tCLI NOPped\n"); 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci default: 1648c2ecf20Sopenharmony_ci printf("[FAIL]\tCLI worked\n"); 1658c2ecf20Sopenharmony_ci nerrs++; 1668c2ecf20Sopenharmony_ci return true; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return false; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint main(void) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci cpu_set_t cpuset; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci CPU_ZERO(&cpuset); 1778c2ecf20Sopenharmony_ci CPU_SET(0, &cpuset); 1788c2ecf20Sopenharmony_ci if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 1798c2ecf20Sopenharmony_ci err(1, "sched_setaffinity to CPU 0"); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Probe for iopl support. Note that iopl(0) works even as nonroot. */ 1828c2ecf20Sopenharmony_ci switch(iopl(3)) { 1838c2ecf20Sopenharmony_ci case 0: 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case -ENOSYS: 1868c2ecf20Sopenharmony_ci printf("[OK]\tiopl() nor supported\n"); 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci printf("[OK]\tiopl(3) failed (%d) -- try running as root\n", 1908c2ecf20Sopenharmony_ci errno); 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Make sure that CLI/STI are blocked even with IOPL level 3 */ 1958c2ecf20Sopenharmony_ci expect_gp_sti(test_cli()); 1968c2ecf20Sopenharmony_ci expect_ok_outb(0x80); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Establish an I/O bitmap to test the restore */ 1998c2ecf20Sopenharmony_ci if (ioperm(0x80, 1, 1) != 0) 2008c2ecf20Sopenharmony_ci err(1, "ioperm(0x80, 1, 1) failed\n"); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Restore our original state prior to starting the fork test. */ 2038c2ecf20Sopenharmony_ci if (iopl(0) != 0) 2048c2ecf20Sopenharmony_ci err(1, "iopl(0)"); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Verify that IOPL emulation is disabled and the I/O bitmap still 2088c2ecf20Sopenharmony_ci * works. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci expect_ok_outb(0x80); 2118c2ecf20Sopenharmony_ci expect_gp_outb(0xed); 2128c2ecf20Sopenharmony_ci /* Drop the I/O bitmap */ 2138c2ecf20Sopenharmony_ci if (ioperm(0x80, 1, 0) != 0) 2148c2ecf20Sopenharmony_ci err(1, "ioperm(0x80, 1, 0) failed\n"); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci pid_t child = fork(); 2178c2ecf20Sopenharmony_ci if (child == -1) 2188c2ecf20Sopenharmony_ci err(1, "fork"); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (child == 0) { 2218c2ecf20Sopenharmony_ci printf("\tchild: set IOPL to 3\n"); 2228c2ecf20Sopenharmony_ci if (iopl(3) != 0) 2238c2ecf20Sopenharmony_ci err(1, "iopl"); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci printf("[RUN]\tchild: write to 0x80\n"); 2268c2ecf20Sopenharmony_ci asm volatile ("outb %%al, $0x80" : : "a" (0)); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci int status; 2318c2ecf20Sopenharmony_ci if (waitpid(child, &status, 0) != child || 2328c2ecf20Sopenharmony_ci !WIFEXITED(status)) { 2338c2ecf20Sopenharmony_ci printf("[FAIL]\tChild died\n"); 2348c2ecf20Sopenharmony_ci nerrs++; 2358c2ecf20Sopenharmony_ci } else if (WEXITSTATUS(status) != 0) { 2368c2ecf20Sopenharmony_ci printf("[FAIL]\tChild failed\n"); 2378c2ecf20Sopenharmony_ci nerrs++; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci printf("[OK]\tChild succeeded\n"); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci printf("[RUN]\tparent: write to 0x80 (should fail)\n"); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci expect_gp_outb(0x80); 2468c2ecf20Sopenharmony_ci expect_gp_sti(test_cli()); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Test the capability checks. */ 2498c2ecf20Sopenharmony_ci printf("\tiopl(3)\n"); 2508c2ecf20Sopenharmony_ci if (iopl(3) != 0) 2518c2ecf20Sopenharmony_ci err(1, "iopl(3)"); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci printf("\tDrop privileges\n"); 2548c2ecf20Sopenharmony_ci if (setresuid(1, 1, 1) != 0) { 2558c2ecf20Sopenharmony_ci printf("[WARN]\tDropping privileges failed\n"); 2568c2ecf20Sopenharmony_ci goto done; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n"); 2608c2ecf20Sopenharmony_ci if (iopl(3) != 0) { 2618c2ecf20Sopenharmony_ci printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n"); 2628c2ecf20Sopenharmony_ci nerrs++; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci printf("[RUN]\tiopl(0) unprivileged\n"); 2668c2ecf20Sopenharmony_ci if (iopl(0) != 0) { 2678c2ecf20Sopenharmony_ci printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n"); 2688c2ecf20Sopenharmony_ci nerrs++; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci printf("[RUN]\tiopl(3) unprivileged\n"); 2728c2ecf20Sopenharmony_ci if (iopl(3) == 0) { 2738c2ecf20Sopenharmony_ci printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n"); 2748c2ecf20Sopenharmony_ci nerrs++; 2758c2ecf20Sopenharmony_ci } else { 2768c2ecf20Sopenharmony_ci printf("[OK]\tFailed as expected\n"); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cidone: 2808c2ecf20Sopenharmony_ci return nerrs ? 1 : 0; 2818c2ecf20Sopenharmony_ci} 282