xref: /PHP-8.3/Zend/zend_call_stack.c (revision 159f14c4)
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 # include <pthread.h>
41 #endif
42 #ifdef __FreeBSD__
43 # include <pthread_np.h>
44 # include <sys/mman.h>
45 # include <sys/sysctl.h>
46 # include <sys/user.h>
47 #endif
48 #ifdef __OpenBSD__
49 typedef int boolean_t;
50 # include <tib.h>
51 # include <pthread_np.h>
52 # include <sys/sysctl.h>
53 # include <sys/user.h>
54 #endif
55 #ifdef __linux__
56 #include <sys/syscall.h>
57 #endif
58 
59 #ifdef ZEND_CHECK_STACK_LIMIT
60 
61 /* Called once per process or thread */
zend_call_stack_init(void)62 ZEND_API void zend_call_stack_init(void) {
63 	if (!zend_call_stack_get(&EG(call_stack))) {
64 		EG(call_stack) = (zend_call_stack){0};
65 	}
66 
67 	switch (EG(max_allowed_stack_size)) {
68 		case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: {
69 			void *base = EG(call_stack).base;
70 			size_t size = EG(call_stack).max_size;
71 			if (UNEXPECTED(base == (void*)0)) {
72 				base = zend_call_stack_position();
73 				size = zend_call_stack_default_size();
74 				/* base is not the actual stack base */
75 				size -= 32 * 1024;
76 			}
77 			EG(stack_base) = base;
78 			EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size));
79 			break;
80 		}
81 		case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: {
82 			EG(stack_base) = (void*)0;
83 			EG(stack_limit) = (void*)0;
84 			break;
85 		}
86 		default: {
87 			ZEND_ASSERT(EG(max_allowed_stack_size) > 0);
88 			void *base = EG(call_stack).base;
89 			if (UNEXPECTED(base == (void*)0)) {
90 				base = zend_call_stack_position();
91 			}
92 			EG(stack_base) = base;
93 			EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size));
94 			break;
95 		}
96 	}
97 }
98 
99 #ifdef __linux__
zend_call_stack_is_main_thread(void)100 static bool zend_call_stack_is_main_thread(void) {
101 # ifdef HAVE_GETTID
102 	return getpid() == gettid();
103 # else
104 	return getpid() == syscall(SYS_gettid);
105 # endif
106 }
107 
108 # if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
zend_call_stack_get_linux_pthread(zend_call_stack * stack)109 static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
110 {
111 	pthread_attr_t attr;
112 	int error;
113 	void *addr;
114 	size_t max_size;
115 
116 	/* pthread_getattr_np() will return bogus values for the main thread with
117 	 * musl or with some old glibc versions */
118 	ZEND_ASSERT(!zend_call_stack_is_main_thread());
119 
120 	error = pthread_getattr_np(pthread_self(), &attr);
121 	if (error) {
122 		return false;
123 	}
124 
125 	error = pthread_attr_getstack(&attr, &addr, &max_size);
126 	if (error) {
127 		pthread_attr_destroy(&attr);
128 		return false;
129 	}
130 
131 #  if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
132 	{
133 		size_t guard_size;
134 		/* In glibc prior to 2.8, addr and size include the guard pages */
135 		error = pthread_attr_getguardsize(&attr, &guard_size);
136 		if (error) {
137 			pthread_attr_destroy(&attr);
138 			return false;
139 		}
140 
141 		addr = (int8_t*)addr + guard_size;
142 		max_size -= guard_size;
143 	}
144 #  endif /* glibc < 2.8 */
145 
146 	stack->base = (int8_t*)addr + max_size;
147 	stack->max_size = max_size;
148 
149 	pthread_attr_destroy(&attr);
150 
151 	return true;
152 }
153 # else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
zend_call_stack_get_linux_pthread(zend_call_stack * stack)154 static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack)
155 {
156 	return false;
157 }
158 # endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
159 
zend_call_stack_get_linux_proc_maps(zend_call_stack * stack)160 static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack)
161 {
162 	FILE *f;
163 	char buffer[4096];
164 	uintptr_t addr_on_stack = (uintptr_t)&buffer;
165 	uintptr_t start, end, prev_end = 0;
166 	size_t max_size;
167 	bool found = false;
168 	struct rlimit rlim;
169 	int error;
170 
171 	/* This method is relevant only for the main thread */
172 	ZEND_ASSERT(zend_call_stack_is_main_thread());
173 
174 	/* Scan the process memory mappings to find the one containing the stack.
175 	 *
176 	 * The end of the stack mapping is the base of the stack. The start is
177 	 * adjusted by the kernel as the stack grows. The maximum stack size is
178 	 * determined by RLIMIT_STACK and the previous mapping.
179 	 *
180 	 *
181 	 *                   ^ Higher addresses  ^
182 	 *                   :                   :
183 	 *                   :                   :
184 	 *   Mapping end --> |-------------------| <-- Stack base (stack start)
185 	 *                   |                   |   ^
186 	 *                   | Stack Mapping     |   | Stack size
187 	 *                   |                   |   v
188 	 * Mapping start --> |-------------------| <-- Current stack end
189 	 * (adjusted         :                   :
190 	 *  downwards as the .                   .
191 	 *  stack grows)     :                   :
192 	 *                   |-------------------|
193 	 *                   | Some Mapping      | The previous mapping may prevent
194 	 *                   |-------------------| stack growth
195 	 *                   :                   :
196 	 *                   :                   :
197 	 *                   v Lower addresses   v
198 	 */
199 
200 	f = fopen("/proc/self/maps", "r");
201 	if (!f) {
202 		return false;
203 	}
204 
205 	while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) {
206 		if (start <= addr_on_stack && end >= addr_on_stack) {
207 			found = true;
208 			break;
209 		}
210 		prev_end = end;
211 	}
212 
213 	fclose(f);
214 
215 	if (!found) {
216 		return false;
217 	}
218 
219 	error = getrlimit(RLIMIT_STACK, &rlim);
220 	if (error || rlim.rlim_cur == RLIM_INFINITY) {
221 		return false;
222 	}
223 
224 	max_size = rlim.rlim_cur;
225 
226 	/* Previous mapping may prevent the stack from growing */
227 	if (end - max_size < prev_end) {
228 		max_size = prev_end - end;
229 	}
230 
231 	stack->base = (void*)end;
232 	stack->max_size = max_size;
233 
234 	return true;
235 }
236 
zend_call_stack_get_linux(zend_call_stack * stack)237 static bool zend_call_stack_get_linux(zend_call_stack *stack)
238 {
239 	if (zend_call_stack_is_main_thread()) {
240 		return zend_call_stack_get_linux_proc_maps(stack);
241 	}
242 
243 	return zend_call_stack_get_linux_pthread(stack);
244 }
245 #else /* __linux__ */
zend_call_stack_get_linux(zend_call_stack * stack)246 static bool zend_call_stack_get_linux(zend_call_stack *stack)
247 {
248 	return false;
249 }
250 #endif /* __linux__ */
251 
252 #ifdef __FreeBSD__
zend_call_stack_is_main_thread(void)253 static bool zend_call_stack_is_main_thread(void)
254 {
255 	int is_main = pthread_main_np();
256 	return is_main == -1 || is_main == 1;
257 }
258 
259 # if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
zend_call_stack_get_freebsd_pthread(zend_call_stack * stack)260 static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
261 {
262 	pthread_attr_t attr;
263 	int error;
264 	void *addr;
265 	size_t max_size;
266 
267 	/* pthread will return bogus values for the main thread */
268 	ZEND_ASSERT(!zend_call_stack_is_main_thread());
269 
270 	pthread_attr_init(&attr);
271 
272 	error = pthread_attr_get_np(pthread_self(), &attr);
273 	if (error) {
274 		goto fail;
275 	}
276 
277 	error = pthread_attr_getstack(&attr, &addr, &max_size);
278 	if (error) {
279 		goto fail;
280 	}
281 
282 	stack->base = (int8_t*)addr + max_size;
283 	stack->max_size = max_size;
284 
285 	pthread_attr_destroy(&attr);
286 	return true;
287 
288 fail:
289 	pthread_attr_destroy(&attr);
290 	return false;
291 }
292 # else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
zend_call_stack_get_freebsd_pthread(zend_call_stack * stack)293 static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack)
294 {
295 	return false;
296 }
297 # endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */
298 
zend_call_stack_get_freebsd_sysctl(zend_call_stack * stack)299 static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack)
300 {
301 	void *stack_base;
302 	int mib[2] = {CTL_KERN, KERN_USRSTACK};
303 	size_t len = sizeof(stack_base);
304 	struct rlimit rlim;
305 
306 	/* This method is relevant only for the main thread */
307 	ZEND_ASSERT(zend_call_stack_is_main_thread());
308 
309 	if (sysctl(mib, sizeof(mib)/sizeof(*mib), &stack_base, &len, NULL, 0) != 0) {
310 		return false;
311 	}
312 
313 	if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
314 		return false;
315 	}
316 
317 	if (rlim.rlim_cur == RLIM_INFINITY) {
318 		return false;
319 	}
320 
321 	size_t guard_size = getpagesize();
322 
323 	stack->base = stack_base;
324 	stack->max_size = rlim.rlim_cur - guard_size;
325 
326 	return true;
327 }
328 
zend_call_stack_get_freebsd(zend_call_stack * stack)329 static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
330 {
331 	if (zend_call_stack_is_main_thread()) {
332 		return zend_call_stack_get_freebsd_sysctl(stack);
333 	}
334 
335 	return zend_call_stack_get_freebsd_pthread(stack);
336 }
337 #else
zend_call_stack_get_freebsd(zend_call_stack * stack)338 static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
339 {
340 	return false;
341 }
342 #endif /* __FreeBSD__ */
343 
344 #ifdef ZEND_WIN32
zend_call_stack_get_win32(zend_call_stack * stack)345 static bool zend_call_stack_get_win32(zend_call_stack *stack)
346 {
347 	ULONG_PTR low_limit, high_limit;
348 	ULONG size;
349 	MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0};
350 	size_t result_size, page_size;
351 
352 	/* The stack consists of three regions: committed, guard, and uncommitted.
353 	 * Memory is committed when the guard region is accessed. If only one page
354 	 * is left in the uncommitted region, a stack overflow error is raised
355 	 * instead.
356 	 *
357 	 * The total useable stack size is the size of the committed and uncommitted
358 	 * regions less one page.
359 	 *
360 	 * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx
361 	 * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size
362 	 *
363 	 *                ^  Higher addresses  ^
364 	 *                :                    :
365 	 *                :                    :
366 	 * high_limit --> |--------------------|
367 	 *            ^   |                    |
368 	 *            |   | Committed region   |
369 	 *            |   |                    |
370 	 *            |   |------------------- | <-- guard_region.BaseAddress
371 	 *   reserved |   |                    |     + guard_region.RegionSize
372 	 *       size |   | Guard region       |
373 	 *            |   |                    |
374 	 *            |   |--------------------| <-- guard_region.BaseAddress,
375 	 *            |   |                    |     uncommitted_region.BaseAddress
376 	 *            |   | Uncommitted region |     + uncommitted_region.RegionSize
377 	 *            v   |                    |
378 	 * low_limit  --> |------------------- | <-- uncommitted_region.BaseAddress
379 	 *                :                    :
380 	 *                :                    :
381 	 *                v  Lower addresses   v
382 	 */
383 
384 	GetCurrentThreadStackLimits(&low_limit, &high_limit);
385 
386 	result_size = VirtualQuery((void*)low_limit,
387 			&uncommitted_region, sizeof(uncommitted_region));
388 	ZEND_ASSERT(result_size >= sizeof(uncommitted_region));
389 
390 	result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize,
391 			&guard_region, sizeof(guard_region));
392 	ZEND_ASSERT(result_size >= sizeof(uncommitted_region));
393 
394 	stack->base = (void*)high_limit;
395 	stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit;
396 
397 	ZEND_ASSERT(stack->max_size > guard_region.RegionSize);
398 	stack->max_size -= guard_region.RegionSize;
399 
400 	/* The uncommitted region does not shrink below 1 page */
401 	page_size = zend_get_page_size();
402 	ZEND_ASSERT(stack->max_size > page_size);
403 	stack->max_size -= page_size;
404 
405 	return true;
406 }
407 #else /* ZEND_WIN32 */
zend_call_stack_get_win32(zend_call_stack * stack)408 static bool zend_call_stack_get_win32(zend_call_stack *stack)
409 {
410 	return false;
411 }
412 #endif /* ZEND_WIN32 */
413 
414 #if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
zend_call_stack_get_macos(zend_call_stack * stack)415 static bool zend_call_stack_get_macos(zend_call_stack *stack)
416 {
417 	void *base = pthread_get_stackaddr_np(pthread_self());
418 	size_t max_size;
419 
420 	if (pthread_main_np()) {
421 		/* pthread_get_stacksize_np() returns a too low value for the main
422 		 * thread in OSX 10.9, 10.10:
423 		 * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html
424 		 * https://github.com/rust-lang/rust/issues/43347
425 		 */
426 
427 		/* Stack size is 8MiB by default for main threads
428 		 * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */
429 		max_size = 8 * 1024 * 1024;
430 	} else {
431 		max_size = pthread_get_stacksize_np(pthread_self());
432 	}
433 
434 	stack->base = base;
435 	stack->max_size = max_size;
436 
437 	return true;
438 }
439 #else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */
zend_call_stack_get_macos(zend_call_stack * stack)440 static bool zend_call_stack_get_macos(zend_call_stack *stack)
441 {
442 	return false;
443 }
444 #endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */
445 
446 #if defined(__OpenBSD__)
447 #if defined(HAVE_PTHREAD_STACKSEG_NP)
zend_call_stack_get_openbsd_pthread(zend_call_stack * stack)448 static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
449 {
450 	stack_t ss;
451 
452 	if (pthread_stackseg_np(pthread_self(), &ss) != 0) {
453 		return false;
454 	}
455 
456 	stack->base = (char *)ss.ss_sp - ss.ss_size;
457 	stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE);
458 
459 	return true;
460 }
461 #else
zend_call_stack_get_openbsd_pthread(zend_call_stack * stack)462 static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack)
463 {
464 	return false;
465 }
466 #endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */
467 
zend_call_stack_get_openbsd_vm(zend_call_stack * stack)468 static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack)
469 {
470 	struct _ps_strings ps;
471 	struct rlimit rlim;
472 	int mib[2] = {CTL_VM, VM_PSSTRINGS };
473 	size_t len = sizeof(ps), pagesize;
474 
475 	if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) {
476 		return false;
477 	}
478 
479 	if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
480 		return false;
481 	}
482 
483 	if (rlim.rlim_cur == RLIM_INFINITY) {
484 		return false;
485 	}
486 
487 	pagesize = sysconf(_SC_PAGE_SIZE);
488 
489 	stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1));
490 	stack->max_size = rlim.rlim_cur - pagesize;
491 
492 	return true;
493 }
494 
zend_call_stack_get_openbsd(zend_call_stack * stack)495 static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
496 {
497 	// TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np)
498 	if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) {
499 		return zend_call_stack_get_openbsd_vm(stack);
500 	}
501 
502 	return zend_call_stack_get_openbsd_pthread(stack);
503 }
504 
505 #else
zend_call_stack_get_openbsd(zend_call_stack * stack)506 static bool zend_call_stack_get_openbsd(zend_call_stack *stack)
507 {
508 	return false;
509 }
510 #endif /* defined(__OpenBSD__) */
511 
512 /** Get the stack information for the calling thread */
zend_call_stack_get(zend_call_stack * stack)513 ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
514 {
515 	if (zend_call_stack_get_linux(stack)) {
516 		return true;
517 	}
518 
519 	if (zend_call_stack_get_freebsd(stack)) {
520 		return true;
521 	}
522 
523 	if (zend_call_stack_get_win32(stack)) {
524 		return true;
525 	}
526 
527 	if (zend_call_stack_get_macos(stack)) {
528 		return true;
529 	}
530 
531 	if (zend_call_stack_get_openbsd(stack)) {
532 		return true;
533 	}
534 
535 	return false;
536 }
537 
538 #endif /* ZEND_CHECK_STACK_LIMIT */
539