brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 7e3e09c Raw
282 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * iopl.c - Test case for a Linux on Xen 64-bit bug4 * Copyright (c) 2015 Andrew Lutomirski5 */6 7#define _GNU_SOURCE8#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 23static int nerrs = 0;24 25static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),26		       int flags)27{28	struct sigaction sa;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))34		err(1, "sigaction");35 36}37 38static 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 48static jmp_buf jmpbuf;49 50static void sigsegv(int sig, siginfo_t *si, void *ctx_void)51{52	siglongjmp(jmpbuf, 1);53}54 55static 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 68static 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 78static 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	089#define RET_FAIL	190#define RET_EMUL	291 92static 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		else107			return RET_EMUL;108	}109	clearhandler(SIGSEGV);110}111 112static 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		else127			return RET_EMUL;128	}129	clearhandler(SIGSEGV);130}131 132static 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 */152static 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 172int main(void)173{174	cpu_set_t cpuset;175 176	CPU_ZERO(&cpuset);177	CPU_SET(0, &cpuset);178	if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)179		err(1, "sched_setaffinity to CPU 0");180 181	/* Probe for iopl support.  Note that iopl(0) works even as nonroot. */182	switch(iopl(3)) {183	case 0:184		break;185	case -ENOSYS:186		printf("[OK]\tiopl() nor supported\n");187		return 0;188	default:189		printf("[OK]\tiopl(3) failed (%d) -- try running as root\n",190		       errno);191		return 0;192	}193 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. */203	if (iopl(0) != 0)204		err(1, "iopl(0)");205 206	/*207	 * Verify that IOPL emulation is disabled and the I/O bitmap still208	 * 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");215 216	pid_t child = fork();217	if (child == -1)218		err(1, "fork");219 220	if (child == 0) {221		printf("\tchild: set IOPL to 3\n");222		if (iopl(3) != 0)223			err(1, "iopl");224 225		printf("[RUN]\tchild: write to 0x80\n");226		asm volatile ("outb %%al, $0x80" : : "a" (0));227 228		return 0;229	} else {230		int status;231		if (waitpid(child, &status, 0) != child ||232		    !WIFEXITED(status)) {233			printf("[FAIL]\tChild died\n");234			nerrs++;235		} else if (WEXITSTATUS(status) != 0) {236			printf("[FAIL]\tChild failed\n");237			nerrs++;238		} else {239			printf("[OK]\tChild succeeded\n");240		}241	}242 243	printf("[RUN]\tparent: write to 0x80 (should fail)\n");244 245	expect_gp_outb(0x80);246	expect_gp_sti(test_cli());247 248	/* Test the capability checks. */249	printf("\tiopl(3)\n");250	if (iopl(3) != 0)251		err(1, "iopl(3)");252 253	printf("\tDrop privileges\n");254	if (setresuid(1, 1, 1) != 0) {255		printf("[WARN]\tDropping privileges failed\n");256		goto done;257	}258 259	printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n");260	if (iopl(3) != 0) {261		printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n");262		nerrs++;263	}264 265	printf("[RUN]\tiopl(0) unprivileged\n");266	if (iopl(0) != 0) {267		printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n");268		nerrs++;269	}270 271	printf("[RUN]\tiopl(3) unprivileged\n");272	if (iopl(3) == 0) {273		printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n");274		nerrs++;275	} else {276		printf("[OK]\tFailed as expected\n");277	}278 279done:280	return nerrs ? 1 : 0;281}282