27# include <processthreadsapi.h>
28# include <memoryapi.h>
30# include <sys/resource.h>
34# ifdef HAVE_SYS_TYPES_H
35# include <sys/types.h>
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)
43#if defined(__FreeBSD__) || defined(__DragonFly__)
44# include <pthread_np.h>
46# include <sys/sysctl.h>
52# include <pthread_np.h>
53# include <sys/sysctl.h>
57# include <sys/sysctl.h>
58# include <sys/syscall.h>
61# include <kernel/OS.h>
64#include <sys/syscall.h>
69# define _STRUCTURED_PROC 1
70# include <sys/procfs.h>
77# include <valgrind/valgrind.h>
80#ifdef ZEND_CHECK_STACK_LIMIT
83ZEND_API void zend_call_stack_init(
void) {
84 if (!zend_call_stack_get(&
EG(call_stack))) {
85 EG(call_stack) = (zend_call_stack){0};
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;
93 base = zend_call_stack_position();
94 size = zend_call_stack_default_size();
98 EG(stack_base) = base;
99 EG(stack_limit) = zend_call_stack_limit(base,
size,
EG(reserved_stack_size));
102 case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: {
103 EG(stack_base) = (
void*)0;
104 EG(stack_limit) = (
void*)0;
109 void *base =
EG(call_stack).base;
111 base = zend_call_stack_position();
113 EG(stack_base) = base;
114 EG(stack_limit) = zend_call_stack_limit(base,
EG(max_allowed_stack_size),
EG(reserved_stack_size));
121static bool zend_call_stack_is_main_thread(
void) {
123 return getpid() == gettid();
125 return getpid() == syscall(SYS_gettid);
129# if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
130static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
141 error = pthread_getattr_np(pthread_self(), &
attr);
148 pthread_attr_destroy(&
attr);
152# if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
156 error = pthread_attr_getguardsize(&
attr, &guard_size);
158 pthread_attr_destroy(&
attr);
163 max_size -= guard_size;
167 stack->base = (int8_t*)
addr + max_size;
168 stack->max_size = max_size;
170 pthread_attr_destroy(&
attr);
175static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
181static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack)
185 uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
221 f =
fopen(
"/proc/self/maps",
"r");
240 error = getrlimit(RLIMIT_STACK, &rlim);
241 if (
error || rlim.rlim_cur == RLIM_INFINITY) {
245 max_size = rlim.rlim_cur;
249 if (RUNNING_ON_VALGRIND) {
255 if (
end - max_size < prev_end) {
256 max_size = prev_end -
end;
259 stack->base = (
void*)
end;
260 stack->max_size = max_size;
265static bool zend_call_stack_get_linux(zend_call_stack *stack)
267 if (zend_call_stack_is_main_thread()) {
268 return zend_call_stack_get_linux_proc_maps(stack);
271 return zend_call_stack_get_linux_pthread(stack);
274static bool zend_call_stack_get_linux(zend_call_stack *stack)
280#if defined(__FreeBSD__) || defined(__DragonFly__)
281static bool zend_call_stack_is_main_thread(
void)
283 int is_main = pthread_main_np();
284 return is_main == -1 || is_main == 1;
287# if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
288static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
298 pthread_attr_init(&
attr);
300 error = pthread_attr_get_np(pthread_self(), &
attr);
310 stack->base = (int8_t*)
addr + max_size;
311 stack->max_size = max_size;
313 pthread_attr_destroy(&
attr);
317 pthread_attr_destroy(&
attr);
321static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
327static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack)
330 int mib[2] = {CTL_KERN, KERN_USRSTACK};
331 size_t len =
sizeof(stack_base);
333 size_t numguards = 0;
338 if (sysctl(mib,
sizeof(mib)/
sizeof(*mib), &stack_base, &
len,
NULL, 0) != 0) {
342 if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
346 if (rlim.rlim_cur == RLIM_INFINITY) {
350 len =
sizeof(numguards);
352 if (sysctlbyname(
"security.bsd.stack_guard_page", &numguards, &
len,
NULL, 0) != 0) {
356 size_t guard_size = numguards * getpagesize();
358 stack->base = stack_base;
359 stack->max_size = rlim.rlim_cur - guard_size;
364static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
366 if (zend_call_stack_is_main_thread()) {
367 return zend_call_stack_get_freebsd_sysctl(stack);
370 return zend_call_stack_get_freebsd_pthread(stack);
373static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
380static bool zend_call_stack_get_win32(zend_call_stack *stack)
382 ULONG_PTR low_limit, high_limit;
384 MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0};
385 size_t result_size, page_size;
419 GetCurrentThreadStackLimits(&low_limit, &high_limit);
421 result_size = VirtualQuery((
void*)low_limit,
422 &uncommitted_region,
sizeof(uncommitted_region));
423 ZEND_ASSERT(result_size >=
sizeof(uncommitted_region));
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));
429 stack->base = (
void*)high_limit;
430 stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit;
432 ZEND_ASSERT(stack->max_size > guard_region.RegionSize);
433 stack->max_size -= guard_region.RegionSize;
438 stack->max_size -= page_size;
443static bool zend_call_stack_get_win32(zend_call_stack *stack)
449#if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
450static bool zend_call_stack_get_macos(zend_call_stack *stack)
452 void *base = pthread_get_stackaddr_np(pthread_self());
455#if !defined(__aarch64__)
456 if (pthread_main_np())
466 max_size = 8 * 1024 * 1024;
471 max_size = pthread_get_stacksize_np(pthread_self());
475 stack->max_size = max_size;
480static bool zend_call_stack_get_macos(zend_call_stack *stack)
486#if defined(__OpenBSD__)
487#if defined(HAVE_PTHREAD_STACKSEG_NP)
488static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
492 if (pthread_stackseg_np(pthread_self(), &ss) != 0) {
496 stack->base = (
char *)ss.ss_sp - ss.ss_size;
497 stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE);
502static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
508static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack)
510 struct _ps_strings ps;
512 int mib[2] = {CTL_VM, VM_PSSTRINGS };
513 size_t len =
sizeof(ps), pagesize;
515 if (sysctl(mib, 2, &ps, &
len,
NULL, 0) != 0) {
519 if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
523 if (rlim.rlim_cur == RLIM_INFINITY) {
527 pagesize = sysconf(_SC_PAGE_SIZE);
529 stack->base = (
void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1));
530 stack->max_size = rlim.rlim_cur - pagesize;
535static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
538 if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) {
539 return zend_call_stack_get_openbsd_vm(stack);
542 return zend_call_stack_get_openbsd_pthread(stack);
546static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
551#if defined(__HAIKU__)
552static bool zend_call_stack_get_haiku(zend_call_stack *stack)
559 if ((
id = find_thread(
NULL)) == B_NAME_NOT_FOUND || get_thread_info(
id, &ti) != B_OK) {
564 guard_size = sysconf(_SC_PAGESIZE) * 4;
566 stack->base = ti.stack_end;
567 stack->max_size = ((size_t)ti.stack_end - (size_t)ti.stack_base) - guard_size;
572static bool zend_call_stack_get_haiku(zend_call_stack *stack)
578#if defined(__NetBSD__)
579# ifdef HAVE_PTHREAD_GETATTR_NP
580static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack)
585 size_t max_size, guard_size;
587 error = pthread_getattr_np(pthread_self(), &
attr);
597 error = pthread_attr_getguardsize(&
attr, &guard_size);
603 max_size -= guard_size;
605 stack->
base = (
char *)
addr + max_size;
606 stack->max_size = max_size;
611static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack)
616static bool zend_call_stack_get_netbsd_vm(zend_call_stack *stack,
void **
ptr)
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) };
638 if (sysctl(mib, 5, *
ptr, &
len,
NULL, 0) != 0) {
646 entry = (
struct kinfo_vmentry *)
start;
647 if (entry->kve_start <= addr_on_stack && entry->kve_end >= addr_on_stack) {
652 start +=
sizeof(
struct kinfo_vmentry);
659 if (getrlimit(RLIMIT_STACK, &rlim) || rlim.rlim_cur == RLIM_INFINITY) {
663 max_size = rlim.rlim_cur;
665 stack->base = (
void *)entry->kve_end;
666 stack->max_size = max_size;
672static bool zend_call_stack_get_netbsd(zend_call_stack *stack)
674 if (syscall(SYS__lwp_self) == 1) {
676 bool r = zend_call_stack_get_netbsd_vm(stack, &
ptr);
681 return zend_call_stack_get_netbsd_pthread(stack);
684static bool zend_call_stack_get_netbsd(zend_call_stack *stack)
691static bool zend_call_stack_get_solaris_pthread(zend_call_stack *stack)
694 if (thr_stksegment(&
s) < 0) {
698 stack->max_size =
s.ss_size;
699 stack->base =
s.ss_sp;
704static bool zend_call_stack_get_solaris_proc_maps(zend_call_stack *stack)
706 uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
707 bool found =
false, r =
false;
708 struct ps_prochandle *proc;
718 proc = Pgrab(pid, PGRAB_RDONLY, &
error);
724 snprintf(path,
sizeof(path),
"/proc/%d/map", (
int)pid);
726 if ((
fd = open(path, O_RDONLY)) == -1) {
741 tmp = realloc(orig,
size);
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) {
760 error = getrlimit(RLIMIT_STACK, &rlim);
761 if (
error || rlim.rlim_cur == RLIM_INFINITY) {
765 stack->base = (
void *)map->pr_vaddr + map->pr_size;
766 stack->max_size = rlim.rlim_cur;
777static bool zend_call_stack_get_solaris(zend_call_stack *stack)
780 if (_lwp_self() == 1) {
781 return zend_call_stack_get_solaris_proc_maps(stack);
784 return zend_call_stack_get_solaris_pthread(stack);
787static bool zend_call_stack_get_solaris(zend_call_stack *stack)
794ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
796 if (zend_call_stack_get_linux(stack)) {
800 if (zend_call_stack_get_freebsd(stack)) {
804 if (zend_call_stack_get_win32(stack)) {
808 if (zend_call_stack_get_macos(stack)) {
812 if (zend_call_stack_get_openbsd(stack)) {
816 if (zend_call_stack_get_netbsd(stack)) {
820 if (zend_call_stack_get_haiku(stack)) {
824 if (zend_call_stack_get_solaris(stack)) {
sscanf(string $string, string $format, mixed &... $vars)
fopen(string $filename, string $mode, bool $use_include_path=false, $context=null)
fgets($stream, ?int $length=null)
unsigned const char * end
ZEND_API size_t zend_get_page_size(void)
#define UNEXPECTED(condition)