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

stack_expansion_ldst.c (4459B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Test that loads/stores expand the stack segment, or trigger a SEGV, in
      4 * various conditions.
      5 *
      6 * Based on test code by Tom Lane.
      7 */
      8
      9#undef NDEBUG
     10#include <assert.h>
     11
     12#include <err.h>
     13#include <errno.h>
     14#include <stdio.h>
     15#include <signal.h>
     16#include <stdlib.h>
     17#include <string.h>
     18#include <sys/resource.h>
     19#include <sys/time.h>
     20#include <sys/types.h>
     21#include <sys/wait.h>
     22#include <unistd.h>
     23
     24#define _KB (1024)
     25#define _MB (1024 * 1024)
     26
     27volatile char *stack_top_ptr;
     28volatile unsigned long stack_top_sp;
     29volatile char c;
     30
     31enum access_type {
     32	LOAD,
     33	STORE,
     34};
     35
     36/*
     37 * Consume stack until the stack pointer is below @target_sp, then do an access
     38 * (load or store) at offset @delta from either the base of the stack or the
     39 * current stack pointer.
     40 */
     41__attribute__ ((noinline))
     42int consume_stack(unsigned long target_sp, unsigned long stack_high, int delta, enum access_type type)
     43{
     44	unsigned long target;
     45	char stack_cur;
     46
     47	if ((unsigned long)&stack_cur > target_sp)
     48		return consume_stack(target_sp, stack_high, delta, type);
     49	else {
     50		// We don't really need this, but without it GCC might not
     51		// generate a recursive call above.
     52		stack_top_ptr = &stack_cur;
     53
     54#ifdef __powerpc__
     55		asm volatile ("mr %[sp], %%r1" : [sp] "=r" (stack_top_sp));
     56#else
     57		asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp));
     58#endif
     59		target = stack_high - delta + 1;
     60		volatile char *p = (char *)target;
     61
     62		if (type == STORE)
     63			*p = c;
     64		else
     65			c = *p;
     66
     67		// Do something to prevent the stack frame being popped prior to
     68		// our access above.
     69		getpid();
     70	}
     71
     72	return 0;
     73}
     74
     75static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high)
     76{
     77	unsigned long start, end;
     78	static char buf[4096];
     79	char name[128];
     80	FILE *f;
     81	int rc;
     82
     83	f = fopen("/proc/self/maps", "r");
     84	if (!f) {
     85		perror("fopen");
     86		return -1;
     87	}
     88
     89	while (fgets(buf, sizeof(buf), f)) {
     90		rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n",
     91			    &start, &end, name);
     92		if (rc == 2)
     93			continue;
     94
     95		if (rc != 3) {
     96			printf("sscanf errored\n");
     97			rc = -1;
     98			break;
     99		}
    100
    101		if (strstr(name, needle)) {
    102			*low = start;
    103			*high = end - 1;
    104			rc = 0;
    105			break;
    106		}
    107	}
    108
    109	fclose(f);
    110
    111	return rc;
    112}
    113
    114int child(unsigned int stack_used, int delta, enum access_type type)
    115{
    116	unsigned long low, stack_high;
    117
    118	assert(search_proc_maps("[stack]", &low, &stack_high) == 0);
    119
    120	assert(consume_stack(stack_high - stack_used, stack_high, delta, type) == 0);
    121
    122	printf("Access OK: %s delta %-7d used size 0x%06x stack high 0x%lx top_ptr %p top sp 0x%lx actual used 0x%lx\n",
    123	       type == LOAD ? "load" : "store", delta, stack_used, stack_high,
    124	       stack_top_ptr, stack_top_sp, stack_high - stack_top_sp + 1);
    125
    126	return 0;
    127}
    128
    129static int test_one(unsigned int stack_used, int delta, enum access_type type)
    130{
    131	pid_t pid;
    132	int rc;
    133
    134	pid = fork();
    135	if (pid == 0)
    136		exit(child(stack_used, delta, type));
    137
    138	assert(waitpid(pid, &rc, 0) != -1);
    139
    140	if (WIFEXITED(rc) && WEXITSTATUS(rc) == 0)
    141		return 0;
    142
    143	// We don't expect a non-zero exit that's not a signal
    144	assert(!WIFEXITED(rc));
    145
    146	printf("Faulted:   %s delta %-7d used size 0x%06x signal %d\n",
    147	       type == LOAD ? "load" : "store", delta, stack_used,
    148	       WTERMSIG(rc));
    149
    150	return 1;
    151}
    152
    153// This is fairly arbitrary but is well below any of the targets below,
    154// so that the delta between the stack pointer and the target is large.
    155#define DEFAULT_SIZE	(32 * _KB)
    156
    157static void test_one_type(enum access_type type, unsigned long page_size, unsigned long rlim_cur)
    158{
    159	unsigned long delta;
    160
    161	// We should be able to access anywhere within the rlimit
    162	for (delta = page_size; delta <= rlim_cur; delta += page_size)
    163		assert(test_one(DEFAULT_SIZE, delta, type) == 0);
    164
    165	assert(test_one(DEFAULT_SIZE, rlim_cur, type) == 0);
    166
    167	// But if we go past the rlimit it should fail
    168	assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0);
    169}
    170
    171static int test(void)
    172{
    173	unsigned long page_size;
    174	struct rlimit rlimit;
    175
    176	page_size = getpagesize();
    177	getrlimit(RLIMIT_STACK, &rlimit);
    178	printf("Stack rlimit is 0x%lx\n", rlimit.rlim_cur);
    179
    180	printf("Testing loads ...\n");
    181	test_one_type(LOAD, page_size, rlimit.rlim_cur);
    182	printf("Testing stores ...\n");
    183	test_one_type(STORE, page_size, rlimit.rlim_cur);
    184
    185	printf("All OK\n");
    186
    187	return 0;
    188}
    189
    190#ifdef __powerpc__
    191#include "utils.h"
    192
    193int main(void)
    194{
    195	return test_harness(test, "stack_expansion_ldst");
    196}
    197#else
    198int main(void)
    199{
    200	return test();
    201}
    202#endif