xref: /PHP-8.2/Zend/zend_call_stack.c (revision 968f048b)
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