Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * iopl.c - Test case for a Linux on Xen 64-bit bug
0004  * Copyright (c) 2015 Andrew Lutomirski
0005  */
0006 
0007 #define _GNU_SOURCE
0008 #include <err.h>
0009 #include <stdio.h>
0010 #include <stdint.h>
0011 #include <signal.h>
0012 #include <setjmp.h>
0013 #include <stdlib.h>
0014 #include <string.h>
0015 #include <errno.h>
0016 #include <unistd.h>
0017 #include <sys/types.h>
0018 #include <sys/wait.h>
0019 #include <stdbool.h>
0020 #include <sched.h>
0021 #include <sys/io.h>
0022 
0023 static int nerrs = 0;
0024 
0025 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
0026                int flags)
0027 {
0028     struct sigaction sa;
0029     memset(&sa, 0, sizeof(sa));
0030     sa.sa_sigaction = handler;
0031     sa.sa_flags = SA_SIGINFO | flags;
0032     sigemptyset(&sa.sa_mask);
0033     if (sigaction(sig, &sa, 0))
0034         err(1, "sigaction");
0035 
0036 }
0037 
0038 static void clearhandler(int sig)
0039 {
0040     struct sigaction sa;
0041     memset(&sa, 0, sizeof(sa));
0042     sa.sa_handler = SIG_DFL;
0043     sigemptyset(&sa.sa_mask);
0044     if (sigaction(sig, &sa, 0))
0045         err(1, "sigaction");
0046 }
0047 
0048 static jmp_buf jmpbuf;
0049 
0050 static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
0051 {
0052     siglongjmp(jmpbuf, 1);
0053 }
0054 
0055 static bool try_outb(unsigned short port)
0056 {
0057     sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
0058     if (sigsetjmp(jmpbuf, 1) != 0) {
0059         return false;
0060     } else {
0061         asm volatile ("outb %%al, %w[port]"
0062                   : : [port] "Nd" (port), "a" (0));
0063         return true;
0064     }
0065     clearhandler(SIGSEGV);
0066 }
0067 
0068 static void expect_ok_outb(unsigned short port)
0069 {
0070     if (!try_outb(port)) {
0071         printf("[FAIL]\toutb to 0x%02hx failed\n", port);
0072         exit(1);
0073     }
0074 
0075     printf("[OK]\toutb to 0x%02hx worked\n", port);
0076 }
0077 
0078 static void expect_gp_outb(unsigned short port)
0079 {
0080     if (try_outb(port)) {
0081         printf("[FAIL]\toutb to 0x%02hx worked\n", port);
0082         nerrs++;
0083     }
0084 
0085     printf("[OK]\toutb to 0x%02hx failed\n", port);
0086 }
0087 
0088 #define RET_FAULTED 0
0089 #define RET_FAIL    1
0090 #define RET_EMUL    2
0091 
0092 static int try_cli(void)
0093 {
0094     unsigned long flags;
0095 
0096     sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
0097     if (sigsetjmp(jmpbuf, 1) != 0) {
0098         return RET_FAULTED;
0099     } else {
0100         asm volatile("cli; pushf; pop %[flags]"
0101                 : [flags] "=rm" (flags));
0102 
0103         /* X86_FLAGS_IF */
0104         if (!(flags & (1 << 9)))
0105             return RET_FAIL;
0106         else
0107             return RET_EMUL;
0108     }
0109     clearhandler(SIGSEGV);
0110 }
0111 
0112 static int try_sti(bool irqs_off)
0113 {
0114     unsigned long flags;
0115 
0116     sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
0117     if (sigsetjmp(jmpbuf, 1) != 0) {
0118         return RET_FAULTED;
0119     } else {
0120         asm volatile("sti; pushf; pop %[flags]"
0121                 : [flags] "=rm" (flags));
0122 
0123         /* X86_FLAGS_IF */
0124         if (irqs_off && (flags & (1 << 9)))
0125             return RET_FAIL;
0126         else
0127             return RET_EMUL;
0128     }
0129     clearhandler(SIGSEGV);
0130 }
0131 
0132 static void expect_gp_sti(bool irqs_off)
0133 {
0134     int ret = try_sti(irqs_off);
0135 
0136     switch (ret) {
0137     case RET_FAULTED:
0138         printf("[OK]\tSTI faulted\n");
0139         break;
0140     case RET_EMUL:
0141         printf("[OK]\tSTI NOPped\n");
0142         break;
0143     default:
0144         printf("[FAIL]\tSTI worked\n");
0145         nerrs++;
0146     }
0147 }
0148 
0149 /*
0150  * Returns whether it managed to disable interrupts.
0151  */
0152 static bool test_cli(void)
0153 {
0154     int ret = try_cli();
0155 
0156     switch (ret) {
0157     case RET_FAULTED:
0158         printf("[OK]\tCLI faulted\n");
0159         break;
0160     case RET_EMUL:
0161         printf("[OK]\tCLI NOPped\n");
0162         break;
0163     default:
0164         printf("[FAIL]\tCLI worked\n");
0165         nerrs++;
0166         return true;
0167     }
0168 
0169     return false;
0170 }
0171 
0172 int main(void)
0173 {
0174     cpu_set_t cpuset;
0175 
0176     CPU_ZERO(&cpuset);
0177     CPU_SET(0, &cpuset);
0178     if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
0179         err(1, "sched_setaffinity to CPU 0");
0180 
0181     /* Probe for iopl support.  Note that iopl(0) works even as nonroot. */
0182     switch(iopl(3)) {
0183     case 0:
0184         break;
0185     case -ENOSYS:
0186         printf("[OK]\tiopl() nor supported\n");
0187         return 0;
0188     default:
0189         printf("[OK]\tiopl(3) failed (%d) -- try running as root\n",
0190                errno);
0191         return 0;
0192     }
0193 
0194     /* Make sure that CLI/STI are blocked even with IOPL level 3 */
0195     expect_gp_sti(test_cli());
0196     expect_ok_outb(0x80);
0197 
0198     /* Establish an I/O bitmap to test the restore */
0199     if (ioperm(0x80, 1, 1) != 0)
0200         err(1, "ioperm(0x80, 1, 1) failed\n");
0201 
0202     /* Restore our original state prior to starting the fork test. */
0203     if (iopl(0) != 0)
0204         err(1, "iopl(0)");
0205 
0206     /*
0207      * Verify that IOPL emulation is disabled and the I/O bitmap still
0208      * works.
0209      */
0210     expect_ok_outb(0x80);
0211     expect_gp_outb(0xed);
0212     /* Drop the I/O bitmap */
0213     if (ioperm(0x80, 1, 0) != 0)
0214         err(1, "ioperm(0x80, 1, 0) failed\n");
0215 
0216     pid_t child = fork();
0217     if (child == -1)
0218         err(1, "fork");
0219 
0220     if (child == 0) {
0221         printf("\tchild: set IOPL to 3\n");
0222         if (iopl(3) != 0)
0223             err(1, "iopl");
0224 
0225         printf("[RUN]\tchild: write to 0x80\n");
0226         asm volatile ("outb %%al, $0x80" : : "a" (0));
0227 
0228         return 0;
0229     } else {
0230         int status;
0231         if (waitpid(child, &status, 0) != child ||
0232             !WIFEXITED(status)) {
0233             printf("[FAIL]\tChild died\n");
0234             nerrs++;
0235         } else if (WEXITSTATUS(status) != 0) {
0236             printf("[FAIL]\tChild failed\n");
0237             nerrs++;
0238         } else {
0239             printf("[OK]\tChild succeeded\n");
0240         }
0241     }
0242 
0243     printf("[RUN]\tparent: write to 0x80 (should fail)\n");
0244 
0245     expect_gp_outb(0x80);
0246     expect_gp_sti(test_cli());
0247 
0248     /* Test the capability checks. */
0249     printf("\tiopl(3)\n");
0250     if (iopl(3) != 0)
0251         err(1, "iopl(3)");
0252 
0253     printf("\tDrop privileges\n");
0254     if (setresuid(1, 1, 1) != 0) {
0255         printf("[WARN]\tDropping privileges failed\n");
0256         goto done;
0257     }
0258 
0259     printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n");
0260     if (iopl(3) != 0) {
0261         printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n");
0262         nerrs++;
0263     }
0264 
0265     printf("[RUN]\tiopl(0) unprivileged\n");
0266     if (iopl(0) != 0) {
0267         printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n");
0268         nerrs++;
0269     }
0270 
0271     printf("[RUN]\tiopl(3) unprivileged\n");
0272     if (iopl(3) == 0) {
0273         printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n");
0274         nerrs++;
0275     } else {
0276         printf("[OK]\tFailed as expected\n");
0277     }
0278 
0279 done:
0280     return nerrs ? 1 : 0;
0281 }