1 // SPDX-License-Identifier: GPL-2.0
3 * Test that loads/stores expand the stack segment, or trigger a SEGV, in
6 * Based on test code by Tom Lane.
18 #include <sys/resource.h>
20 #include <sys/types.h>
25 #define _MB (1024 * 1024)
27 volatile char *stack_top_ptr
;
28 volatile unsigned long stack_top_sp
;
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.
41 __attribute__ ((noinline
))
42 int consume_stack(unsigned long target_sp
, unsigned long stack_high
, int delta
, enum access_type type
)
47 if ((unsigned long)&stack_cur
> target_sp
)
48 return consume_stack(target_sp
, stack_high
, delta
, type
);
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
;
55 asm volatile ("mr %[sp], %%r1" : [sp
] "=r" (stack_top_sp
));
57 asm volatile ("mov %%rsp, %[sp]" : [sp
] "=r" (stack_top_sp
));
59 target
= stack_high
- delta
+ 1;
60 volatile char *p
= (char *)target
;
67 // Do something to prevent the stack frame being popped prior to
75 static int search_proc_maps(char *needle
, unsigned long *low
, unsigned long *high
)
77 unsigned long start
, end
;
78 static char buf
[4096];
83 f
= fopen("/proc/self/maps", "r");
89 while (fgets(buf
, sizeof(buf
), f
)) {
90 rc
= sscanf(buf
, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n",
96 printf("sscanf errored\n");
101 if (strstr(name
, needle
)) {
114 int child(unsigned int stack_used
, int delta
, enum access_type type
)
116 unsigned long low
, stack_high
;
118 assert(search_proc_maps("[stack]", &low
, &stack_high
) == 0);
120 assert(consume_stack(stack_high
- stack_used
, stack_high
, delta
, type
) == 0);
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);
129 static int test_one(unsigned int stack_used
, int delta
, enum access_type type
)
136 exit(child(stack_used
, delta
, type
));
138 assert(waitpid(pid
, &rc
, 0) != -1);
140 if (WIFEXITED(rc
) && WEXITSTATUS(rc
) == 0)
143 // We don't expect a non-zero exit that's not a signal
144 assert(!WIFEXITED(rc
));
146 printf("Faulted: %s delta %-7d used size 0x%06x signal %d\n",
147 type
== LOAD
? "load" : "store", delta
, stack_used
,
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)
157 static void test_one_type(enum access_type type
, unsigned long page_size
, unsigned long rlim_cur
)
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);
165 assert(test_one(DEFAULT_SIZE
, rlim_cur
, type
) == 0);
167 // But if we go past the rlimit it should fail
168 assert(test_one(DEFAULT_SIZE
, rlim_cur
+ 1, type
) != 0);
171 static int test(void)
173 unsigned long page_size
;
174 struct rlimit rlimit
;
176 page_size
= getpagesize();
177 getrlimit(RLIMIT_STACK
, &rlimit
);
178 printf("Stack rlimit is 0x%lx\n", rlimit
.rlim_cur
);
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
);
195 return test_harness(test
, "stack_expansion_ldst");