1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker * Copyright (c) Zilogic Systems Pvt. Ltd., 2020
4*49cdfc7eSAndroid Build Coastguard Worker * Email: [email protected]
5*49cdfc7eSAndroid Build Coastguard Worker */
6*49cdfc7eSAndroid Build Coastguard Worker
7*49cdfc7eSAndroid Build Coastguard Worker /*
8*49cdfc7eSAndroid Build Coastguard Worker * Test mmap() MAP_GROWSDOWN flag
9*49cdfc7eSAndroid Build Coastguard Worker *
10*49cdfc7eSAndroid Build Coastguard Worker * # Test1:
11*49cdfc7eSAndroid Build Coastguard Worker *
12*49cdfc7eSAndroid Build Coastguard Worker * We assign the memory region partially allocated with MAP_GROWSDOWN flag to
13*49cdfc7eSAndroid Build Coastguard Worker * a thread as a stack and expect the mapping to grow when we touch the
14*49cdfc7eSAndroid Build Coastguard Worker * guard page by calling a recusive function in the thread that uses the
15*49cdfc7eSAndroid Build Coastguard Worker * growable mapping as a stack.
16*49cdfc7eSAndroid Build Coastguard Worker *
17*49cdfc7eSAndroid Build Coastguard Worker * The kernel only grows the memory region when the stack pointer is within
18*49cdfc7eSAndroid Build Coastguard Worker * guard page when the guard page is touched so simply faulting the guard
19*49cdfc7eSAndroid Build Coastguard Worker * page will not cause the mapping to grow.
20*49cdfc7eSAndroid Build Coastguard Worker *
21*49cdfc7eSAndroid Build Coastguard Worker * Newer kernels does not allow a MAP_GROWSDOWN mapping to grow closer than
22*49cdfc7eSAndroid Build Coastguard Worker * 'stack_guard_gap' pages to an existing mapping. So when we map the stack we
23*49cdfc7eSAndroid Build Coastguard Worker * make sure there is enough of free address space before the lowest stack
24*49cdfc7eSAndroid Build Coastguard Worker * address.
25*49cdfc7eSAndroid Build Coastguard Worker *
26*49cdfc7eSAndroid Build Coastguard Worker * Kernel default 'stack_guard_gap' size is '256 * getpagesize()'.
27*49cdfc7eSAndroid Build Coastguard Worker *
28*49cdfc7eSAndroid Build Coastguard Worker * The stack memory map would look like:
29*49cdfc7eSAndroid Build Coastguard Worker *
30*49cdfc7eSAndroid Build Coastguard Worker * | - - - reserved size - - - |
31*49cdfc7eSAndroid Build Coastguard Worker *
32*49cdfc7eSAndroid Build Coastguard Worker * +-- - - - --+------------+-------------+
33*49cdfc7eSAndroid Build Coastguard Worker * | 256 pages | unmapped | mapped |
34*49cdfc7eSAndroid Build Coastguard Worker * +-- - - - --+------------+-------------+
35*49cdfc7eSAndroid Build Coastguard Worker * | mapped size |
36*49cdfc7eSAndroid Build Coastguard Worker * ^ | - - stack size - - |
37*49cdfc7eSAndroid Build Coastguard Worker * start
38*49cdfc7eSAndroid Build Coastguard Worker * ^ ^
39*49cdfc7eSAndroid Build Coastguard Worker * stack bottom stack top
40*49cdfc7eSAndroid Build Coastguard Worker *
41*49cdfc7eSAndroid Build Coastguard Worker * # Test2:
42*49cdfc7eSAndroid Build Coastguard Worker *
43*49cdfc7eSAndroid Build Coastguard Worker * We allocate stack as we do in the first test but we mmap a page in the
44*49cdfc7eSAndroid Build Coastguard Worker * space the stack is supposed to grow into and we expect the thread to
45*49cdfc7eSAndroid Build Coastguard Worker * segfault when the guard page is faulted.
46*49cdfc7eSAndroid Build Coastguard Worker */
47*49cdfc7eSAndroid Build Coastguard Worker
48*49cdfc7eSAndroid Build Coastguard Worker #include <unistd.h>
49*49cdfc7eSAndroid Build Coastguard Worker #include <pthread.h>
50*49cdfc7eSAndroid Build Coastguard Worker #include <sys/mman.h>
51*49cdfc7eSAndroid Build Coastguard Worker #include <sys/wait.h>
52*49cdfc7eSAndroid Build Coastguard Worker #include <sys/types.h>
53*49cdfc7eSAndroid Build Coastguard Worker #include <stdlib.h>
54*49cdfc7eSAndroid Build Coastguard Worker #include <stdbool.h>
55*49cdfc7eSAndroid Build Coastguard Worker
56*49cdfc7eSAndroid Build Coastguard Worker #include "tst_test.h"
57*49cdfc7eSAndroid Build Coastguard Worker #include "tst_safe_pthread.h"
58*49cdfc7eSAndroid Build Coastguard Worker
59*49cdfc7eSAndroid Build Coastguard Worker static long page_size;
60*49cdfc7eSAndroid Build Coastguard Worker
check_stackgrow_up(void)61*49cdfc7eSAndroid Build Coastguard Worker static bool __attribute__((noinline)) check_stackgrow_up(void)
62*49cdfc7eSAndroid Build Coastguard Worker {
63*49cdfc7eSAndroid Build Coastguard Worker char local_var;
64*49cdfc7eSAndroid Build Coastguard Worker static char *addr;
65*49cdfc7eSAndroid Build Coastguard Worker
66*49cdfc7eSAndroid Build Coastguard Worker if (!addr) {
67*49cdfc7eSAndroid Build Coastguard Worker addr = &local_var;
68*49cdfc7eSAndroid Build Coastguard Worker return check_stackgrow_up();
69*49cdfc7eSAndroid Build Coastguard Worker }
70*49cdfc7eSAndroid Build Coastguard Worker
71*49cdfc7eSAndroid Build Coastguard Worker return (addr < &local_var);
72*49cdfc7eSAndroid Build Coastguard Worker }
73*49cdfc7eSAndroid Build Coastguard Worker
setup(void)74*49cdfc7eSAndroid Build Coastguard Worker static void setup(void)
75*49cdfc7eSAndroid Build Coastguard Worker {
76*49cdfc7eSAndroid Build Coastguard Worker if (check_stackgrow_up())
77*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "Test can't be performed with stack grows up architecture");
78*49cdfc7eSAndroid Build Coastguard Worker
79*49cdfc7eSAndroid Build Coastguard Worker page_size = getpagesize();
80*49cdfc7eSAndroid Build Coastguard Worker }
81*49cdfc7eSAndroid Build Coastguard Worker
82*49cdfc7eSAndroid Build Coastguard Worker /*
83*49cdfc7eSAndroid Build Coastguard Worker * Returns stack lowest address. Note that the address is not mapped and will
84*49cdfc7eSAndroid Build Coastguard Worker * be mapped on page fault when we grow the stack to the lowest address possible.
85*49cdfc7eSAndroid Build Coastguard Worker */
allocate_stack(size_t stack_size,size_t mapped_size)86*49cdfc7eSAndroid Build Coastguard Worker static void *allocate_stack(size_t stack_size, size_t mapped_size)
87*49cdfc7eSAndroid Build Coastguard Worker {
88*49cdfc7eSAndroid Build Coastguard Worker void *start, *stack_top, *stack_bottom;
89*49cdfc7eSAndroid Build Coastguard Worker
90*49cdfc7eSAndroid Build Coastguard Worker long reserved_size = 256 * page_size + stack_size;
91*49cdfc7eSAndroid Build Coastguard Worker
92*49cdfc7eSAndroid Build Coastguard Worker start = SAFE_MMAP(NULL, reserved_size, PROT_READ | PROT_WRITE,
93*49cdfc7eSAndroid Build Coastguard Worker MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
94*49cdfc7eSAndroid Build Coastguard Worker SAFE_MUNMAP(start, reserved_size);
95*49cdfc7eSAndroid Build Coastguard Worker
96*49cdfc7eSAndroid Build Coastguard Worker SAFE_MMAP((start + reserved_size - mapped_size), mapped_size, PROT_READ | PROT_WRITE,
97*49cdfc7eSAndroid Build Coastguard Worker MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN,
98*49cdfc7eSAndroid Build Coastguard Worker -1, 0);
99*49cdfc7eSAndroid Build Coastguard Worker
100*49cdfc7eSAndroid Build Coastguard Worker stack_top = start + reserved_size;
101*49cdfc7eSAndroid Build Coastguard Worker stack_bottom = start + reserved_size - stack_size;
102*49cdfc7eSAndroid Build Coastguard Worker
103*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "start = %p, stack_top = %p, stack bottom = %p",
104*49cdfc7eSAndroid Build Coastguard Worker start, stack_top, stack_bottom);
105*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "mapped pages %zu, stack pages %zu",
106*49cdfc7eSAndroid Build Coastguard Worker mapped_size/page_size, stack_size/page_size);
107*49cdfc7eSAndroid Build Coastguard Worker
108*49cdfc7eSAndroid Build Coastguard Worker return stack_bottom;
109*49cdfc7eSAndroid Build Coastguard Worker }
110*49cdfc7eSAndroid Build Coastguard Worker
check_depth_recursive(void * limit)111*49cdfc7eSAndroid Build Coastguard Worker static __attribute__((noinline)) void *check_depth_recursive(void *limit)
112*49cdfc7eSAndroid Build Coastguard Worker {
113*49cdfc7eSAndroid Build Coastguard Worker if ((off_t) &limit < (off_t) limit) {
114*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "&limit = %p, limit = %p", &limit, limit);
115*49cdfc7eSAndroid Build Coastguard Worker return NULL;
116*49cdfc7eSAndroid Build Coastguard Worker }
117*49cdfc7eSAndroid Build Coastguard Worker
118*49cdfc7eSAndroid Build Coastguard Worker return check_depth_recursive(limit);
119*49cdfc7eSAndroid Build Coastguard Worker }
120*49cdfc7eSAndroid Build Coastguard Worker
121*49cdfc7eSAndroid Build Coastguard Worker /*
122*49cdfc7eSAndroid Build Coastguard Worker * We set the limit one page above the stack bottom to make sure that the stack
123*49cdfc7eSAndroid Build Coastguard Worker * frame will not overflow to the next page, which would potentially cause
124*49cdfc7eSAndroid Build Coastguard Worker * segfault if we are unlucky and there is a mapping right after the guard gap.
125*49cdfc7eSAndroid Build Coastguard Worker *
126*49cdfc7eSAndroid Build Coastguard Worker * Generally the stack frame would be much smaller than page_size so moving the
127*49cdfc7eSAndroid Build Coastguard Worker * pointer by a few bytes would probably be enough, but we do not want to take
128*49cdfc7eSAndroid Build Coastguard Worker * any chances.
129*49cdfc7eSAndroid Build Coastguard Worker */
grow_stack(void * stack,size_t size)130*49cdfc7eSAndroid Build Coastguard Worker static void grow_stack(void *stack, size_t size)
131*49cdfc7eSAndroid Build Coastguard Worker {
132*49cdfc7eSAndroid Build Coastguard Worker pthread_t test_thread;
133*49cdfc7eSAndroid Build Coastguard Worker pthread_attr_t attr;
134*49cdfc7eSAndroid Build Coastguard Worker int ret;
135*49cdfc7eSAndroid Build Coastguard Worker void *limit = stack + page_size;
136*49cdfc7eSAndroid Build Coastguard Worker
137*49cdfc7eSAndroid Build Coastguard Worker ret = pthread_attr_init(&attr);
138*49cdfc7eSAndroid Build Coastguard Worker if (ret)
139*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK, "pthread_attr_init failed during setup");
140*49cdfc7eSAndroid Build Coastguard Worker
141*49cdfc7eSAndroid Build Coastguard Worker ret = pthread_attr_setstack(&attr, stack, size);
142*49cdfc7eSAndroid Build Coastguard Worker if (ret)
143*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK, "pthread_attr_setstack failed during setup");
144*49cdfc7eSAndroid Build Coastguard Worker
145*49cdfc7eSAndroid Build Coastguard Worker SAFE_PTHREAD_CREATE(&test_thread, &attr, check_depth_recursive, limit);
146*49cdfc7eSAndroid Build Coastguard Worker SAFE_PTHREAD_JOIN(test_thread, NULL);
147*49cdfc7eSAndroid Build Coastguard Worker
148*49cdfc7eSAndroid Build Coastguard Worker exit(0);
149*49cdfc7eSAndroid Build Coastguard Worker }
150*49cdfc7eSAndroid Build Coastguard Worker
grow_stack_success(size_t stack_size,size_t mapped_size)151*49cdfc7eSAndroid Build Coastguard Worker static void grow_stack_success(size_t stack_size, size_t mapped_size)
152*49cdfc7eSAndroid Build Coastguard Worker {
153*49cdfc7eSAndroid Build Coastguard Worker pid_t child_pid;
154*49cdfc7eSAndroid Build Coastguard Worker int wstatus;
155*49cdfc7eSAndroid Build Coastguard Worker void *stack;
156*49cdfc7eSAndroid Build Coastguard Worker
157*49cdfc7eSAndroid Build Coastguard Worker child_pid = SAFE_FORK();
158*49cdfc7eSAndroid Build Coastguard Worker if (!child_pid) {
159*49cdfc7eSAndroid Build Coastguard Worker stack = allocate_stack(stack_size, mapped_size);
160*49cdfc7eSAndroid Build Coastguard Worker grow_stack(stack, stack_size);
161*49cdfc7eSAndroid Build Coastguard Worker }
162*49cdfc7eSAndroid Build Coastguard Worker
163*49cdfc7eSAndroid Build Coastguard Worker SAFE_WAIT(&wstatus);
164*49cdfc7eSAndroid Build Coastguard Worker if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)
165*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "Stack grows in unmapped region");
166*49cdfc7eSAndroid Build Coastguard Worker else
167*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "Child: %s", tst_strstatus(wstatus));
168*49cdfc7eSAndroid Build Coastguard Worker
169*49cdfc7eSAndroid Build Coastguard Worker }
170*49cdfc7eSAndroid Build Coastguard Worker
171*49cdfc7eSAndroid Build Coastguard Worker /*
172*49cdfc7eSAndroid Build Coastguard Worker * We map a page at the bottom of the stack which will cause the thread to be
173*49cdfc7eSAndroid Build Coastguard Worker * killed with SIGSEGV on faulting the guard page.
174*49cdfc7eSAndroid Build Coastguard Worker */
grow_stack_fail(size_t stack_size,size_t mapped_size)175*49cdfc7eSAndroid Build Coastguard Worker static void grow_stack_fail(size_t stack_size, size_t mapped_size)
176*49cdfc7eSAndroid Build Coastguard Worker {
177*49cdfc7eSAndroid Build Coastguard Worker pid_t child_pid;
178*49cdfc7eSAndroid Build Coastguard Worker int wstatus;
179*49cdfc7eSAndroid Build Coastguard Worker void *stack;
180*49cdfc7eSAndroid Build Coastguard Worker
181*49cdfc7eSAndroid Build Coastguard Worker child_pid = SAFE_FORK();
182*49cdfc7eSAndroid Build Coastguard Worker if (!child_pid) {
183*49cdfc7eSAndroid Build Coastguard Worker tst_no_corefile(0);
184*49cdfc7eSAndroid Build Coastguard Worker stack = allocate_stack(stack_size, mapped_size);
185*49cdfc7eSAndroid Build Coastguard Worker
186*49cdfc7eSAndroid Build Coastguard Worker SAFE_MMAP(stack, page_size, PROT_READ | PROT_WRITE,
187*49cdfc7eSAndroid Build Coastguard Worker MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
188*49cdfc7eSAndroid Build Coastguard Worker
189*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "mapped page at %p", stack);
190*49cdfc7eSAndroid Build Coastguard Worker
191*49cdfc7eSAndroid Build Coastguard Worker grow_stack(stack, stack_size);
192*49cdfc7eSAndroid Build Coastguard Worker }
193*49cdfc7eSAndroid Build Coastguard Worker
194*49cdfc7eSAndroid Build Coastguard Worker SAFE_WAIT(&wstatus);
195*49cdfc7eSAndroid Build Coastguard Worker if (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGSEGV)
196*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "Child killed by %s as expected", tst_strsig(SIGSEGV));
197*49cdfc7eSAndroid Build Coastguard Worker else
198*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "Child: %s", tst_strstatus(wstatus));
199*49cdfc7eSAndroid Build Coastguard Worker }
200*49cdfc7eSAndroid Build Coastguard Worker
run_test(void)201*49cdfc7eSAndroid Build Coastguard Worker static void run_test(void)
202*49cdfc7eSAndroid Build Coastguard Worker {
203*49cdfc7eSAndroid Build Coastguard Worker size_t pthread_stack = LTP_ALIGN(PTHREAD_STACK_MIN, getpagesize());
204*49cdfc7eSAndroid Build Coastguard Worker size_t stack_size = 8 * pthread_stack;
205*49cdfc7eSAndroid Build Coastguard Worker
206*49cdfc7eSAndroid Build Coastguard Worker grow_stack_success(stack_size, pthread_stack);
207*49cdfc7eSAndroid Build Coastguard Worker grow_stack_success(stack_size, stack_size/2);
208*49cdfc7eSAndroid Build Coastguard Worker grow_stack_fail(stack_size, pthread_stack);
209*49cdfc7eSAndroid Build Coastguard Worker grow_stack_fail(stack_size, stack_size/2);
210*49cdfc7eSAndroid Build Coastguard Worker }
211*49cdfc7eSAndroid Build Coastguard Worker
212*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
213*49cdfc7eSAndroid Build Coastguard Worker .setup = setup,
214*49cdfc7eSAndroid Build Coastguard Worker .test_all = run_test,
215*49cdfc7eSAndroid Build Coastguard Worker .forks_child = 1,
216*49cdfc7eSAndroid Build Coastguard Worker };
217