cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ioperm.c (4051B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * ioperm.c - Test case for ioperm(2)
      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
     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(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(unsigned short port)
     79{
     80	if (try_outb(port)) {
     81		printf("[FAIL]\toutb to 0x%02hx worked\n", port);
     82		exit(1);
     83	}
     84
     85	printf("[OK]\toutb to 0x%02hx failed\n", port);
     86}
     87
     88int main(void)
     89{
     90	cpu_set_t cpuset;
     91	CPU_ZERO(&cpuset);
     92	CPU_SET(0, &cpuset);
     93	if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
     94		err(1, "sched_setaffinity to CPU 0");
     95
     96	expect_gp(0x80);
     97	expect_gp(0xed);
     98
     99	/*
    100	 * Probe for ioperm support.  Note that clearing ioperm bits
    101	 * works even as nonroot.
    102	 */
    103	printf("[RUN]\tenable 0x80\n");
    104	if (ioperm(0x80, 1, 1) != 0) {
    105		printf("[OK]\tioperm(0x80, 1, 1) failed (%d) -- try running as root\n",
    106		       errno);
    107		return 0;
    108	}
    109	expect_ok(0x80);
    110	expect_gp(0xed);
    111
    112	printf("[RUN]\tdisable 0x80\n");
    113	if (ioperm(0x80, 1, 0) != 0) {
    114		printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
    115		return 1;
    116	}
    117	expect_gp(0x80);
    118	expect_gp(0xed);
    119
    120	/* Make sure that fork() preserves ioperm. */
    121	if (ioperm(0x80, 1, 1) != 0) {
    122		printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
    123		return 1;
    124	}
    125
    126	pid_t child = fork();
    127	if (child == -1)
    128		err(1, "fork");
    129
    130	if (child == 0) {
    131		printf("[RUN]\tchild: check that we inherited permissions\n");
    132		expect_ok(0x80);
    133		expect_gp(0xed);
    134		printf("[RUN]\tchild: Extend permissions to 0x81\n");
    135		if (ioperm(0x81, 1, 1) != 0) {
    136			printf("[FAIL]\tioperm(0x81, 1, 1) failed (%d)", errno);
    137			return 1;
    138		}
    139		printf("[RUN]\tchild: Drop permissions to 0x80\n");
    140		if (ioperm(0x80, 1, 0) != 0) {
    141			printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
    142			return 1;
    143		}
    144		expect_gp(0x80);
    145		return 0;
    146	} else {
    147		int status;
    148		if (waitpid(child, &status, 0) != child ||
    149		    !WIFEXITED(status)) {
    150			printf("[FAIL]\tChild died\n");
    151			nerrs++;
    152		} else if (WEXITSTATUS(status) != 0) {
    153			printf("[FAIL]\tChild failed\n");
    154			nerrs++;
    155		} else {
    156			printf("[OK]\tChild succeeded\n");
    157		}
    158	}
    159
    160	/* Verify that the child dropping 0x80 did not affect the parent */
    161	printf("\tVerify that unsharing the bitmap worked\n");
    162	expect_ok(0x80);
    163
    164	/* Test the capability checks. */
    165	printf("\tDrop privileges\n");
    166	if (setresuid(1, 1, 1) != 0) {
    167		printf("[WARN]\tDropping privileges failed\n");
    168		return 0;
    169	}
    170
    171	printf("[RUN]\tdisable 0x80\n");
    172	if (ioperm(0x80, 1, 0) != 0) {
    173		printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno);
    174		return 1;
    175	}
    176	printf("[OK]\tit worked\n");
    177
    178	printf("[RUN]\tenable 0x80 again\n");
    179	if (ioperm(0x80, 1, 1) == 0) {
    180		printf("[FAIL]\tit succeeded but should have failed.\n");
    181		return 1;
    182	}
    183	printf("[OK]\tit failed\n");
    184	return 0;
    185}