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