| .. | .. |
|---|
| 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 | | - |
|---|