brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · ed91439 Raw
203 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Test that loads/stores expand the stack segment, or trigger a SEGV, in4 * various conditions.5 *6 * Based on test code by Tom Lane.7 */8 9#undef NDEBUG10#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 access38 * (load or store) at offset @delta from either the base of the stack or the39 * 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 not51		// 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#else57		asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp));58#endif59		target = stack_high - delta + 1;60		volatile char *p = (char *)target;61 62		if (type == STORE)63			*p = c;64		else65			c = *p;66 67		// Do something to prevent the stack frame being popped prior to68		// 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 signal144	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 rlimit162	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 fail168	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#else198int main(void)199{200	return test();201}202#endif203