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 defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun)
41 # include <pthread.h>
42 #endif
43 #if defined(__FreeBSD__) || defined(__DragonFly__)
44 # include <pthread_np.h>
45 # include <sys/mman.h>
46 # include <sys/sysctl.h>
47 # include <sys/user.h>
48 #endif
49 #ifdef __OpenBSD__
50 typedef int boolean_t;
51 # include <tib.h>
52 # include <pthread_np.h>
53 # include <sys/sysctl.h>
54 # include <sys/user.h>
55 #endif
56 #ifdef __NetBSD__
57 # include <sys/sysctl.h>
58 # include <sys/syscall.h>
59 #endif
60 #ifdef __HAIKU__
61 # include <kernel/OS.h>
62 #endif
63 #ifdef __linux__
64 #include <sys/syscall.h>
65 #endif
66 #ifdef __sun
67 # include <sys/lwp.h>
68 # ifdef HAVE_LIBPROC_H
69 # define _STRUCTURED_PROC 1
70 # include <sys/procfs.h>
71 # include <libproc.h>
72 # endif
73 #include <thread.h>
74 #endif
75
76 #ifdef HAVE_VALGRIND
77 # include <valgrind/valgrind.h>
78 #endif
79
80 #ifdef ZEND_CHECK_STACK_LIMIT
81
82 /* Called once per process or thread */
zend_call_stack_init(void)83 ZEND_API void zend_call_stack_init(void) {
84 if (!zend_call_stack_get(&EG(call_stack))) {
85 EG(call_stack) = (zend_call_stack){0};
86 }
87
88 switch (EG(max_allowed_stack_size)) {
89 case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: {
90 void *base = EG(call_stack).base;
91 size_t size = EG(call_stack).max_size;
92 if (UNEXPECTED(base == (void*)0)) {
93 base = zend_call_stack_position();
94 size = zend_call_stack_default_size();
95 /* base is not the actual stack base */
96 size -= 32 * 1024;
97 }
98 EG(stack_base) = base;
99 EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size));
100 break;
101 }
102 case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: {
103 EG(stack_base) = (void*)0;
104 EG(stack_limit) = (void*)0;
105 break;
106 }
107 default: {
108 ZEND_ASSERT(EG(max_allowed_stack_size) > 0);
109 void *base = EG(call_stack).base;
110 if (UNEXPECTED(base == (void*)0)) {
111 base = zend_call_stack_position();
112 }
113 EG(stack_base) = base;
114 EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size));
115 break;
116 }
117 }
118 }
119
120 #ifdef __linux__
zend_call_stack_is_main_thread(void)121 static bool zend_call_stack_is_main_thread(void) {
122 # ifdef HAVE_GETTID
123 return getpid() == gettid();
124 # else
125 return getpid() == syscall(SYS_gettid);
126 # endif
127 }
128
129 # if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
zend_call_stack_get_linux_pthread(zend_call_stack * stack)130 static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
131 {
132 pthread_attr_t attr;
133 int error;
134 void *addr;
135 size_t max_size;
136
137 /* pthread_getattr_np() will return bogus values for the main thread with
138 * musl or with some old glibc versions */
139 ZEND_ASSERT(!zend_call_stack_is_main_thread());
140
141 error = pthread_getattr_np(pthread_self(), &attr);
142 if (UNEXPECTED(error)) {
143 return false;
144 }
145
146 error = pthread_attr_getstack(&attr, &addr, &max_size);
147 if (UNEXPECTED(error)) {
148 pthread_attr_destroy(&attr);
149 return false;
150 }
151
152 # if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
153 {
154 size_t guard_size;
155 /* In glibc prior to 2.8, addr and size include the guard pages */
156 error = pthread_attr_getguardsize(&attr, &guard_size);
157 if (UNEXPECTED(error)) {
158 pthread_attr_destroy(&attr);
159 return false;
160 }
161
162 addr = (int8_t*)addr + guard_size;
163 max_size -= guard_size;
164 }
165 # endif /* glibc < 2.8 */
166
167 stack->base = (int8_t*)addr + max_size;
168 stack->max_size = max_size;
169
170 pthread_attr_destroy(&attr);
171
172 return true;
173 }
174 # else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
zend_call_stack_get_linux_pthread(zend_call_stack * stack)175 static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
176 {
177 return false;
178 }
179 # endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
180
zend_call_stack_get_linux_proc_maps(zend_call_stack * stack)181 static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack)
182 {
183 FILE *f;
184 char buffer[4096];
185 uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
186 uintptr_t start, end, prev_end = 0;
187 size_t max_size;
188 bool found = false;
189 struct rlimit rlim;
190 int error;
191
192 /* This method is relevant only for the main thread */
193 ZEND_ASSERT(zend_call_stack_is_main_thread());
194
195 /* Scan the process memory mappings to find the one containing the stack.
196 *
197 * The end of the stack mapping is the base of the stack. The start is
198 * adjusted by the kernel as the stack grows. The maximum stack size is
199 * determined by RLIMIT_STACK and the previous mapping.
200 *
201 *
202 * ^ Higher addresses ^
203 * : :
204 * : :
205 * Mapping end --> |-------------------| <-- Stack base (stack start)
206 * | | ^
207 * | Stack Mapping | | Stack size
208 * | | v
209 * Mapping start --> |-------------------| <-- Current stack end
210 * (adjusted : :
211 * downwards as the . .
212 * stack grows) : :
213 * |-------------------|
214 * | Some Mapping | The previous mapping may prevent
215 * |-------------------| stack growth
216 * : :
217 * : :
218 * v Lower addresses v
219 */
220
221 f = fopen("/proc/self/maps", "r");
222 if (!f) {
223 return false;
224 }
225
226 while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) {
227 if (start <= addr_on_stack && end >= addr_on_stack) {
228 found = true;
229 break;
230 }
231 prev_end = end;
232 }
233
234 fclose(f);
235
236 if (!found) {
237 return false;
238 }
239
240 error = getrlimit(RLIMIT_STACK, &rlim);
241 if (error || rlim.rlim_cur == RLIM_INFINITY) {
242 return false;
243 }
244
245 max_size = rlim.rlim_cur;
246
247 #ifdef HAVE_VALGRIND
248 /* Under Valgrind, the last page is not useable */
249 if (RUNNING_ON_VALGRIND) {
250 max_size -= zend_get_page_size();
251 }
252 #endif
253
254 /* Previous mapping may prevent the stack from growing */
255 if (end - max_size < prev_end) {
256 max_size = prev_end - end;
257 }
258
259 stack->base = (void*)end;
260 stack->max_size = max_size;
261
262 return true;
263 }
264
zend_call_stack_get_linux(zend_call_stack * stack)265 static bool zend_call_stack_get_linux(zend_call_stack *stack)
266 {
267 if (zend_call_stack_is_main_thread()) {
268 return zend_call_stack_get_linux_proc_maps(stack);
269 }
270
271 return zend_call_stack_get_linux_pthread(stack);
272 }
273 #else /* __linux__ */
zend_call_stack_get_linux(zend_call_stack * stack)274 static bool zend_call_stack_get_linux(zend_call_stack *stack)
275 {
276 return false;
277 }
278 #endif /* __linux__ */
279
280 #if defined(__FreeBSD__) || defined(__DragonFly__)
zend_call_stack_is_main_thread(void)281 static bool zend_call_stack_is_main_thread(void)
282 {
283 int is_main = pthread_main_np();
284 return is_main == -1 || is_main == 1;
285 }
286
287 # if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
zend_call_stack_get_freebsd_pthread(zend_call_stack * stack)288 static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
289 {
290 pthread_attr_t attr;
291 int error;
292 void *addr;
293 size_t max_size;
294
295 /* pthread will return bogus values for the main thread */
296 ZEND_ASSERT(!zend_call_stack_is_main_thread());
297
298 pthread_attr_init(&attr);
299
300 error = pthread_attr_get_np(pthread_self(), &attr);
301 if (error) {
302 goto fail;
303 }
304
305 error = pthread_attr_getstack(&attr, &addr, &max_size);
306 if (error) {
307 goto fail;
308 }
309
310 stack->base = (int8_t*)addr + max_size;
311 stack->max_size = max_size;
312
313 pthread_attr_destroy(&attr);
314 return true;
315
316 fail:
317 pthread_attr_destroy(&attr);
318 return false;
319 }
320 # else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
zend_call_stack_get_freebsd_pthread(zend_call_stack * stack)321 static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
322 {
323 return false;
324 }
325 # endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
326
zend_call_stack_get_freebsd_sysctl(zend_call_stack * stack)327 static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack)
328 {
329 void *stack_base;
330 int mib[2] = {CTL_KERN, KERN_USRSTACK};
331 size_t len = sizeof(stack_base);
332 struct rlimit rlim;
333 size_t numguards = 0;
334
335 /* This method is relevant only for the main thread */
336 ZEND_ASSERT(zend_call_stack_is_main_thread());
337
338 if (sysctl(mib, sizeof(mib)/sizeof(*mib), &stack_base, &len, NULL, 0) != 0) {
339 return false;
340 }
341
342 if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
343 return false;
344 }
345
346 if (rlim.rlim_cur == RLIM_INFINITY) {
347 return false;
348 }
349
350 len = sizeof(numguards);
351 /* For most of the cases, we do not necessarily need to do so as, by default, it is `1` page, but is user writable */
352 if (sysctlbyname("security.bsd.stack_guard_page", &numguards, &len, NULL, 0) != 0) {
353 return false;
354 }
355
356 size_t guard_size = numguards * getpagesize();
357
358 stack->base = stack_base;
359 stack->max_size = rlim.rlim_cur - guard_size;
360
361 return true;
362 }
363
zend_call_stack_get_freebsd(zend_call_stack * stack)364 static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
365 {
366 if (zend_call_stack_is_main_thread()) {
367 return zend_call_stack_get_freebsd_sysctl(stack);
368 }
369
370 return zend_call_stack_get_freebsd_pthread(stack);
371 }
372 #else
zend_call_stack_get_freebsd(zend_call_stack * stack)373 static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
374 {
375 return false;
376 }
377 #endif /* __FreeBSD__ */
378
379 #ifdef ZEND_WIN32
zend_call_stack_get_win32(zend_call_stack * stack)380 static bool zend_call_stack_get_win32(zend_call_stack *stack)
381 {
382 ULONG_PTR low_limit, high_limit;
383 ULONG size;
384 MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0};
385 size_t result_size, page_size;
386
387 /* The stack consists of three regions: committed, guard, and uncommitted.
388 * Memory is committed when the guard region is accessed. If only one page
389 * is left in the uncommitted region, a stack overflow error is raised
390 * instead.
391 *
392 * The total useable stack size is the size of the committed and uncommitted
393 * regions less one page.
394 *
395 * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx
396 * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size
397 *
398 * ^ Higher addresses ^
399 * : :
400 * : :
401 * high_limit --> |--------------------|
402 * ^ | |
403 * | | Committed region |
404 * | | |
405 * | |------------------- | <-- guard_region.BaseAddress
406 * reserved | | | + guard_region.RegionSize
407 * size | | Guard region |
408 * | | |
409 * | |--------------------| <-- guard_region.BaseAddress,
410 * | | | uncommitted_region.BaseAddress
411 * | | Uncommitted region | + uncommitted_region.RegionSize
412 * v | |
413 * low_limit --> |------------------- | <-- uncommitted_region.BaseAddress
414 * : :
415 * : :
416 * v Lower addresses v
417 */
418
419 GetCurrentThreadStackLimits(&low_limit, &high_limit);
420
421 result_size = VirtualQuery((void*)low_limit,
422 &uncommitted_region, sizeof(uncommitted_region));
423 ZEND_ASSERT(result_size >= sizeof(uncommitted_region));
424
425 result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize,
426 &guard_region, sizeof(guard_region));
427 ZEND_ASSERT(result_size >= sizeof(uncommitted_region));
428
429 stack->base = (void*)high_limit;
430 stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit;
431
432 ZEND_ASSERT(stack->max_size > guard_region.RegionSize);
433 stack->max_size -= guard_region.RegionSize;
434
435 /* The uncommitted region does not shrink below 1 page */
436 page_size = zend_get_page_size();
437 ZEND_ASSERT(stack->max_size > page_size);
438 stack->max_size -= page_size;
439
440 return true;
441 }
442 #else /* ZEND_WIN32 */
zend_call_stack_get_win32(zend_call_stack * stack)443 static bool zend_call_stack_get_win32(zend_call_stack *stack)
444 {
445 return false;
446 }
447 #endif /* ZEND_WIN32 */
448
449 #if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
zend_call_stack_get_macos(zend_call_stack * stack)450 static bool zend_call_stack_get_macos(zend_call_stack *stack)
451 {
452 void *base = pthread_get_stackaddr_np(pthread_self());
453 size_t max_size;
454
455 #if !defined(__aarch64__)
456 if (pthread_main_np())
457 {
458 /* pthread_get_stacksize_np() returns a too low value for the main
459 * thread in OSX 10.9, 10.10:
460 * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html
461 * https://github.com/rust-lang/rust/issues/43347
462 */
463
464 /* Stack size is 8MiB by default for main threads
465 * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */
466 max_size = 8 * 1024 * 1024;
467 }
468 else
469 #endif
470 {
471 max_size = pthread_get_stacksize_np(pthread_self());
472 }
473
474 stack->base = base;
475 stack->max_size = max_size;
476
477 return true;
478 }
479 #else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */
zend_call_stack_get_macos(zend_call_stack * stack)480 static bool zend_call_stack_get_macos(zend_call_stack *stack)
481 {
482 return false;
483 }
484 #endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */
485
486 #if defined(__OpenBSD__)
487 #if defined(HAVE_PTHREAD_STACKSEG_NP)
zend_call_stack_get_openbsd_pthread(zend_call_stack * stack)488 static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
489 {
490 stack_t ss;
491
492 if (pthread_stackseg_np(pthread_self(), &ss) != 0) {
493 return false;
494 }
495
496 stack->base = (char *)ss.ss_sp - ss.ss_size;
497 stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE);
498
499 return true;
500 }
501 #else
zend_call_stack_get_openbsd_pthread(zend_call_stack * stack)502 static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
503 {
504 return false;
505 }
506 #endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */
507
zend_call_stack_get_openbsd_vm(zend_call_stack * stack)508 static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack)
509 {
510 struct _ps_strings ps;
511 struct rlimit rlim;
512 int mib[2] = {CTL_VM, VM_PSSTRINGS };
513 size_t len = sizeof(ps), pagesize;
514
515 if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) {
516 return false;
517 }
518
519 if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
520 return false;
521 }
522
523 if (rlim.rlim_cur == RLIM_INFINITY) {
524 return false;
525 }
526
527 pagesize = sysconf(_SC_PAGE_SIZE);
528
529 stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1));
530 stack->max_size = rlim.rlim_cur - pagesize;
531
532 return true;
533 }
534
zend_call_stack_get_openbsd(zend_call_stack * stack)535 static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
536 {
537 // TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np)
538 if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) {
539 return zend_call_stack_get_openbsd_vm(stack);
540 }
541
542 return zend_call_stack_get_openbsd_pthread(stack);
543 }
544
545 #else
zend_call_stack_get_openbsd(zend_call_stack * stack)546 static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
547 {
548 return false;
549 }
550 #endif /* defined(__OpenBSD__) */
551 #if defined(__HAIKU__)
zend_call_stack_get_haiku(zend_call_stack * stack)552 static bool zend_call_stack_get_haiku(zend_call_stack *stack)
553 {
554 thread_id id;
555 thread_info ti;
556 size_t guard_size;
557
558 // unlikely, main thread ought to be always available but we never know
559 if ((id = find_thread(NULL)) == B_NAME_NOT_FOUND || get_thread_info(id, &ti) != B_OK) {
560 return false;
561 }
562
563 // USER_STACK_GUARD_SIZE
564 guard_size = sysconf(_SC_PAGESIZE) * 4;
565
566 stack->base = ti.stack_end;
567 stack->max_size = ((size_t)ti.stack_end - (size_t)ti.stack_base) - guard_size;
568
569 return true;
570 }
571 #else
zend_call_stack_get_haiku(zend_call_stack * stack)572 static bool zend_call_stack_get_haiku(zend_call_stack *stack)
573 {
574 return false;
575 }
576 #endif /* defined(__HAIKU__) */
577
578 #if defined(__NetBSD__)
579 # ifdef HAVE_PTHREAD_GETATTR_NP
zend_call_stack_get_netbsd_pthread(zend_call_stack * stack)580 static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack)
581 {
582 pthread_attr_t attr;
583 int error;
584 void *addr;
585 size_t max_size, guard_size;
586
587 error = pthread_getattr_np(pthread_self(), &attr);
588 if (error) {
589 return false;
590 }
591
592 error = pthread_attr_getstack(&attr, &addr, &max_size);
593 if (error) {
594 return false;
595 }
596
597 error = pthread_attr_getguardsize(&attr, &guard_size);
598 if (error) {
599 return false;
600 }
601
602 addr = (char *)addr + guard_size;
603 max_size -= guard_size;
604
605 stack->base = (char *)addr + max_size;
606 stack->max_size = max_size;
607
608 return true;
609 }
610 # else
zend_call_stack_get_netbsd_pthread(zend_call_stack * stack)611 static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack)
612 {
613 return false;
614 }
615 # endif /* HAVE_PTHREAD_GETATTR_NP */
zend_call_stack_get_netbsd_vm(zend_call_stack * stack,void ** ptr)616 static bool zend_call_stack_get_netbsd_vm(zend_call_stack *stack, void **ptr)
617 {
618 /**
619 * NetBSD supports procfs in a similar fashion as Linux
620 * however NetBSD's mid/long term plan is to remove it completely.
621 */
622 char *start, *end;
623 struct kinfo_vmentry *entry;
624 size_t len, max_size;
625 uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
626 int mib[5] = { CTL_VM, VM_PROC, VM_PROC_MAP, getpid(), sizeof(struct kinfo_vmentry) };
627 bool found = false;
628 struct rlimit rlim;
629
630 if (sysctl(mib, 5, NULL, &len, NULL, 0) != 0) {
631 return false;
632 }
633
634 // kinfo_getvmmap uses the same formula, only we do not want to rely on libkvm
635 len = len * 4 / 3 ;
636 *ptr = malloc(len);
637
638 if (sysctl(mib, 5, *ptr, &len, NULL, 0) != 0) {
639 return false;
640 }
641
642 start = (char *)*ptr;
643 end = start + len;
644
645 while (start < end) {
646 entry = (struct kinfo_vmentry *)start;
647 if (entry->kve_start <= addr_on_stack && entry->kve_end >= addr_on_stack) {
648 found = true;
649 break;
650 }
651
652 start += sizeof(struct kinfo_vmentry);
653 }
654
655 if (!found) {
656 return false;
657 }
658
659 if (getrlimit(RLIMIT_STACK, &rlim) || rlim.rlim_cur == RLIM_INFINITY) {
660 return false;
661 }
662
663 max_size = rlim.rlim_cur;
664
665 stack->base = (void *)entry->kve_end;
666 stack->max_size = max_size;
667
668 return true;
669 }
670
671
zend_call_stack_get_netbsd(zend_call_stack * stack)672 static bool zend_call_stack_get_netbsd(zend_call_stack *stack)
673 {
674 if (syscall(SYS__lwp_self) == 1) {
675 void *ptr = NULL;
676 bool r = zend_call_stack_get_netbsd_vm(stack, &ptr);
677 free(ptr);
678 return r;
679 }
680
681 return zend_call_stack_get_netbsd_pthread(stack);
682 }
683 #else
zend_call_stack_get_netbsd(zend_call_stack * stack)684 static bool zend_call_stack_get_netbsd(zend_call_stack *stack)
685 {
686 return false;
687 }
688 #endif /* defined(__NetBSD__) */
689
690 #if defined(__sun)
zend_call_stack_get_solaris_pthread(zend_call_stack * stack)691 static bool zend_call_stack_get_solaris_pthread(zend_call_stack *stack)
692 {
693 stack_t s;
694 if (thr_stksegment(&s) < 0) {
695 return false;
696 }
697
698 stack->max_size = s.ss_size;
699 stack->base = s.ss_sp;
700 return true;
701 }
702
703 #ifdef HAVE_LIBPROC_H
zend_call_stack_get_solaris_proc_maps(zend_call_stack * stack)704 static bool zend_call_stack_get_solaris_proc_maps(zend_call_stack *stack)
705 {
706 uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
707 bool found = false, r = false;
708 struct ps_prochandle *proc;
709 prmap_t *map, *orig;
710 struct rlimit rlim;
711 char path[PATH_MAX];
712 size_t size;
713 ssize_t len = -1;
714 pid_t pid;
715 int error, fd;
716
717 pid = getpid();
718 proc = Pgrab(pid, PGRAB_RDONLY, &error);
719 if (!proc) {
720 return false;
721 }
722
723 size = (1 << 20);
724 snprintf(path, sizeof(path), "/proc/%d/map", (int)pid);
725
726 if ((fd = open(path, O_RDONLY)) == -1) {
727 Prelease(proc, 0);
728 return false;
729 }
730
731 orig = malloc(size);
732 if (!orig) {
733 Prelease(proc, 0);
734 close(fd);
735 return false;
736 }
737
738 while (size > 0 && (len = pread(fd, orig, size, 0)) == size) {
739 prmap_t *tmp;
740 size <<= 1;
741 tmp = realloc(orig, size);
742 if (!tmp) {
743 goto end;
744 }
745 orig = tmp;
746 }
747
748 for (map = orig; len > 0; ++map) {
749 if ((uintptr_t)map->pr_vaddr <= addr_on_stack && (uintptr_t)map->pr_vaddr + map->pr_size >= addr_on_stack) {
750 found = true;
751 break;
752 }
753 len -= sizeof(*map);
754 }
755
756 if (!found) {
757 goto end;
758 }
759
760 error = getrlimit(RLIMIT_STACK, &rlim);
761 if (error || rlim.rlim_cur == RLIM_INFINITY) {
762 goto end;
763 }
764
765 stack->base = (void *)map->pr_vaddr + map->pr_size;
766 stack->max_size = rlim.rlim_cur;
767 r = true;
768
769 end:
770 free(orig);
771 Prelease(proc, 0);
772 close(fd);
773 return r;
774 }
775 #endif
776
zend_call_stack_get_solaris(zend_call_stack * stack)777 static bool zend_call_stack_get_solaris(zend_call_stack *stack)
778 {
779 #ifdef HAVE_LIBPROC_H
780 if (_lwp_self() == 1) {
781 return zend_call_stack_get_solaris_proc_maps(stack);
782 }
783 #endif
784 return zend_call_stack_get_solaris_pthread(stack);
785 }
786 #else
zend_call_stack_get_solaris(zend_call_stack * stack)787 static bool zend_call_stack_get_solaris(zend_call_stack *stack)
788 {
789 return false;
790 }
791 #endif /* defined(__sun) */
792
793 /** Get the stack information for the calling thread */
zend_call_stack_get(zend_call_stack * stack)794 ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
795 {
796 if (zend_call_stack_get_linux(stack)) {
797 return true;
798 }
799
800 if (zend_call_stack_get_freebsd(stack)) {
801 return true;
802 }
803
804 if (zend_call_stack_get_win32(stack)) {
805 return true;
806 }
807
808 if (zend_call_stack_get_macos(stack)) {
809 return true;
810 }
811
812 if (zend_call_stack_get_openbsd(stack)) {
813 return true;
814 }
815
816 if (zend_call_stack_get_netbsd(stack)) {
817 return true;
818 }
819
820 if (zend_call_stack_get_haiku(stack)) {
821 return true;
822 }
823
824 if (zend_call_stack_get_solaris(stack)) {
825 return true;
826 }
827
828 return false;
829 }
830
831 #endif /* ZEND_CHECK_STACK_LIMIT */
832