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