xref: /php-src/Zend/zend_call_stack.c (revision 5a043c26)
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