xref: /PHP-8.4/Zend/zend_call_stack.c (revision 062e9f91)
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