/src/php-src/Zend/zend_call_stack.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright © Zend Technologies Ltd., a subsidiary company of | |
6 | | | Perforce Software, Inc., and Contributors. | |
7 | | +----------------------------------------------------------------------+ |
8 | | | This source file is subject to the Modified BSD License that is | |
9 | | | bundled with this package in the file LICENSE, and is available | |
10 | | | through the World Wide Web at <https://www.php.net/license/>. | |
11 | | | | |
12 | | | SPDX-License-Identifier: BSD-3-Clause | |
13 | | +----------------------------------------------------------------------+ |
14 | | | Authors: Arnaud Le Blanc <arnaud.lb@gmail.com> | |
15 | | +----------------------------------------------------------------------+ |
16 | | */ |
17 | | |
18 | | /* Inspired from Chromium's stack_util.cc */ |
19 | | |
20 | | #include "zend.h" |
21 | | #include "zend_globals.h" |
22 | | #include "zend_portability.h" |
23 | | #include "zend_call_stack.h" |
24 | | #include <stdint.h> |
25 | | #ifdef ZEND_WIN32 |
26 | | # include <processthreadsapi.h> |
27 | | # include <memoryapi.h> |
28 | | #else /* ZEND_WIN32 */ |
29 | | # include <sys/resource.h> |
30 | | # ifdef HAVE_UNISTD_H |
31 | | # include <unistd.h> |
32 | | # endif |
33 | | # ifdef HAVE_SYS_TYPES_H |
34 | | # include <sys/types.h> |
35 | | # endif |
36 | | #endif /* ZEND_WIN32 */ |
37 | | #if (defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)) || \ |
38 | | defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || \ |
39 | | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun) |
40 | | # include <pthread.h> |
41 | | #endif |
42 | | #if defined(__FreeBSD__) || defined(__DragonFly__) |
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 __NetBSD__ |
56 | | # include <sys/sysctl.h> |
57 | | # include <sys/syscall.h> |
58 | | #endif |
59 | | #ifdef __HAIKU__ |
60 | | # include <kernel/OS.h> |
61 | | #endif |
62 | | #ifdef __linux__ |
63 | | #include <sys/syscall.h> |
64 | | #endif |
65 | | #ifdef __sun |
66 | | # include <sys/lwp.h> |
67 | | # ifdef HAVE_LIBPROC_H |
68 | | # define _STRUCTURED_PROC 1 |
69 | | # include <sys/procfs.h> |
70 | | # include <libproc.h> |
71 | | # endif |
72 | | #include <thread.h> |
73 | | #endif |
74 | | |
75 | | #ifdef HAVE_VALGRIND |
76 | | # include <valgrind/valgrind.h> |
77 | | #endif |
78 | | |
79 | | #ifdef ZEND_CHECK_STACK_LIMIT |
80 | | |
81 | | /* Called once per process or thread */ |
82 | 2 | ZEND_API void zend_call_stack_init(void) { |
83 | 2 | if (!zend_call_stack_get(&EG(call_stack))) { |
84 | 0 | EG(call_stack) = (zend_call_stack){0}; |
85 | 0 | } |
86 | | |
87 | 2 | switch (EG(max_allowed_stack_size)) { |
88 | 2 | case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: { |
89 | 2 | void *base = EG(call_stack).base; |
90 | 2 | size_t size = EG(call_stack).max_size; |
91 | 2 | if (UNEXPECTED(base == (void*)0)) { |
92 | 0 | base = zend_call_stack_position(); |
93 | 0 | size = zend_call_stack_default_size(); |
94 | | /* base is not the actual stack base */ |
95 | 0 | size -= 32 * 1024; |
96 | 0 | } |
97 | 2 | EG(stack_base) = base; |
98 | 2 | EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size)); |
99 | 2 | break; |
100 | 0 | } |
101 | 0 | case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: { |
102 | 0 | EG(stack_base) = (void*)0; |
103 | 0 | EG(stack_limit) = (void*)0; |
104 | 0 | break; |
105 | 0 | } |
106 | 0 | default: { |
107 | 0 | ZEND_ASSERT(EG(max_allowed_stack_size) > 0); |
108 | 0 | void *base = EG(call_stack).base; |
109 | 0 | if (UNEXPECTED(base == (void*)0)) { |
110 | 0 | base = zend_call_stack_position(); |
111 | 0 | } |
112 | 0 | EG(stack_base) = base; |
113 | 0 | EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size)); |
114 | 0 | break; |
115 | 0 | } |
116 | 2 | } |
117 | 2 | } |
118 | | |
119 | | #ifdef __linux__ |
120 | 4 | static bool zend_call_stack_is_main_thread(void) { |
121 | 4 | # ifdef HAVE_GETTID |
122 | 4 | return getpid() == gettid(); |
123 | | # else |
124 | | return getpid() == syscall(SYS_gettid); |
125 | | # endif |
126 | 4 | } |
127 | | |
128 | | # if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) |
129 | | static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) |
130 | 0 | { |
131 | 0 | pthread_attr_t attr; |
132 | 0 | int error; |
133 | 0 | void *addr; |
134 | 0 | size_t max_size; |
135 | | |
136 | | /* pthread_getattr_np() will return bogus values for the main thread with |
137 | | * musl or with some old glibc versions */ |
138 | 0 | ZEND_ASSERT(!zend_call_stack_is_main_thread()); |
139 | |
|
140 | 0 | error = pthread_getattr_np(pthread_self(), &attr); |
141 | 0 | if (UNEXPECTED(error)) { |
142 | 0 | return false; |
143 | 0 | } |
144 | | |
145 | 0 | error = pthread_attr_getstack(&attr, &addr, &max_size); |
146 | 0 | if (UNEXPECTED(error)) { |
147 | 0 | pthread_attr_destroy(&attr); |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | | # if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)) |
152 | | { |
153 | | size_t guard_size; |
154 | | /* In glibc prior to 2.8, addr and size include the guard pages */ |
155 | | error = pthread_attr_getguardsize(&attr, &guard_size); |
156 | | if (UNEXPECTED(error)) { |
157 | | pthread_attr_destroy(&attr); |
158 | | return false; |
159 | | } |
160 | | |
161 | | addr = (int8_t*)addr + guard_size; |
162 | | max_size -= guard_size; |
163 | | } |
164 | | # endif /* glibc < 2.8 */ |
165 | | |
166 | 0 | stack->base = (int8_t*)addr + max_size; |
167 | 0 | stack->max_size = max_size; |
168 | |
|
169 | 0 | pthread_attr_destroy(&attr); |
170 | |
|
171 | 0 | return true; |
172 | 0 | } |
173 | | # else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ |
174 | | static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) |
175 | | { |
176 | | return false; |
177 | | } |
178 | | # endif /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ |
179 | | |
180 | | static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack) |
181 | 2 | { |
182 | 2 | FILE *f; |
183 | 2 | char buffer[4096]; |
184 | 2 | uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); |
185 | 2 | uintptr_t start, end, prev_end = 0; |
186 | 2 | size_t max_size; |
187 | 2 | bool found = false; |
188 | 2 | struct rlimit rlim; |
189 | 2 | int error; |
190 | | |
191 | | /* This method is relevant only for the main thread */ |
192 | 2 | ZEND_ASSERT(zend_call_stack_is_main_thread()); |
193 | | |
194 | | /* Scan the process memory mappings to find the one containing the stack. |
195 | | * |
196 | | * The end of the stack mapping is the base of the stack. The start is |
197 | | * adjusted by the kernel as the stack grows. The maximum stack size is |
198 | | * determined by RLIMIT_STACK and the previous mapping. |
199 | | * |
200 | | * |
201 | | * ^ Higher addresses ^ |
202 | | * : : |
203 | | * : : |
204 | | * Mapping end --> |-------------------| <-- Stack base (stack start) |
205 | | * | | ^ |
206 | | * | Stack Mapping | | Stack size |
207 | | * | | v |
208 | | * Mapping start --> |-------------------| <-- Current stack end |
209 | | * (adjusted : : |
210 | | * downwards as the . . |
211 | | * stack grows) : : |
212 | | * |-------------------| |
213 | | * | Some Mapping | The previous mapping may prevent |
214 | | * |-------------------| stack growth |
215 | | * : : |
216 | | * : : |
217 | | * v Lower addresses v |
218 | | */ |
219 | | |
220 | 2 | f = fopen("/proc/self/maps", "r"); |
221 | 2 | if (!f) { |
222 | 0 | return false; |
223 | 0 | } |
224 | | |
225 | 136 | while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) { |
226 | 136 | if (start <= addr_on_stack && end >= addr_on_stack) { |
227 | 2 | found = true; |
228 | 2 | break; |
229 | 2 | } |
230 | 134 | prev_end = end; |
231 | 134 | } |
232 | | |
233 | 2 | fclose(f); |
234 | | |
235 | 2 | if (!found) { |
236 | 0 | return false; |
237 | 0 | } |
238 | | |
239 | 2 | error = getrlimit(RLIMIT_STACK, &rlim); |
240 | 2 | if (error || rlim.rlim_cur == RLIM_INFINITY) { |
241 | 0 | return false; |
242 | 0 | } |
243 | | |
244 | 2 | max_size = rlim.rlim_cur; |
245 | | |
246 | | #ifdef HAVE_VALGRIND |
247 | | /* Under Valgrind, the last page is not useable */ |
248 | | if (RUNNING_ON_VALGRIND) { |
249 | | max_size -= zend_get_page_size(); |
250 | | } |
251 | | #endif |
252 | | |
253 | | /* Previous mapping may prevent the stack from growing */ |
254 | 2 | if (end - max_size < prev_end) { |
255 | 0 | max_size = prev_end - end; |
256 | 0 | } |
257 | | |
258 | 2 | stack->base = (void*)end; |
259 | 2 | stack->max_size = max_size; |
260 | | |
261 | 2 | return true; |
262 | 2 | } |
263 | | |
264 | | static bool zend_call_stack_get_linux(zend_call_stack *stack) |
265 | 2 | { |
266 | 2 | if (zend_call_stack_is_main_thread()) { |
267 | 2 | return zend_call_stack_get_linux_proc_maps(stack); |
268 | 2 | } |
269 | | |
270 | 0 | return zend_call_stack_get_linux_pthread(stack); |
271 | 2 | } |
272 | | #else /* __linux__ */ |
273 | | static bool zend_call_stack_get_linux(zend_call_stack *stack) |
274 | | { |
275 | | return false; |
276 | | } |
277 | | #endif /* __linux__ */ |
278 | | |
279 | | #if defined(__FreeBSD__) || defined(__DragonFly__) |
280 | | static bool zend_call_stack_is_main_thread(void) |
281 | | { |
282 | | int is_main = pthread_main_np(); |
283 | | return is_main == -1 || is_main == 1; |
284 | | } |
285 | | |
286 | | # if defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) |
287 | | static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack) |
288 | | { |
289 | | pthread_attr_t attr; |
290 | | int error; |
291 | | void *addr; |
292 | | size_t max_size; |
293 | | |
294 | | /* pthread will return bogus values for the main thread */ |
295 | | ZEND_ASSERT(!zend_call_stack_is_main_thread()); |
296 | | |
297 | | pthread_attr_init(&attr); |
298 | | |
299 | | error = pthread_attr_get_np(pthread_self(), &attr); |
300 | | if (error) { |
301 | | goto fail; |
302 | | } |
303 | | |
304 | | error = pthread_attr_getstack(&attr, &addr, &max_size); |
305 | | if (error) { |
306 | | goto fail; |
307 | | } |
308 | | |
309 | | stack->base = (int8_t*)addr + max_size; |
310 | | stack->max_size = max_size; |
311 | | |
312 | | pthread_attr_destroy(&attr); |
313 | | return true; |
314 | | |
315 | | fail: |
316 | | pthread_attr_destroy(&attr); |
317 | | return false; |
318 | | } |
319 | | # else /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ |
320 | | static bool zend_call_stack_get_freebsd_pthread(zend_call_stack *stack) |
321 | | { |
322 | | return false; |
323 | | } |
324 | | # endif /* defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ |
325 | | |
326 | | static bool zend_call_stack_get_freebsd_sysctl(zend_call_stack *stack) |
327 | | { |
328 | | void *stack_base; |
329 | | int mib[2] = {CTL_KERN, KERN_USRSTACK}; |
330 | | size_t len = sizeof(stack_base); |
331 | | struct rlimit rlim; |
332 | | size_t numguards = 0; |
333 | | |
334 | | /* This method is relevant only for the main thread */ |
335 | | ZEND_ASSERT(zend_call_stack_is_main_thread()); |
336 | | |
337 | | if (sysctl(mib, sizeof(mib)/sizeof(*mib), &stack_base, &len, NULL, 0) != 0) { |
338 | | return false; |
339 | | } |
340 | | |
341 | | if (getrlimit(RLIMIT_STACK, &rlim) != 0) { |
342 | | return false; |
343 | | } |
344 | | |
345 | | if (rlim.rlim_cur == RLIM_INFINITY) { |
346 | | return false; |
347 | | } |
348 | | |
349 | | len = sizeof(numguards); |
350 | | /* For most of the cases, we do not necessarily need to do so as, by default, it is `1` page, but is user writable */ |
351 | | if (sysctlbyname("security.bsd.stack_guard_page", &numguards, &len, NULL, 0) != 0) { |
352 | | return false; |
353 | | } |
354 | | |
355 | | size_t guard_size = numguards * getpagesize(); |
356 | | |
357 | | stack->base = stack_base; |
358 | | stack->max_size = rlim.rlim_cur - guard_size; |
359 | | |
360 | | return true; |
361 | | } |
362 | | |
363 | | static bool zend_call_stack_get_freebsd(zend_call_stack *stack) |
364 | | { |
365 | | if (zend_call_stack_is_main_thread()) { |
366 | | return zend_call_stack_get_freebsd_sysctl(stack); |
367 | | } |
368 | | |
369 | | return zend_call_stack_get_freebsd_pthread(stack); |
370 | | } |
371 | | #else |
372 | | static bool zend_call_stack_get_freebsd(zend_call_stack *stack) |
373 | 0 | { |
374 | 0 | return false; |
375 | 0 | } |
376 | | #endif /* __FreeBSD__ */ |
377 | | |
378 | | #ifdef ZEND_WIN32 |
379 | | static bool zend_call_stack_get_win32(zend_call_stack *stack) |
380 | | { |
381 | | ULONG_PTR low_limit, high_limit; |
382 | | MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0}; |
383 | | size_t result_size, page_size; |
384 | | |
385 | | /* The stack consists of three regions: committed, guard, and uncommitted. |
386 | | * Memory is committed when the guard region is accessed. If only one page |
387 | | * is left in the uncommitted region, a stack overflow error is raised |
388 | | * instead. |
389 | | * |
390 | | * The total useable stack size is the size of the committed and uncommitted |
391 | | * regions less one page. |
392 | | * |
393 | | * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx |
394 | | * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size |
395 | | * |
396 | | * ^ Higher addresses ^ |
397 | | * : : |
398 | | * : : |
399 | | * high_limit --> |--------------------| |
400 | | * ^ | | |
401 | | * | | Committed region | |
402 | | * | | | |
403 | | * | |------------------- | <-- guard_region.BaseAddress |
404 | | * reserved | | | + guard_region.RegionSize |
405 | | * size | | Guard region | |
406 | | * | | | |
407 | | * | |--------------------| <-- guard_region.BaseAddress, |
408 | | * | | | uncommitted_region.BaseAddress |
409 | | * | | Uncommitted region | + uncommitted_region.RegionSize |
410 | | * v | | |
411 | | * low_limit --> |------------------- | <-- uncommitted_region.BaseAddress |
412 | | * : : |
413 | | * : : |
414 | | * v Lower addresses v |
415 | | */ |
416 | | |
417 | | GetCurrentThreadStackLimits(&low_limit, &high_limit); |
418 | | |
419 | | result_size = VirtualQuery((void*)low_limit, |
420 | | &uncommitted_region, sizeof(uncommitted_region)); |
421 | | ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); |
422 | | |
423 | | result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize, |
424 | | &guard_region, sizeof(guard_region)); |
425 | | ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); |
426 | | |
427 | | stack->base = (void*)high_limit; |
428 | | stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit; |
429 | | |
430 | | ZEND_ASSERT(stack->max_size > guard_region.RegionSize); |
431 | | stack->max_size -= guard_region.RegionSize; |
432 | | |
433 | | /* The uncommitted region does not shrink below 1 page */ |
434 | | page_size = zend_get_page_size(); |
435 | | ZEND_ASSERT(stack->max_size > page_size); |
436 | | stack->max_size -= page_size; |
437 | | |
438 | | return true; |
439 | | } |
440 | | #else /* ZEND_WIN32 */ |
441 | | static bool zend_call_stack_get_win32(zend_call_stack *stack) |
442 | 0 | { |
443 | 0 | return false; |
444 | 0 | } |
445 | | #endif /* ZEND_WIN32 */ |
446 | | |
447 | | #if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) |
448 | | static bool zend_call_stack_get_macos(zend_call_stack *stack) |
449 | | { |
450 | | void *base = pthread_get_stackaddr_np(pthread_self()); |
451 | | size_t max_size; |
452 | | |
453 | | #if !defined(__aarch64__) |
454 | | if (pthread_main_np()) |
455 | | { |
456 | | /* pthread_get_stacksize_np() returns a too low value for the main |
457 | | * thread in OSX 10.9, 10.10: |
458 | | * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html |
459 | | * https://github.com/rust-lang/rust/issues/43347 |
460 | | */ |
461 | | |
462 | | /* Stack size is 8MiB by default for main threads |
463 | | * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */ |
464 | | max_size = 8 * 1024 * 1024; |
465 | | } |
466 | | else |
467 | | #endif |
468 | | { |
469 | | max_size = pthread_get_stacksize_np(pthread_self()); |
470 | | } |
471 | | |
472 | | stack->base = base; |
473 | | stack->max_size = max_size; |
474 | | |
475 | | return true; |
476 | | } |
477 | | #else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ |
478 | | static bool zend_call_stack_get_macos(zend_call_stack *stack) |
479 | 0 | { |
480 | 0 | return false; |
481 | 0 | } |
482 | | #endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ |
483 | | |
484 | | #if defined(__OpenBSD__) |
485 | | #if defined(HAVE_PTHREAD_STACKSEG_NP) |
486 | | static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) |
487 | | { |
488 | | stack_t ss; |
489 | | |
490 | | if (pthread_stackseg_np(pthread_self(), &ss) != 0) { |
491 | | return false; |
492 | | } |
493 | | |
494 | | stack->base = (char *)ss.ss_sp - ss.ss_size; |
495 | | stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE); |
496 | | |
497 | | return true; |
498 | | } |
499 | | #else |
500 | | static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) |
501 | | { |
502 | | return false; |
503 | | } |
504 | | #endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */ |
505 | | |
506 | | static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack) |
507 | | { |
508 | | struct _ps_strings ps; |
509 | | struct rlimit rlim; |
510 | | int mib[2] = {CTL_VM, VM_PSSTRINGS }; |
511 | | size_t len = sizeof(ps), pagesize; |
512 | | |
513 | | if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) { |
514 | | return false; |
515 | | } |
516 | | |
517 | | if (getrlimit(RLIMIT_STACK, &rlim) != 0) { |
518 | | return false; |
519 | | } |
520 | | |
521 | | if (rlim.rlim_cur == RLIM_INFINITY) { |
522 | | return false; |
523 | | } |
524 | | |
525 | | pagesize = sysconf(_SC_PAGE_SIZE); |
526 | | |
527 | | stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1)); |
528 | | stack->max_size = rlim.rlim_cur - pagesize; |
529 | | |
530 | | return true; |
531 | | } |
532 | | |
533 | | static bool zend_call_stack_get_openbsd(zend_call_stack *stack) |
534 | | { |
535 | | // TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np) |
536 | | if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) { |
537 | | return zend_call_stack_get_openbsd_vm(stack); |
538 | | } |
539 | | |
540 | | return zend_call_stack_get_openbsd_pthread(stack); |
541 | | } |
542 | | |
543 | | #else |
544 | | static bool zend_call_stack_get_openbsd(zend_call_stack *stack) |
545 | 0 | { |
546 | 0 | return false; |
547 | 0 | } |
548 | | #endif /* defined(__OpenBSD__) */ |
549 | | #if defined(__HAIKU__) |
550 | | static bool zend_call_stack_get_haiku(zend_call_stack *stack) |
551 | | { |
552 | | thread_id id; |
553 | | thread_info ti; |
554 | | size_t guard_size; |
555 | | |
556 | | // unlikely, main thread ought to be always available but we never know |
557 | | if ((id = find_thread(NULL)) == B_NAME_NOT_FOUND || get_thread_info(id, &ti) != B_OK) { |
558 | | return false; |
559 | | } |
560 | | |
561 | | // USER_STACK_GUARD_SIZE |
562 | | guard_size = sysconf(_SC_PAGESIZE) * 4; |
563 | | |
564 | | stack->base = ti.stack_end; |
565 | | stack->max_size = ((size_t)ti.stack_end - (size_t)ti.stack_base) - guard_size; |
566 | | |
567 | | return true; |
568 | | } |
569 | | #else |
570 | | static bool zend_call_stack_get_haiku(zend_call_stack *stack) |
571 | 0 | { |
572 | 0 | return false; |
573 | 0 | } |
574 | | #endif /* defined(__HAIKU__) */ |
575 | | |
576 | | #if defined(__NetBSD__) |
577 | | # ifdef HAVE_PTHREAD_GETATTR_NP |
578 | | static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack) |
579 | | { |
580 | | pthread_attr_t attr; |
581 | | int error; |
582 | | void *addr; |
583 | | size_t max_size, guard_size; |
584 | | |
585 | | error = pthread_getattr_np(pthread_self(), &attr); |
586 | | if (error) { |
587 | | return false; |
588 | | } |
589 | | |
590 | | error = pthread_attr_getstack(&attr, &addr, &max_size); |
591 | | if (error) { |
592 | | return false; |
593 | | } |
594 | | |
595 | | error = pthread_attr_getguardsize(&attr, &guard_size); |
596 | | if (error) { |
597 | | return false; |
598 | | } |
599 | | |
600 | | addr = (char *)addr + guard_size; |
601 | | max_size -= guard_size; |
602 | | |
603 | | stack->base = (char *)addr + max_size; |
604 | | stack->max_size = max_size; |
605 | | |
606 | | return true; |
607 | | } |
608 | | # else |
609 | | static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack) |
610 | | { |
611 | | return false; |
612 | | } |
613 | | # endif /* HAVE_PTHREAD_GETATTR_NP */ |
614 | | static bool zend_call_stack_get_netbsd_vm(zend_call_stack *stack, void **ptr) |
615 | | { |
616 | | /** |
617 | | * NetBSD supports procfs in a similar fashion as Linux |
618 | | * however NetBSD's mid/long term plan is to remove it completely. |
619 | | */ |
620 | | char *start, *end; |
621 | | struct kinfo_vmentry *entry; |
622 | | size_t len, max_size; |
623 | | uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); |
624 | | int mib[5] = { CTL_VM, VM_PROC, VM_PROC_MAP, getpid(), sizeof(struct kinfo_vmentry) }; |
625 | | bool found = false; |
626 | | struct rlimit rlim; |
627 | | |
628 | | if (sysctl(mib, 5, NULL, &len, NULL, 0) != 0) { |
629 | | return false; |
630 | | } |
631 | | |
632 | | // kinfo_getvmmap uses the same formula, only we do not want to rely on libkvm |
633 | | len = len * 4 / 3 ; |
634 | | *ptr = malloc(len); |
635 | | |
636 | | if (sysctl(mib, 5, *ptr, &len, NULL, 0) != 0) { |
637 | | return false; |
638 | | } |
639 | | |
640 | | start = (char *)*ptr; |
641 | | end = start + len; |
642 | | |
643 | | while (start < end) { |
644 | | entry = (struct kinfo_vmentry *)start; |
645 | | if (entry->kve_start <= addr_on_stack && entry->kve_end >= addr_on_stack) { |
646 | | found = true; |
647 | | break; |
648 | | } |
649 | | |
650 | | start += sizeof(struct kinfo_vmentry); |
651 | | } |
652 | | |
653 | | if (!found) { |
654 | | return false; |
655 | | } |
656 | | |
657 | | if (getrlimit(RLIMIT_STACK, &rlim) || rlim.rlim_cur == RLIM_INFINITY) { |
658 | | return false; |
659 | | } |
660 | | |
661 | | max_size = rlim.rlim_cur; |
662 | | |
663 | | stack->base = (void *)entry->kve_end; |
664 | | stack->max_size = max_size; |
665 | | |
666 | | return true; |
667 | | } |
668 | | |
669 | | |
670 | | static bool zend_call_stack_get_netbsd(zend_call_stack *stack) |
671 | | { |
672 | | if (syscall(SYS__lwp_self) == 1) { |
673 | | void *ptr = NULL; |
674 | | bool r = zend_call_stack_get_netbsd_vm(stack, &ptr); |
675 | | free(ptr); |
676 | | return r; |
677 | | } |
678 | | |
679 | | return zend_call_stack_get_netbsd_pthread(stack); |
680 | | } |
681 | | #else |
682 | | static bool zend_call_stack_get_netbsd(zend_call_stack *stack) |
683 | 0 | { |
684 | 0 | return false; |
685 | 0 | } |
686 | | #endif /* defined(__NetBSD__) */ |
687 | | |
688 | | #if defined(__sun) |
689 | | static bool zend_call_stack_get_solaris_pthread(zend_call_stack *stack) |
690 | | { |
691 | | stack_t s; |
692 | | if (thr_stksegment(&s) < 0) { |
693 | | return false; |
694 | | } |
695 | | |
696 | | stack->max_size = s.ss_size; |
697 | | stack->base = s.ss_sp; |
698 | | return true; |
699 | | } |
700 | | |
701 | | #ifdef HAVE_LIBPROC_H |
702 | | static bool zend_call_stack_get_solaris_proc_maps(zend_call_stack *stack) |
703 | | { |
704 | | uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); |
705 | | bool found = false, r = false; |
706 | | struct ps_prochandle *proc; |
707 | | prmap_t *map, *orig; |
708 | | struct rlimit rlim; |
709 | | char path[PATH_MAX]; |
710 | | size_t size; |
711 | | ssize_t len = -1; |
712 | | pid_t pid; |
713 | | int error, fd; |
714 | | |
715 | | pid = getpid(); |
716 | | proc = Pgrab(pid, PGRAB_RDONLY, &error); |
717 | | if (!proc) { |
718 | | return false; |
719 | | } |
720 | | |
721 | | size = (1 << 20); |
722 | | snprintf(path, sizeof(path), "/proc/%d/map", (int)pid); |
723 | | |
724 | | if ((fd = open(path, O_RDONLY)) == -1) { |
725 | | Prelease(proc, 0); |
726 | | return false; |
727 | | } |
728 | | |
729 | | orig = malloc(size); |
730 | | if (!orig) { |
731 | | Prelease(proc, 0); |
732 | | close(fd); |
733 | | return false; |
734 | | } |
735 | | |
736 | | while (size > 0 && (len = pread(fd, orig, size, 0)) == size) { |
737 | | prmap_t *tmp; |
738 | | size <<= 1; |
739 | | tmp = realloc(orig, size); |
740 | | if (!tmp) { |
741 | | goto end; |
742 | | } |
743 | | orig = tmp; |
744 | | } |
745 | | |
746 | | for (map = orig; len > 0; ++map) { |
747 | | if ((uintptr_t)map->pr_vaddr <= addr_on_stack && (uintptr_t)map->pr_vaddr + map->pr_size >= addr_on_stack) { |
748 | | found = true; |
749 | | break; |
750 | | } |
751 | | len -= sizeof(*map); |
752 | | } |
753 | | |
754 | | if (!found) { |
755 | | goto end; |
756 | | } |
757 | | |
758 | | error = getrlimit(RLIMIT_STACK, &rlim); |
759 | | if (error || rlim.rlim_cur == RLIM_INFINITY) { |
760 | | goto end; |
761 | | } |
762 | | |
763 | | stack->base = (void *)map->pr_vaddr + map->pr_size; |
764 | | stack->max_size = rlim.rlim_cur; |
765 | | r = true; |
766 | | |
767 | | end: |
768 | | free(orig); |
769 | | Prelease(proc, 0); |
770 | | close(fd); |
771 | | return r; |
772 | | } |
773 | | #endif |
774 | | |
775 | | static bool zend_call_stack_get_solaris(zend_call_stack *stack) |
776 | | { |
777 | | #ifdef HAVE_LIBPROC_H |
778 | | if (_lwp_self() == 1) { |
779 | | return zend_call_stack_get_solaris_proc_maps(stack); |
780 | | } |
781 | | #endif |
782 | | return zend_call_stack_get_solaris_pthread(stack); |
783 | | } |
784 | | #else |
785 | | static bool zend_call_stack_get_solaris(zend_call_stack *stack) |
786 | 0 | { |
787 | 0 | return false; |
788 | 0 | } |
789 | | #endif /* defined(__sun) */ |
790 | | |
791 | | /** Get the stack information for the calling thread */ |
792 | | ZEND_API bool zend_call_stack_get(zend_call_stack *stack) |
793 | 2 | { |
794 | 2 | if (zend_call_stack_get_linux(stack)) { |
795 | 2 | return true; |
796 | 2 | } |
797 | | |
798 | 0 | if (zend_call_stack_get_freebsd(stack)) { |
799 | 0 | return true; |
800 | 0 | } |
801 | | |
802 | 0 | if (zend_call_stack_get_win32(stack)) { |
803 | 0 | return true; |
804 | 0 | } |
805 | | |
806 | 0 | if (zend_call_stack_get_macos(stack)) { |
807 | 0 | return true; |
808 | 0 | } |
809 | | |
810 | 0 | if (zend_call_stack_get_openbsd(stack)) { |
811 | 0 | return true; |
812 | 0 | } |
813 | | |
814 | 0 | if (zend_call_stack_get_netbsd(stack)) { |
815 | 0 | return true; |
816 | 0 | } |
817 | | |
818 | 0 | if (zend_call_stack_get_haiku(stack)) { |
819 | 0 | return true; |
820 | 0 | } |
821 | | |
822 | 0 | if (zend_call_stack_get_solaris(stack)) { |
823 | 0 | return true; |
824 | 0 | } |
825 | | |
826 | 0 | return false; |
827 | 0 | } |
828 | | |
829 | | #endif /* ZEND_CHECK_STACK_LIMIT */ |