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