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