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

pkey_siginfo.c (9196B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3/*
      4 * Copyright 2020, Sandipan Das, IBM Corp.
      5 *
      6 * Test if the signal information reports the correct memory protection
      7 * key upon getting a key access violation fault for a page that was
      8 * attempted to be protected by two different keys from two competing
      9 * threads at the same time.
     10 */
     11
     12#define _GNU_SOURCE
     13#include <stdio.h>
     14#include <stdlib.h>
     15#include <string.h>
     16#include <signal.h>
     17
     18#include <unistd.h>
     19#include <pthread.h>
     20#include <sys/mman.h>
     21
     22#include "pkeys.h"
     23
     24#define PPC_INST_NOP	0x60000000
     25#define PPC_INST_BLR	0x4e800020
     26#define PROT_RWX	(PROT_READ | PROT_WRITE | PROT_EXEC)
     27
     28#define NUM_ITERATIONS	1000000
     29
     30static volatile sig_atomic_t perm_pkey, rest_pkey;
     31static volatile sig_atomic_t rights, fault_count;
     32static volatile unsigned int *volatile fault_addr;
     33static pthread_barrier_t iteration_barrier;
     34
     35static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
     36{
     37	void *pgstart;
     38	size_t pgsize;
     39	int pkey;
     40
     41	pkey = siginfo_pkey(sinfo);
     42
     43	/* Check if this fault originated from a pkey access violation */
     44	if (sinfo->si_code != SEGV_PKUERR) {
     45		sigsafe_err("got a fault for an unexpected reason\n");
     46		_exit(1);
     47	}
     48
     49	/* Check if this fault originated from the expected address */
     50	if (sinfo->si_addr != (void *) fault_addr) {
     51		sigsafe_err("got a fault for an unexpected address\n");
     52		_exit(1);
     53	}
     54
     55	/* Check if this fault originated from the restrictive pkey */
     56	if (pkey != rest_pkey) {
     57		sigsafe_err("got a fault for an unexpected pkey\n");
     58		_exit(1);
     59	}
     60
     61	/* Check if too many faults have occurred for the same iteration */
     62	if (fault_count > 0) {
     63		sigsafe_err("got too many faults for the same address\n");
     64		_exit(1);
     65	}
     66
     67	pgsize = getpagesize();
     68	pgstart = (void *) ((unsigned long) fault_addr & ~(pgsize - 1));
     69
     70	/*
     71	 * If the current fault occurred due to lack of execute rights,
     72	 * reassociate the page with the exec-only pkey since execute
     73	 * rights cannot be changed directly for the faulting pkey as
     74	 * IAMR is inaccessible from userspace.
     75	 *
     76	 * Otherwise, if the current fault occurred due to lack of
     77	 * read-write rights, change the AMR permission bits for the
     78	 * pkey.
     79	 *
     80	 * This will let the test continue.
     81	 */
     82	if (rights == PKEY_DISABLE_EXECUTE &&
     83	    mprotect(pgstart, pgsize, PROT_EXEC))
     84		_exit(1);
     85	else
     86		pkey_set_rights(pkey, 0);
     87
     88	fault_count++;
     89}
     90
     91struct region {
     92	unsigned long rights;
     93	unsigned int *base;
     94	size_t size;
     95};
     96
     97static void *protect(void *p)
     98{
     99	unsigned long rights;
    100	unsigned int *base;
    101	size_t size;
    102	int tid, i;
    103
    104	tid = gettid();
    105	base = ((struct region *) p)->base;
    106	size = ((struct region *) p)->size;
    107	FAIL_IF_EXIT(!base);
    108
    109	/* No read, write and execute restrictions */
    110	rights = 0;
    111
    112	printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
    113
    114	/* Allocate the permissive pkey */
    115	perm_pkey = sys_pkey_alloc(0, rights);
    116	FAIL_IF_EXIT(perm_pkey < 0);
    117
    118	/*
    119	 * Repeatedly try to protect the common region with a permissive
    120	 * pkey
    121	 */
    122	for (i = 0; i < NUM_ITERATIONS; i++) {
    123		/*
    124		 * Wait until the other thread has finished allocating the
    125		 * restrictive pkey or until the next iteration has begun
    126		 */
    127		pthread_barrier_wait(&iteration_barrier);
    128
    129		/* Try to associate the permissive pkey with the region */
    130		FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
    131					       perm_pkey));
    132	}
    133
    134	/* Free the permissive pkey */
    135	sys_pkey_free(perm_pkey);
    136
    137	return NULL;
    138}
    139
    140static void *protect_access(void *p)
    141{
    142	size_t size, numinsns;
    143	unsigned int *base;
    144	int tid, i;
    145
    146	tid = gettid();
    147	base = ((struct region *) p)->base;
    148	size = ((struct region *) p)->size;
    149	rights = ((struct region *) p)->rights;
    150	numinsns = size / sizeof(base[0]);
    151	FAIL_IF_EXIT(!base);
    152
    153	/* Allocate the restrictive pkey */
    154	rest_pkey = sys_pkey_alloc(0, rights);
    155	FAIL_IF_EXIT(rest_pkey < 0);
    156
    157	printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights));
    158	printf("tid %d, %s randomly in range [%p, %p]\n", tid,
    159	       (rights == PKEY_DISABLE_EXECUTE) ? "execute" :
    160	       (rights == PKEY_DISABLE_WRITE)  ? "write" : "read",
    161	       base, base + numinsns);
    162
    163	/*
    164	 * Repeatedly try to protect the common region with a restrictive
    165	 * pkey and read, write or execute from it
    166	 */
    167	for (i = 0; i < NUM_ITERATIONS; i++) {
    168		/*
    169		 * Wait until the other thread has finished allocating the
    170		 * permissive pkey or until the next iteration has begun
    171		 */
    172		pthread_barrier_wait(&iteration_barrier);
    173
    174		/* Try to associate the restrictive pkey with the region */
    175		FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX,
    176					       rest_pkey));
    177
    178		/* Choose a random instruction word address from the region */
    179		fault_addr = base + (rand() % numinsns);
    180		fault_count = 0;
    181
    182		switch (rights) {
    183		/* Read protection test */
    184		case PKEY_DISABLE_ACCESS:
    185			/*
    186			 * Read an instruction word from the region and
    187			 * verify if it has not been overwritten to
    188			 * something unexpected
    189			 */
    190			FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP &&
    191				     *fault_addr != PPC_INST_BLR);
    192			break;
    193
    194		/* Write protection test */
    195		case PKEY_DISABLE_WRITE:
    196			/*
    197			 * Write an instruction word to the region and
    198			 * verify if the overwrite has succeeded
    199			 */
    200			*fault_addr = PPC_INST_BLR;
    201			FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR);
    202			break;
    203
    204		/* Execute protection test */
    205		case PKEY_DISABLE_EXECUTE:
    206			/* Jump to the region and execute instructions */
    207			asm volatile(
    208				"mtctr	%0; bctrl"
    209				: : "r"(fault_addr) : "ctr", "lr");
    210			break;
    211		}
    212
    213		/*
    214		 * Restore the restrictions originally imposed by the
    215		 * restrictive pkey as the signal handler would have
    216		 * cleared out the corresponding AMR bits
    217		 */
    218		pkey_set_rights(rest_pkey, rights);
    219	}
    220
    221	/* Free restrictive pkey */
    222	sys_pkey_free(rest_pkey);
    223
    224	return NULL;
    225}
    226
    227static void reset_pkeys(unsigned long rights)
    228{
    229	int pkeys[NR_PKEYS], i;
    230
    231	/* Exhaustively allocate all available pkeys */
    232	for (i = 0; i < NR_PKEYS; i++)
    233		pkeys[i] = sys_pkey_alloc(0, rights);
    234
    235	/* Free all allocated pkeys */
    236	for (i = 0; i < NR_PKEYS; i++)
    237		sys_pkey_free(pkeys[i]);
    238}
    239
    240static int test(void)
    241{
    242	pthread_t prot_thread, pacc_thread;
    243	struct sigaction act;
    244	pthread_attr_t attr;
    245	size_t numinsns;
    246	struct region r;
    247	int ret, i;
    248
    249	srand(time(NULL));
    250	ret = pkeys_unsupported();
    251	if (ret)
    252		return ret;
    253
    254	/* Allocate the region */
    255	r.size = getpagesize();
    256	r.base = mmap(NULL, r.size, PROT_RWX,
    257		      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    258	FAIL_IF(r.base == MAP_FAILED);
    259
    260	/*
    261	 * Fill the region with no-ops with a branch at the end
    262	 * for returning to the caller
    263	 */
    264	numinsns = r.size / sizeof(r.base[0]);
    265	for (i = 0; i < numinsns - 1; i++)
    266		r.base[i] = PPC_INST_NOP;
    267	r.base[i] = PPC_INST_BLR;
    268
    269	/* Setup SIGSEGV handler */
    270	act.sa_handler = 0;
    271	act.sa_sigaction = segv_handler;
    272	FAIL_IF(sigprocmask(SIG_SETMASK, 0, &act.sa_mask) != 0);
    273	act.sa_flags = SA_SIGINFO;
    274	act.sa_restorer = 0;
    275	FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0);
    276
    277	/*
    278	 * For these tests, the parent process should clear all bits of
    279	 * AMR and IAMR, i.e. impose no restrictions, for all available
    280	 * pkeys. This will be the base for the initial AMR and IAMR
    281	 * values for all the test thread pairs.
    282	 *
    283	 * If the AMR and IAMR bits of all available pkeys are cleared
    284	 * before running the tests and a fault is generated when
    285	 * attempting to read, write or execute instructions from a
    286	 * pkey protected region, the pkey responsible for this must be
    287	 * the one from the protect-and-access thread since the other
    288	 * one is fully permissive. Despite that, if the pkey reported
    289	 * by siginfo is not the restrictive pkey, then there must be a
    290	 * kernel bug.
    291	 */
    292	reset_pkeys(0);
    293
    294	/* Setup barrier for protect and protect-and-access threads */
    295	FAIL_IF(pthread_attr_init(&attr) != 0);
    296	FAIL_IF(pthread_barrier_init(&iteration_barrier, NULL, 2) != 0);
    297
    298	/* Setup and start protect and protect-and-read threads */
    299	puts("starting thread pair (protect, protect-and-read)");
    300	r.rights = PKEY_DISABLE_ACCESS;
    301	FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
    302	FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
    303	FAIL_IF(pthread_join(prot_thread, NULL) != 0);
    304	FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
    305
    306	/* Setup and start protect and protect-and-write threads */
    307	puts("starting thread pair (protect, protect-and-write)");
    308	r.rights = PKEY_DISABLE_WRITE;
    309	FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
    310	FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
    311	FAIL_IF(pthread_join(prot_thread, NULL) != 0);
    312	FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
    313
    314	/* Setup and start protect and protect-and-execute threads */
    315	puts("starting thread pair (protect, protect-and-execute)");
    316	r.rights = PKEY_DISABLE_EXECUTE;
    317	FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0);
    318	FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0);
    319	FAIL_IF(pthread_join(prot_thread, NULL) != 0);
    320	FAIL_IF(pthread_join(pacc_thread, NULL) != 0);
    321
    322	/* Cleanup */
    323	FAIL_IF(pthread_attr_destroy(&attr) != 0);
    324	FAIL_IF(pthread_barrier_destroy(&iteration_barrier) != 0);
    325	munmap(r.base, r.size);
    326
    327	return 0;
    328}
    329
    330int main(void)
    331{
    332	return test_harness(test, "pkey_siginfo");
    333}