1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Arnaud Le Blanc <arnaud.lb@gmail.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* Inspired from Chromium's stack_util.cc */
20
21 #include "zend.h"
22 #include "zend_globals.h"
23 #include "zend_portability.h"
24 #include "zend_call_stack.h"
25 #include <stdint.h>
26 #ifdef ZEND_WIN32
27 # include <processthreadsapi.h>
28 # include <memoryapi.h>
29 #else /* ZEND_WIN32 */
30 # include <sys/resource.h>
31 # ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 # endif
34 # ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 # endif
37 #endif /* ZEND_WIN32 */
38 #if (defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)) || \
39 defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__)
40 # include <pthread.h>
41 #endif
42 #ifdef __FreeBSD__
43 # include <pthread_np.h>
44 # include <sys/mman.h>
45 # include <sys/sysctl.h>
46 # include <sys/user.h>
47 #endif
48 #ifdef __OpenBSD__
49 typedef int boolean_t;
50 # include <tib.h>
51 # include <pthread_np.h>
52 # include <sys/sysctl.h>
53 # include <sys/user.h>
54 #endif
55 #ifdef __linux__
56 #include <sys/syscall.h>
57 #endif
58
59 #ifdef ZEND_CHECK_STACK_LIMIT
60
61 /* Called once per process or thread */
zend_call_stack_init(void)62 ZEND_API void zend_call_stack_init(void) {
63 if (!zend_call_stack_get(&EG(call_stack))) {
64 EG(call_stack) = (zend_call_stack){0};
65 }
66
67 switch (EG(max_allowed_stack_size)) {
68 case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: {
69 void *base = EG(call_stack).base;
70 size_t size = EG(call_stack).max_size;
71 if (UNEXPECTED(base == (void*)0)) {
72 base = zend_call_stack_position();
73 size = zend_call_stack_default_size();
74 /* base is not the actual stack base */
75 size -= 32 * 1024;
76 }
77 EG(stack_base) = base;
78 EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size));
79 break;
80 }
81 case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: {
82 EG(stack_base) = (void*)0;
83 EG(stack_limit) = (void*)0;
84 break;
85 }
86 default: {
87 ZEND_ASSERT(EG(max_allowed_stack_size) > 0);
88 void *base = EG(call_stack).base;
89 if (UNEXPECTED(base == (void*)0)) {
90 base = zend_call_stack_position();
91 }
92 EG(stack_base) = base;
93 EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size));
94 break;
95 }
96 }
97 }
98
99 #ifdef __linux__
zend_call_stack_is_main_thread(void)100 static bool zend_call_stack_is_main_thread(void) {
101 # ifdef HAVE_GETTID
102 return getpid() == gettid();
103 # else
104 return getpid() == syscall(SYS_gettid);
105 # endif
106 }
107
108 # if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
zend_call_stack_get_linux_pthread(zend_call_stack * stack)109 static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
110 {
111 pthread_attr_t attr;
112 int error;
113 void *addr;
114 size_t max_size;
115
116 /* pthread_getattr_np() will return bogus values for the main thread with
117 * musl or with some old glibc versions */
118 ZEND_ASSERT(!zend_call_stack_is_main_thread());
119
120 error = pthread_getattr_np(pthread_self(), &attr);
121 if (error) {
122 return false;
123 }
124
125 error = pthread_attr_getstack(&attr, &addr, &max_size);
126 if (error) {
127 pthread_attr_destroy(&attr);
128 return false;
129 }
130
131 # if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
132 {
133 size_t guard_size;
134 /* In glibc prior to 2.8, addr and size include the guard pages */
135 error = pthread_attr_getguardsize(&attr, &guard_size);
136 if (error) {
137 pthread_attr_destroy(&attr);
138 return false;
139 }
140
141 addr = (int8_t*)addr + guard_size;
142 max_size -= guard_size;
143 }
144 # endif /* glibc < 2.8 */
145
146 stack->base = (int8_t*)addr + max_size;
147 stack->max_size = max_size;
148
149 pthread_attr_destroy(&attr);
150
151 return true;
152 }
153 # else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
zend_call_stack_get_linux_pthread(zend_call_stack * stack)154 static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
155 {
156 return false;
157 }
158 # endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
159
zend_call_stack_get_linux_proc_maps(zend_call_stack * stack)160 static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack)
161 {
162 FILE *f;
163 char buffer[4096];
164 uintptr_t addr_on_stack = (uintptr_t)&buffer;
165 uintptr_t start, end, prev_end = 0;
166 size_t max_size;
167 bool found = false;
168 struct rlimit rlim;
169 int error;
170
171 /* This method is relevant only for the main thread */
172 ZEND_ASSERT(zend_call_stack_is_main_thread());
173
174 /* Scan the process memory mappings to find the one containing the stack.
175 *
176 * The end of the stack mapping is the base of the stack. The start is
177 * adjusted by the kernel as the stack grows. The maximum stack size is
178 * determined by RLIMIT_STACK and the previous mapping.
179 *
180 *
181 * ^ Higher addresses ^
182 * : :
183 * : :
184 * Mapping end --> |-------------------| <-- Stack base (stack start)
185 * | | ^
186 * | Stack Mapping | | Stack size
187 * | | v
188 * Mapping start --> |-------------------| <-- Current stack end
189 * (adjusted : :
190 * downwards as the . .
191 * stack grows) : :
192 * |-------------------|
193 * | Some Mapping | The previous mapping may prevent
194 * |-------------------| stack growth
195 * : :
196 * : :
197 * v Lower addresses v
198 */
199
200 f = fopen("/proc/self/maps", "r");
201 if (!f) {
202 return false;
203 }
204
205 while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) {
206 if (start <= addr_on_stack && end >= addr_on_stack) {
207 found = true;
208 break;
209 }
210 prev_end = end;
211 }
212
213 fclose(f);
214
215 if (!found) {
216 return false;
217 }
218
219 error = getrlimit(RLIMIT_STACK, &rlim);
220 if (error || rlim.rlim_cur == RLIM_INFINITY) {
221 return false;
222 }
223
224 max_size = rlim.rlim_cur;
225
226 /* Previous mapping may prevent the stack from growing */
227 if (end - max_size < prev_end) {
228 max_size = prev_end - end;
229 }
230
231 stack->base = (void*)end;
232 stack->max_size = max_size;
233
234 return true;
235 }
236
zend_call_stack_get_linux(zend_call_stack * stack)237 static bool zend_call_stack_get_linux(zend_call_stack *stack)
238 {
239 if (zend_call_stack_is_main_thread()) {
240 return zend_call_stack_get_linux_proc_maps(stack);
241 }
242
243 return zend_call_stack_get_linux_pthread(stack);
244 }
245 #else /* __linux__ */
zend_call_stack_get_linux(zend_call_stack * stack)246 static bool zend_call_stack_get_linux(zend_call_stack *stack)
247 {
248 return false;
249 }
250 #endif /* __linux__ */
251
252 #ifdef __FreeBSD__
zend_call_stack_is_main_thread(void)253 static bool zend_call_stack_is_main_thread(void)
254 {
255 int is_main = pthread_main_np();
256 return is_main == -1 || is_main == 1;
257 }
258
259 # if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
zend_call_stack_get_freebsd_pthread(zend_call_stack * stack)260 static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
261 {
262 pthread_attr_t attr;
263 int error;
264 void *addr;
265 size_t max_size;
266
267 /* pthread will return bogus values for the main thread */
268 ZEND_ASSERT(!zend_call_stack_is_main_thread());
269
270 pthread_attr_init(&attr);
271
272 error = pthread_attr_get_np(pthread_self(), &attr);
273 if (error) {
274 goto fail;
275 }
276
277 error = pthread_attr_getstack(&attr, &addr, &max_size);
278 if (error) {
279 goto fail;
280 }
281
282 stack->base = (int8_t*)addr + max_size;
283 stack->max_size = max_size;
284
285 pthread_attr_destroy(&attr);
286 return true;
287
288 fail:
289 pthread_attr_destroy(&attr);
290 return false;
291 }
292 # else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
zend_call_stack_get_freebsd_pthread(zend_call_stack * stack)293 static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
294 {
295 return false;
296 }
297 # endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
298
zend_call_stack_get_freebsd_sysctl(zend_call_stack * stack)299 static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack)
300 {
301 void *stack_base;
302 int mib[2] = {CTL_KERN, KERN_USRSTACK};
303 size_t len = sizeof(stack_base);
304 struct rlimit rlim;
305
306 /* This method is relevant only for the main thread */
307 ZEND_ASSERT(zend_call_stack_is_main_thread());
308
309 if (sysctl(mib, sizeof(mib)/sizeof(*mib), &stack_base, &len, NULL, 0) != 0) {
310 return false;
311 }
312
313 if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
314 return false;
315 }
316
317 if (rlim.rlim_cur == RLIM_INFINITY) {
318 return false;
319 }
320
321 size_t guard_size = getpagesize();
322
323 stack->base = stack_base;
324 stack->max_size = rlim.rlim_cur - guard_size;
325
326 return true;
327 }
328
zend_call_stack_get_freebsd(zend_call_stack * stack)329 static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
330 {
331 if (zend_call_stack_is_main_thread()) {
332 return zend_call_stack_get_freebsd_sysctl(stack);
333 }
334
335 return zend_call_stack_get_freebsd_pthread(stack);
336 }
337 #else
zend_call_stack_get_freebsd(zend_call_stack * stack)338 static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
339 {
340 return false;
341 }
342 #endif /* __FreeBSD__ */
343
344 #ifdef ZEND_WIN32
zend_call_stack_get_win32(zend_call_stack * stack)345 static bool zend_call_stack_get_win32(zend_call_stack *stack)
346 {
347 ULONG_PTR low_limit, high_limit;
348 ULONG size;
349 MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0};
350 size_t result_size, page_size;
351
352 /* The stack consists of three regions: committed, guard, and uncommitted.
353 * Memory is committed when the guard region is accessed. If only one page
354 * is left in the uncommitted region, a stack overflow error is raised
355 * instead.
356 *
357 * The total useable stack size is the size of the committed and uncommitted
358 * regions less one page.
359 *
360 * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx
361 * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size
362 *
363 * ^ Higher addresses ^
364 * : :
365 * : :
366 * high_limit --> |--------------------|
367 * ^ | |
368 * | | Committed region |
369 * | | |
370 * | |------------------- | <-- guard_region.BaseAddress
371 * reserved | | | + guard_region.RegionSize
372 * size | | Guard region |
373 * | | |
374 * | |--------------------| <-- guard_region.BaseAddress,
375 * | | | uncommitted_region.BaseAddress
376 * | | Uncommitted region | + uncommitted_region.RegionSize
377 * v | |
378 * low_limit --> |------------------- | <-- uncommitted_region.BaseAddress
379 * : :
380 * : :
381 * v Lower addresses v
382 */
383
384 GetCurrentThreadStackLimits(&low_limit, &high_limit);
385
386 result_size = VirtualQuery((void*)low_limit,
387 &uncommitted_region, sizeof(uncommitted_region));
388 ZEND_ASSERT(result_size >= sizeof(uncommitted_region));
389
390 result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize,
391 &guard_region, sizeof(guard_region));
392 ZEND_ASSERT(result_size >= sizeof(uncommitted_region));
393
394 stack->base = (void*)high_limit;
395 stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit;
396
397 ZEND_ASSERT(stack->max_size > guard_region.RegionSize);
398 stack->max_size -= guard_region.RegionSize;
399
400 /* The uncommitted region does not shrink below 1 page */
401 page_size = zend_get_page_size();
402 ZEND_ASSERT(stack->max_size > page_size);
403 stack->max_size -= page_size;
404
405 return true;
406 }
407 #else /* ZEND_WIN32 */
zend_call_stack_get_win32(zend_call_stack * stack)408 static bool zend_call_stack_get_win32(zend_call_stack *stack)
409 {
410 return false;
411 }
412 #endif /* ZEND_WIN32 */
413
414 #if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
zend_call_stack_get_macos(zend_call_stack * stack)415 static bool zend_call_stack_get_macos(zend_call_stack *stack)
416 {
417 void *base = pthread_get_stackaddr_np(pthread_self());
418 size_t max_size;
419
420 if (pthread_main_np()) {
421 /* pthread_get_stacksize_np() returns a too low value for the main
422 * thread in OSX 10.9, 10.10:
423 * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html
424 * https://github.com/rust-lang/rust/issues/43347
425 */
426
427 /* Stack size is 8MiB by default for main threads
428 * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */
429 max_size = 8 * 1024 * 1024;
430 } else {
431 max_size = pthread_get_stacksize_np(pthread_self());
432 }
433
434 stack->base = base;
435 stack->max_size = max_size;
436
437 return true;
438 }
439 #else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */
zend_call_stack_get_macos(zend_call_stack * stack)440 static bool zend_call_stack_get_macos(zend_call_stack *stack)
441 {
442 return false;
443 }
444 #endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */
445
446 #if defined(__OpenBSD__)
447 #if defined(HAVE_PTHREAD_STACKSEG_NP)
zend_call_stack_get_openbsd_pthread(zend_call_stack * stack)448 static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
449 {
450 stack_t ss;
451
452 if (pthread_stackseg_np(pthread_self(), &ss) != 0) {
453 return false;
454 }
455
456 stack->base = (char *)ss.ss_sp - ss.ss_size;
457 stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE);
458
459 return true;
460 }
461 #else
zend_call_stack_get_openbsd_pthread(zend_call_stack * stack)462 static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
463 {
464 return false;
465 }
466 #endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */
467
zend_call_stack_get_openbsd_vm(zend_call_stack * stack)468 static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack)
469 {
470 struct _ps_strings ps;
471 struct rlimit rlim;
472 int mib[2] = {CTL_VM, VM_PSSTRINGS };
473 size_t len = sizeof(ps), pagesize;
474
475 if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) {
476 return false;
477 }
478
479 if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
480 return false;
481 }
482
483 if (rlim.rlim_cur == RLIM_INFINITY) {
484 return false;
485 }
486
487 pagesize = sysconf(_SC_PAGE_SIZE);
488
489 stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1));
490 stack->max_size = rlim.rlim_cur - pagesize;
491
492 return true;
493 }
494
zend_call_stack_get_openbsd(zend_call_stack * stack)495 static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
496 {
497 // TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np)
498 if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) {
499 return zend_call_stack_get_openbsd_vm(stack);
500 }
501
502 return zend_call_stack_get_openbsd_pthread(stack);
503 }
504
505 #else
zend_call_stack_get_openbsd(zend_call_stack * stack)506 static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
507 {
508 return false;
509 }
510 #endif /* defined(__OpenBSD__) */
511
512 /** Get the stack information for the calling thread */
zend_call_stack_get(zend_call_stack * stack)513 ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
514 {
515 if (zend_call_stack_get_linux(stack)) {
516 return true;
517 }
518
519 if (zend_call_stack_get_freebsd(stack)) {
520 return true;
521 }
522
523 if (zend_call_stack_get_win32(stack)) {
524 return true;
525 }
526
527 if (zend_call_stack_get_macos(stack)) {
528 return true;
529 }
530
531 if (zend_call_stack_get_openbsd(stack)) {
532 return true;
533 }
534
535 return false;
536 }
537
538 #endif /* ZEND_CHECK_STACK_LIMIT */
539