File Index
Files and directories
Sign in
FILE

iopl.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/x86/iopl.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * iopl.c - Test case for a Linux on Xen 64-bit bug
  4 * Copyright (c) 2015 Andrew Lutomirski
  5 */
  6
  7#define _GNU_SOURCE
  8#include <err.h>
  9#include <stdio.h>
 10#include <stdint.h>
 11#include <signal.h>
 12#include <setjmp.h>
 13#include <stdlib.h>
 14#include <string.h>
 15#include <errno.h>
 16#include <unistd.h>
 17#include <sys/types.h>
 18#include <sys/wait.h>
 19#include <stdbool.h>
 20#include <sched.h>
 21#include <sys/io.h>
 22
 23#include "helpers.h"
 24
 25static int nerrs = 0;
 26
 27static jmp_buf jmpbuf;
 28
 29static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
 30{
 31	siglongjmp(jmpbuf, 1);
 32}
 33
 34static bool try_outb(unsigned short port)
 35{
 36	sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
 37	if (sigsetjmp(jmpbuf, 1) != 0) {
 38		return false;
 39	} else {
 40		asm volatile ("outb %%al, %w[port]"
 41			      : : [port] "Nd" (port), "a" (0));
 42		return true;
 43	}
 44	clearhandler(SIGSEGV);
 45}
 46
 47static void expect_ok_outb(unsigned short port)
 48{
 49	if (!try_outb(port)) {
 50		printf("[FAIL]\toutb to 0x%02hx failed\n", port);
 51		exit(1);
 52	}
 53
 54	printf("[OK]\toutb to 0x%02hx worked\n", port);
 55}
 56
 57static void expect_gp_outb(unsigned short port)
 58{
 59	if (try_outb(port)) {
 60		printf("[FAIL]\toutb to 0x%02hx worked\n", port);
 61		nerrs++;
 62	}
 63
 64	printf("[OK]\toutb to 0x%02hx failed\n", port);
 65}
 66
 67#define RET_FAULTED	0
 68#define RET_FAIL	1
 69#define RET_EMUL	2
 70
 71static int try_cli(void)
 72{
 73	unsigned long flags;
 74
 75	sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
 76	if (sigsetjmp(jmpbuf, 1) != 0) {
 77		return RET_FAULTED;
 78	} else {
 79		asm volatile("cli; pushf; pop %[flags]"
 80				: [flags] "=rm" (flags));
 81
 82		/* X86_FLAGS_IF */
 83		if (!(flags & (1 << 9)))
 84			return RET_FAIL;
 85		else
 86			return RET_EMUL;
 87	}
 88	clearhandler(SIGSEGV);
 89}
 90
 91static int try_sti(bool irqs_off)
 92{
 93	unsigned long flags;
 94
 95	sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
 96	if (sigsetjmp(jmpbuf, 1) != 0) {
 97		return RET_FAULTED;
 98	} else {
 99		asm volatile("sti; pushf; pop %[flags]"
100				: [flags] "=rm" (flags));
101
102		/* X86_FLAGS_IF */
103		if (irqs_off && (flags & (1 << 9)))
104			return RET_FAIL;
105		else
106			return RET_EMUL;
107	}
108	clearhandler(SIGSEGV);
109}
110
111static void expect_gp_sti(bool irqs_off)
112{
113	int ret = try_sti(irqs_off);
114
115	switch (ret) {
116	case RET_FAULTED:
117		printf("[OK]\tSTI faulted\n");
118		break;
119	case RET_EMUL:
120		printf("[OK]\tSTI NOPped\n");
121		break;
122	default:
123		printf("[FAIL]\tSTI worked\n");
124		nerrs++;
125	}
126}
127
128/*
129 * Returns whether it managed to disable interrupts.
130 */
131static bool test_cli(void)
132{
133	int ret = try_cli();
134
135	switch (ret) {
136	case RET_FAULTED:
137		printf("[OK]\tCLI faulted\n");
138		break;
139	case RET_EMUL:
140		printf("[OK]\tCLI NOPped\n");
141		break;
142	default:
143		printf("[FAIL]\tCLI worked\n");
144		nerrs++;
145		return true;
146	}
147
148	return false;
149}
150
151int main(void)
152{
153	cpu_set_t cpuset;
154
155	CPU_ZERO(&cpuset);
156	CPU_SET(0, &cpuset);
157	if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
158		err(1, "sched_setaffinity to CPU 0");
159
160	/* Probe for iopl support.  Note that iopl(0) works even as nonroot. */
161	switch(iopl(3)) {
162	case 0:
163		break;
164	case -ENOSYS:
165		printf("[OK]\tiopl() nor supported\n");
166		return 0;
167	default:
168		printf("[OK]\tiopl(3) failed (%d) -- try running as root\n",
169		       errno);
170		return 0;
171	}
172
173	/* Make sure that CLI/STI are blocked even with IOPL level 3 */
174	expect_gp_sti(test_cli());
175	expect_ok_outb(0x80);
176
177	/* Establish an I/O bitmap to test the restore */
178	if (ioperm(0x80, 1, 1) != 0)
179		err(1, "ioperm(0x80, 1, 1) failed\n");
180
181	/* Restore our original state prior to starting the fork test. */
182	if (iopl(0) != 0)
183		err(1, "iopl(0)");
184
185	/*
186	 * Verify that IOPL emulation is disabled and the I/O bitmap still
187	 * works.
188	 */
189	expect_ok_outb(0x80);
190	expect_gp_outb(0xed);
191	/* Drop the I/O bitmap */
192	if (ioperm(0x80, 1, 0) != 0)
193		err(1, "ioperm(0x80, 1, 0) failed\n");
194
195	pid_t child = fork();
196	if (child == -1)
197		err(1, "fork");
198
199	if (child == 0) {
200		printf("\tchild: set IOPL to 3\n");
201		if (iopl(3) != 0)
202			err(1, "iopl");
203
204		printf("[RUN]\tchild: write to 0x80\n");
205		asm volatile ("outb %%al, $0x80" : : "a" (0));
206
207		return 0;
208	} else {
209		int status;
210		if (waitpid(child, &status, 0) != child ||
211		    !WIFEXITED(status)) {
212			printf("[FAIL]\tChild died\n");
213			nerrs++;
214		} else if (WEXITSTATUS(status) != 0) {
215			printf("[FAIL]\tChild failed\n");
216			nerrs++;
217		} else {
218			printf("[OK]\tChild succeeded\n");
219		}
220	}
221
222	printf("[RUN]\tparent: write to 0x80 (should fail)\n");
223
224	expect_gp_outb(0x80);
225	expect_gp_sti(test_cli());
226
227	/* Test the capability checks. */
228	printf("\tiopl(3)\n");
229	if (iopl(3) != 0)
230		err(1, "iopl(3)");
231
232	printf("\tDrop privileges\n");
233	if (setresuid(1, 1, 1) != 0) {
234		printf("[WARN]\tDropping privileges failed\n");
235		goto done;
236	}
237
238	printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n");
239	if (iopl(3) != 0) {
240		printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n");
241		nerrs++;
242	}
243
244	printf("[RUN]\tiopl(0) unprivileged\n");
245	if (iopl(0) != 0) {
246		printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n");
247		nerrs++;
248	}
249
250	printf("[RUN]\tiopl(3) unprivileged\n");
251	if (iopl(3) == 0) {
252		printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n");
253		nerrs++;
254	} else {
255		printf("[OK]\tFailed as expected\n");
256	}
257
258done:
259	return nerrs ? 1 : 0;
260}
~/ Read-only file preview.