Linux Audio

Check our new training course

Linux BSP development engineering services

Need help to port Linux and bootloaders to your hardware?
Loading...
  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
  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