1 // SPDX-License-Identifier: GPL-2.0
3 * iopl.c - Test case for a Linux on Xen 64-bit bug
4 * Copyright (c) 2015 Andrew Lutomirski
17 #include <sys/types.h>
25 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
29 memset(&sa, 0, sizeof(sa));
30 sa.sa_sigaction = handler;
31 sa.sa_flags = SA_SIGINFO | flags;
32 sigemptyset(&sa.sa_mask);
33 if (sigaction(sig, &sa, 0))
38 static void clearhandler(int sig)
41 memset(&sa, 0, sizeof(sa));
42 sa.sa_handler = SIG_DFL;
43 sigemptyset(&sa.sa_mask);
44 if (sigaction(sig, &sa, 0))
48 static jmp_buf jmpbuf;
50 static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
52 siglongjmp(jmpbuf, 1);
55 static bool try_outb(unsigned short port)
57 sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
58 if (sigsetjmp(jmpbuf, 1) != 0) {
61 asm volatile ("outb %%al, %w[port]"
62 : : [port] "Nd" (port), "a" (0));
65 clearhandler(SIGSEGV);
68 static void expect_ok_outb(unsigned short port)
70 if (!try_outb(port)) {
71 printf("[FAIL]\toutb to 0x%02hx failed\n", port);
75 printf("[OK]\toutb to 0x%02hx worked\n", port);
78 static void expect_gp_outb(unsigned short port)
81 printf("[FAIL]\toutb to 0x%02hx worked\n", port);
85 printf("[OK]\toutb to 0x%02hx failed\n", port);
92 static int try_cli(void)
96 sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
97 if (sigsetjmp(jmpbuf, 1) != 0) {
100 asm volatile("cli; pushf; pop %[flags]"
101 : [flags] "=rm" (flags));
104 if (!(flags & (1 << 9)))
109 clearhandler(SIGSEGV);
112 static int try_sti(bool irqs_off)
116 sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
117 if (sigsetjmp(jmpbuf, 1) != 0) {
120 asm volatile("sti; pushf; pop %[flags]"
121 : [flags] "=rm" (flags));
124 if (irqs_off && (flags & (1 << 9)))
129 clearhandler(SIGSEGV);
132 static void expect_gp_sti(bool irqs_off)
134 int ret = try_sti(irqs_off);
138 printf("[OK]\tSTI faulted\n");
141 printf("[OK]\tSTI NOPped\n");
144 printf("[FAIL]\tSTI worked\n");
150 * Returns whether it managed to disable interrupts.
152 static bool test_cli(void)
158 printf("[OK]\tCLI faulted\n");
161 printf("[OK]\tCLI NOPped\n");
164 printf("[FAIL]\tCLI worked\n");
178 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
179 err(1, "sched_setaffinity to CPU 0");
181 /* Probe for iopl support. Note that iopl(0) works even as nonroot. */
186 printf("[OK]\tiopl() nor supported\n");
189 printf("[OK]\tiopl(3) failed (%d) -- try running as root\n",
194 /* Make sure that CLI/STI are blocked even with IOPL level 3 */
195 expect_gp_sti(test_cli());
196 expect_ok_outb(0x80);
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");
202 /* Restore our original state prior to starting the fork test. */
207 * Verify that IOPL emulation is disabled and the I/O bitmap still
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");
216 pid_t child = fork();
221 printf("\tchild: set IOPL to 3\n");
225 printf("[RUN]\tchild: write to 0x80\n");
226 asm volatile ("outb %%al, $0x80" : : "a" (0));
231 if (waitpid(child, &status, 0) != child ||
232 !WIFEXITED(status)) {
233 printf("[FAIL]\tChild died\n");
235 } else if (WEXITSTATUS(status) != 0) {
236 printf("[FAIL]\tChild failed\n");
239 printf("[OK]\tChild succeeded\n");
243 printf("[RUN]\tparent: write to 0x80 (should fail)\n");
245 expect_gp_outb(0x80);
246 expect_gp_sti(test_cli());
248 /* Test the capability checks. */
249 printf("\tiopl(3)\n");
253 printf("\tDrop privileges\n");
254 if (setresuid(1, 1, 1) != 0) {
255 printf("[WARN]\tDropping privileges failed\n");
259 printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n");
261 printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n");
265 printf("[RUN]\tiopl(0) unprivileged\n");
267 printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n");
271 printf("[RUN]\tiopl(3) unprivileged\n");
273 printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n");
276 printf("[OK]\tFailed as expected\n");
280 return nerrs ? 1 : 0;