.. | .. |
---|
35 | 35 | |
---|
36 | 36 | } |
---|
37 | 37 | |
---|
| 38 | +static 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 | + |
---|
38 | 48 | static jmp_buf jmpbuf; |
---|
39 | 49 | |
---|
40 | 50 | static void sigsegv(int sig, siginfo_t *si, void *ctx_void) |
---|
.. | .. |
---|
42 | 52 | siglongjmp(jmpbuf, 1); |
---|
43 | 53 | } |
---|
44 | 54 | |
---|
| 55 | +static 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 | + |
---|
| 68 | +static 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 | + |
---|
| 78 | +static 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 | + |
---|
| 88 | +#define RET_FAULTED 0 |
---|
| 89 | +#define RET_FAIL 1 |
---|
| 90 | +#define RET_EMUL 2 |
---|
| 91 | + |
---|
| 92 | +static int try_cli(void) |
---|
| 93 | +{ |
---|
| 94 | + unsigned long flags; |
---|
| 95 | + |
---|
| 96 | + sethandler(SIGSEGV, sigsegv, SA_RESETHAND); |
---|
| 97 | + if (sigsetjmp(jmpbuf, 1) != 0) { |
---|
| 98 | + return RET_FAULTED; |
---|
| 99 | + } else { |
---|
| 100 | + asm volatile("cli; pushf; pop %[flags]" |
---|
| 101 | + : [flags] "=rm" (flags)); |
---|
| 102 | + |
---|
| 103 | + /* X86_FLAGS_IF */ |
---|
| 104 | + if (!(flags & (1 << 9))) |
---|
| 105 | + return RET_FAIL; |
---|
| 106 | + else |
---|
| 107 | + return RET_EMUL; |
---|
| 108 | + } |
---|
| 109 | + clearhandler(SIGSEGV); |
---|
| 110 | +} |
---|
| 111 | + |
---|
| 112 | +static int try_sti(bool irqs_off) |
---|
| 113 | +{ |
---|
| 114 | + unsigned long flags; |
---|
| 115 | + |
---|
| 116 | + sethandler(SIGSEGV, sigsegv, SA_RESETHAND); |
---|
| 117 | + if (sigsetjmp(jmpbuf, 1) != 0) { |
---|
| 118 | + return RET_FAULTED; |
---|
| 119 | + } else { |
---|
| 120 | + asm volatile("sti; pushf; pop %[flags]" |
---|
| 121 | + : [flags] "=rm" (flags)); |
---|
| 122 | + |
---|
| 123 | + /* X86_FLAGS_IF */ |
---|
| 124 | + if (irqs_off && (flags & (1 << 9))) |
---|
| 125 | + return RET_FAIL; |
---|
| 126 | + else |
---|
| 127 | + return RET_EMUL; |
---|
| 128 | + } |
---|
| 129 | + clearhandler(SIGSEGV); |
---|
| 130 | +} |
---|
| 131 | + |
---|
| 132 | +static void expect_gp_sti(bool irqs_off) |
---|
| 133 | +{ |
---|
| 134 | + int ret = try_sti(irqs_off); |
---|
| 135 | + |
---|
| 136 | + switch (ret) { |
---|
| 137 | + case RET_FAULTED: |
---|
| 138 | + printf("[OK]\tSTI faulted\n"); |
---|
| 139 | + break; |
---|
| 140 | + case RET_EMUL: |
---|
| 141 | + printf("[OK]\tSTI NOPped\n"); |
---|
| 142 | + break; |
---|
| 143 | + default: |
---|
| 144 | + printf("[FAIL]\tSTI worked\n"); |
---|
| 145 | + nerrs++; |
---|
| 146 | + } |
---|
| 147 | +} |
---|
| 148 | + |
---|
| 149 | +/* |
---|
| 150 | + * Returns whether it managed to disable interrupts. |
---|
| 151 | + */ |
---|
| 152 | +static bool test_cli(void) |
---|
| 153 | +{ |
---|
| 154 | + int ret = try_cli(); |
---|
| 155 | + |
---|
| 156 | + switch (ret) { |
---|
| 157 | + case RET_FAULTED: |
---|
| 158 | + printf("[OK]\tCLI faulted\n"); |
---|
| 159 | + break; |
---|
| 160 | + case RET_EMUL: |
---|
| 161 | + printf("[OK]\tCLI NOPped\n"); |
---|
| 162 | + break; |
---|
| 163 | + default: |
---|
| 164 | + printf("[FAIL]\tCLI worked\n"); |
---|
| 165 | + nerrs++; |
---|
| 166 | + return true; |
---|
| 167 | + } |
---|
| 168 | + |
---|
| 169 | + return false; |
---|
| 170 | +} |
---|
| 171 | + |
---|
45 | 172 | int main(void) |
---|
46 | 173 | { |
---|
47 | 174 | cpu_set_t cpuset; |
---|
| 175 | + |
---|
48 | 176 | CPU_ZERO(&cpuset); |
---|
49 | 177 | CPU_SET(0, &cpuset); |
---|
50 | 178 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) |
---|
51 | 179 | err(1, "sched_setaffinity to CPU 0"); |
---|
52 | 180 | |
---|
53 | 181 | /* Probe for iopl support. Note that iopl(0) works even as nonroot. */ |
---|
54 | | - if (iopl(3) != 0) { |
---|
| 182 | + switch(iopl(3)) { |
---|
| 183 | + case 0: |
---|
| 184 | + break; |
---|
| 185 | + case -ENOSYS: |
---|
| 186 | + printf("[OK]\tiopl() nor supported\n"); |
---|
| 187 | + return 0; |
---|
| 188 | + default: |
---|
55 | 189 | printf("[OK]\tiopl(3) failed (%d) -- try running as root\n", |
---|
56 | 190 | errno); |
---|
57 | 191 | return 0; |
---|
58 | 192 | } |
---|
59 | 193 | |
---|
60 | | - /* Restore our original state prior to starting the test. */ |
---|
| 194 | + /* Make sure that CLI/STI are blocked even with IOPL level 3 */ |
---|
| 195 | + expect_gp_sti(test_cli()); |
---|
| 196 | + expect_ok_outb(0x80); |
---|
| 197 | + |
---|
| 198 | + /* Establish an I/O bitmap to test the restore */ |
---|
| 199 | + if (ioperm(0x80, 1, 1) != 0) |
---|
| 200 | + err(1, "ioperm(0x80, 1, 1) failed\n"); |
---|
| 201 | + |
---|
| 202 | + /* Restore our original state prior to starting the fork test. */ |
---|
61 | 203 | if (iopl(0) != 0) |
---|
62 | 204 | err(1, "iopl(0)"); |
---|
| 205 | + |
---|
| 206 | + /* |
---|
| 207 | + * Verify that IOPL emulation is disabled and the I/O bitmap still |
---|
| 208 | + * works. |
---|
| 209 | + */ |
---|
| 210 | + expect_ok_outb(0x80); |
---|
| 211 | + expect_gp_outb(0xed); |
---|
| 212 | + /* Drop the I/O bitmap */ |
---|
| 213 | + if (ioperm(0x80, 1, 0) != 0) |
---|
| 214 | + err(1, "ioperm(0x80, 1, 0) failed\n"); |
---|
63 | 215 | |
---|
64 | 216 | pid_t child = fork(); |
---|
65 | 217 | if (child == -1) |
---|
.. | .. |
---|
90 | 242 | |
---|
91 | 243 | printf("[RUN]\tparent: write to 0x80 (should fail)\n"); |
---|
92 | 244 | |
---|
93 | | - sethandler(SIGSEGV, sigsegv, 0); |
---|
94 | | - if (sigsetjmp(jmpbuf, 1) != 0) { |
---|
95 | | - printf("[OK]\twrite was denied\n"); |
---|
96 | | - } else { |
---|
97 | | - asm volatile ("outb %%al, $0x80" : : "a" (0)); |
---|
98 | | - printf("[FAIL]\twrite was allowed\n"); |
---|
99 | | - nerrs++; |
---|
100 | | - } |
---|
| 245 | + expect_gp_outb(0x80); |
---|
| 246 | + expect_gp_sti(test_cli()); |
---|
101 | 247 | |
---|
102 | 248 | /* Test the capability checks. */ |
---|
103 | 249 | printf("\tiopl(3)\n"); |
---|
.. | .. |
---|
133 | 279 | done: |
---|
134 | 280 | return nerrs ? 1 : 0; |
---|
135 | 281 | } |
---|
136 | | - |
---|