0001
0002
0003
0004
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
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
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
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
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
0195 expect_gp_sti(test_cli());
0196 expect_ok_outb(0x80);
0197
0198
0199 if (ioperm(0x80, 1, 1) != 0)
0200 err(1, "ioperm(0x80, 1, 1) failed\n");
0201
0202
0203 if (iopl(0) != 0)
0204 err(1, "iopl(0)");
0205
0206
0207
0208
0209
0210 expect_ok_outb(0x80);
0211 expect_gp_outb(0xed);
0212
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
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 }