/src/php-src/Zend/zend_call_stack.c
Line | Count | Source (jump to first uncovered line) |
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 */ |
83 | 16 | ZEND_API void zend_call_stack_init(void) { |
84 | 16 | if (!zend_call_stack_get(&EG(call_stack))) { |
85 | 0 | EG(call_stack) = (zend_call_stack){0}; |
86 | 0 | } |
87 | | |
88 | 16 | switch (EG(max_allowed_stack_size)) { |
89 | 16 | case ZEND_MAX_ALLOWED_STACK_SIZE_DETECT: { |
90 | 16 | void *base = EG(call_stack).base; |
91 | 16 | size_t size = EG(call_stack).max_size; |
92 | 16 | if (UNEXPECTED(base == (void*)0)) { |
93 | 0 | base = zend_call_stack_position(); |
94 | 0 | size = zend_call_stack_default_size(); |
95 | | /* base is not the actual stack base */ |
96 | 0 | size -= 32 * 1024; |
97 | 0 | } |
98 | 16 | EG(stack_base) = base; |
99 | 16 | EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size)); |
100 | 16 | break; |
101 | 0 | } |
102 | 0 | case ZEND_MAX_ALLOWED_STACK_SIZE_UNCHECKED: { |
103 | 0 | EG(stack_base) = (void*)0; |
104 | 0 | EG(stack_limit) = (void*)0; |
105 | 0 | break; |
106 | 0 | } |
107 | 0 | default: { |
108 | 0 | ZEND_ASSERT(EG(max_allowed_stack_size) > 0); |
109 | 0 | void *base = EG(call_stack).base; |
110 | 0 | if (UNEXPECTED(base == (void*)0)) { |
111 | 0 | base = zend_call_stack_position(); |
112 | 0 | } |
113 | 0 | EG(stack_base) = base; |
114 | 0 | EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size)); |
115 | 0 | break; |
116 | 0 | } |
117 | 16 | } |
118 | 16 | } |
119 | | |
120 | | #ifdef __linux__ |
121 | 32 | static bool zend_call_stack_is_main_thread(void) { |
122 | 32 | # ifdef HAVE_GETTID |
123 | 32 | return getpid() == gettid(); |
124 | | # else |
125 | | return getpid() == syscall(SYS_gettid); |
126 | | # endif |
127 | 32 | } |
128 | | |
129 | | # if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) |
130 | | static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) |
131 | 0 | { |
132 | 0 | pthread_attr_t attr; |
133 | 0 | int error; |
134 | 0 | void *addr; |
135 | 0 | 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 | 0 | ZEND_ASSERT(!zend_call_stack_is_main_thread()); |
140 | | |
141 | 0 | error = pthread_getattr_np(pthread_self(), &attr); |
142 | 0 | if (UNEXPECTED(error)) { |
143 | 0 | return false; |
144 | 0 | } |
145 | | |
146 | 0 | error = pthread_attr_getstack(&attr, &addr, &max_size); |
147 | 0 | if (UNEXPECTED(error)) { |
148 | 0 | pthread_attr_destroy(&attr); |
149 | 0 | return false; |
150 | 0 | } |
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 | 0 | stack->base = (int8_t*)addr + max_size; |
168 | 0 | stack->max_size = max_size; |
169 | |
|
170 | 0 | pthread_attr_destroy(&attr); |
171 | |
|
172 | 0 | return true; |
173 | 0 | } |
174 | | # else /* defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) */ |
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 | | |
181 | | static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack) |
182 | 16 | { |
183 | 16 | FILE *f; |
184 | 16 | char buffer[4096]; |
185 | 16 | uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); |
186 | 16 | uintptr_t start, end, prev_end = 0; |
187 | 16 | size_t max_size; |
188 | 16 | bool found = false; |
189 | 16 | struct rlimit rlim; |
190 | 16 | int error; |
191 | | |
192 | | /* This method is relevant only for the main thread */ |
193 | 16 | 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 | 16 | f = fopen("/proc/self/maps", "r"); |
222 | 16 | if (!f) { |
223 | 0 | return false; |
224 | 0 | } |
225 | | |
226 | 1.09k | while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) { |
227 | 1.09k | if (start <= addr_on_stack && end >= addr_on_stack) { |
228 | 16 | found = true; |
229 | 16 | break; |
230 | 16 | } |
231 | 1.08k | prev_end = end; |
232 | 1.08k | } |
233 | | |
234 | 16 | fclose(f); |
235 | | |
236 | 16 | if (!found) { |
237 | 0 | return false; |
238 | 0 | } |
239 | | |
240 | 16 | error = getrlimit(RLIMIT_STACK, &rlim); |
241 | 16 | if (error || rlim.rlim_cur == RLIM_INFINITY) { |
242 | 0 | return false; |
243 | 0 | } |
244 | | |
245 | 16 | 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 | 16 | if (end - max_size < prev_end) { |
256 | 0 | max_size = prev_end - end; |
257 | 0 | } |
258 | | |
259 | 16 | stack->base = (void*)end; |
260 | 16 | stack->max_size = max_size; |
261 | | |
262 | 16 | return true; |
263 | 16 | } |
264 | | |
265 | | static bool zend_call_stack_get_linux(zend_call_stack *stack) |
266 | 16 | { |
267 | 16 | if (zend_call_stack_is_main_thread()) { |
268 | 16 | return zend_call_stack_get_linux_proc_maps(stack); |
269 | 16 | } |
270 | | |
271 | 0 | return zend_call_stack_get_linux_pthread(stack); |
272 | 16 | } |
273 | | #else /* __linux__ */ |
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__) |
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) |
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) */ |
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 | | |
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 | | |
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 |
373 | | static bool zend_call_stack_get_freebsd(zend_call_stack *stack) |
374 | 0 | { |
375 | 0 | return false; |
376 | 0 | } |
377 | | #endif /* __FreeBSD__ */ |
378 | | |
379 | | #ifdef ZEND_WIN32 |
380 | | static bool zend_call_stack_get_win32(zend_call_stack *stack) |
381 | | { |
382 | | ULONG_PTR low_limit, high_limit; |
383 | | MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0}; |
384 | | size_t result_size, page_size; |
385 | | |
386 | | /* The stack consists of three regions: committed, guard, and uncommitted. |
387 | | * Memory is committed when the guard region is accessed. If only one page |
388 | | * is left in the uncommitted region, a stack overflow error is raised |
389 | | * instead. |
390 | | * |
391 | | * The total useable stack size is the size of the committed and uncommitted |
392 | | * regions less one page. |
393 | | * |
394 | | * http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx |
395 | | * https://learn.microsoft.com/en-us/windows/win32/procthread/thread-stack-size |
396 | | * |
397 | | * ^ Higher addresses ^ |
398 | | * : : |
399 | | * : : |
400 | | * high_limit --> |--------------------| |
401 | | * ^ | | |
402 | | * | | Committed region | |
403 | | * | | | |
404 | | * | |------------------- | <-- guard_region.BaseAddress |
405 | | * reserved | | | + guard_region.RegionSize |
406 | | * size | | Guard region | |
407 | | * | | | |
408 | | * | |--------------------| <-- guard_region.BaseAddress, |
409 | | * | | | uncommitted_region.BaseAddress |
410 | | * | | Uncommitted region | + uncommitted_region.RegionSize |
411 | | * v | | |
412 | | * low_limit --> |------------------- | <-- uncommitted_region.BaseAddress |
413 | | * : : |
414 | | * : : |
415 | | * v Lower addresses v |
416 | | */ |
417 | | |
418 | | GetCurrentThreadStackLimits(&low_limit, &high_limit); |
419 | | |
420 | | result_size = VirtualQuery((void*)low_limit, |
421 | | &uncommitted_region, sizeof(uncommitted_region)); |
422 | | ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); |
423 | | |
424 | | result_size = VirtualQuery((int8_t*)uncommitted_region.BaseAddress + uncommitted_region.RegionSize, |
425 | | &guard_region, sizeof(guard_region)); |
426 | | ZEND_ASSERT(result_size >= sizeof(uncommitted_region)); |
427 | | |
428 | | stack->base = (void*)high_limit; |
429 | | stack->max_size = (uintptr_t)high_limit - (uintptr_t)low_limit; |
430 | | |
431 | | ZEND_ASSERT(stack->max_size > guard_region.RegionSize); |
432 | | stack->max_size -= guard_region.RegionSize; |
433 | | |
434 | | /* The uncommitted region does not shrink below 1 page */ |
435 | | page_size = zend_get_page_size(); |
436 | | ZEND_ASSERT(stack->max_size > page_size); |
437 | | stack->max_size -= page_size; |
438 | | |
439 | | return true; |
440 | | } |
441 | | #else /* ZEND_WIN32 */ |
442 | | static bool zend_call_stack_get_win32(zend_call_stack *stack) |
443 | 0 | { |
444 | 0 | return false; |
445 | 0 | } |
446 | | #endif /* ZEND_WIN32 */ |
447 | | |
448 | | #if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) |
449 | | static bool zend_call_stack_get_macos(zend_call_stack *stack) |
450 | | { |
451 | | void *base = pthread_get_stackaddr_np(pthread_self()); |
452 | | size_t max_size; |
453 | | |
454 | | #if !defined(__aarch64__) |
455 | | if (pthread_main_np()) |
456 | | { |
457 | | /* pthread_get_stacksize_np() returns a too low value for the main |
458 | | * thread in OSX 10.9, 10.10: |
459 | | * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html |
460 | | * https://github.com/rust-lang/rust/issues/43347 |
461 | | */ |
462 | | |
463 | | /* Stack size is 8MiB by default for main threads |
464 | | * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */ |
465 | | max_size = 8 * 1024 * 1024; |
466 | | } |
467 | | else |
468 | | #endif |
469 | | { |
470 | | max_size = pthread_get_stacksize_np(pthread_self()); |
471 | | } |
472 | | |
473 | | stack->base = base; |
474 | | stack->max_size = max_size; |
475 | | |
476 | | return true; |
477 | | } |
478 | | #else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ |
479 | | static bool zend_call_stack_get_macos(zend_call_stack *stack) |
480 | 0 | { |
481 | 0 | return false; |
482 | 0 | } |
483 | | #endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ |
484 | | |
485 | | #if defined(__OpenBSD__) |
486 | | #if defined(HAVE_PTHREAD_STACKSEG_NP) |
487 | | static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) |
488 | | { |
489 | | stack_t ss; |
490 | | |
491 | | if (pthread_stackseg_np(pthread_self(), &ss) != 0) { |
492 | | return false; |
493 | | } |
494 | | |
495 | | stack->base = (char *)ss.ss_sp - ss.ss_size; |
496 | | stack->max_size = ss.ss_size - sysconf(_SC_PAGE_SIZE); |
497 | | |
498 | | return true; |
499 | | } |
500 | | #else |
501 | | static bool zend_call_stack_get_openbsd_pthread(zend_call_stack *stack) |
502 | | { |
503 | | return false; |
504 | | } |
505 | | #endif /* defined(HAVE_PTHREAD_STACKSEG_NP) */ |
506 | | |
507 | | static bool zend_call_stack_get_openbsd_vm(zend_call_stack *stack) |
508 | | { |
509 | | struct _ps_strings ps; |
510 | | struct rlimit rlim; |
511 | | int mib[2] = {CTL_VM, VM_PSSTRINGS }; |
512 | | size_t len = sizeof(ps), pagesize; |
513 | | |
514 | | if (sysctl(mib, 2, &ps, &len, NULL, 0) != 0) { |
515 | | return false; |
516 | | } |
517 | | |
518 | | if (getrlimit(RLIMIT_STACK, &rlim) != 0) { |
519 | | return false; |
520 | | } |
521 | | |
522 | | if (rlim.rlim_cur == RLIM_INFINITY) { |
523 | | return false; |
524 | | } |
525 | | |
526 | | pagesize = sysconf(_SC_PAGE_SIZE); |
527 | | |
528 | | stack->base = (void *)((uintptr_t)ps.val + (pagesize - 1) & ~(pagesize - 1)); |
529 | | stack->max_size = rlim.rlim_cur - pagesize; |
530 | | |
531 | | return true; |
532 | | } |
533 | | |
534 | | static bool zend_call_stack_get_openbsd(zend_call_stack *stack) |
535 | | { |
536 | | // TIB_THREAD_INITIAL_STACK is private and here we avoid using pthread's api (ie pthread_main_np) |
537 | | if (!TIB_GET()->tib_thread || (TIB_GET()->tib_thread_flags & 0x002) != 0) { |
538 | | return zend_call_stack_get_openbsd_vm(stack); |
539 | | } |
540 | | |
541 | | return zend_call_stack_get_openbsd_pthread(stack); |
542 | | } |
543 | | |
544 | | #else |
545 | | static bool zend_call_stack_get_openbsd(zend_call_stack *stack) |
546 | 0 | { |
547 | 0 | return false; |
548 | 0 | } |
549 | | #endif /* defined(__OpenBSD__) */ |
550 | | #if defined(__HAIKU__) |
551 | | static bool zend_call_stack_get_haiku(zend_call_stack *stack) |
552 | | { |
553 | | thread_id id; |
554 | | thread_info ti; |
555 | | size_t guard_size; |
556 | | |
557 | | // unlikely, main thread ought to be always available but we never know |
558 | | if ((id = find_thread(NULL)) == B_NAME_NOT_FOUND || get_thread_info(id, &ti) != B_OK) { |
559 | | return false; |
560 | | } |
561 | | |
562 | | // USER_STACK_GUARD_SIZE |
563 | | guard_size = sysconf(_SC_PAGESIZE) * 4; |
564 | | |
565 | | stack->base = ti.stack_end; |
566 | | stack->max_size = ((size_t)ti.stack_end - (size_t)ti.stack_base) - guard_size; |
567 | | |
568 | | return true; |
569 | | } |
570 | | #else |
571 | | static bool zend_call_stack_get_haiku(zend_call_stack *stack) |
572 | 0 | { |
573 | 0 | return false; |
574 | 0 | } |
575 | | #endif /* defined(__HAIKU__) */ |
576 | | |
577 | | #if defined(__NetBSD__) |
578 | | # ifdef HAVE_PTHREAD_GETATTR_NP |
579 | | static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack) |
580 | | { |
581 | | pthread_attr_t attr; |
582 | | int error; |
583 | | void *addr; |
584 | | size_t max_size, guard_size; |
585 | | |
586 | | error = pthread_getattr_np(pthread_self(), &attr); |
587 | | if (error) { |
588 | | return false; |
589 | | } |
590 | | |
591 | | error = pthread_attr_getstack(&attr, &addr, &max_size); |
592 | | if (error) { |
593 | | return false; |
594 | | } |
595 | | |
596 | | error = pthread_attr_getguardsize(&attr, &guard_size); |
597 | | if (error) { |
598 | | return false; |
599 | | } |
600 | | |
601 | | addr = (char *)addr + guard_size; |
602 | | max_size -= guard_size; |
603 | | |
604 | | stack->base = (char *)addr + max_size; |
605 | | stack->max_size = max_size; |
606 | | |
607 | | return true; |
608 | | } |
609 | | # else |
610 | | static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack) |
611 | | { |
612 | | return false; |
613 | | } |
614 | | # endif /* HAVE_PTHREAD_GETATTR_NP */ |
615 | | static bool zend_call_stack_get_netbsd_vm(zend_call_stack *stack, void **ptr) |
616 | | { |
617 | | /** |
618 | | * NetBSD supports procfs in a similar fashion as Linux |
619 | | * however NetBSD's mid/long term plan is to remove it completely. |
620 | | */ |
621 | | char *start, *end; |
622 | | struct kinfo_vmentry *entry; |
623 | | size_t len, max_size; |
624 | | uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); |
625 | | int mib[5] = { CTL_VM, VM_PROC, VM_PROC_MAP, getpid(), sizeof(struct kinfo_vmentry) }; |
626 | | bool found = false; |
627 | | struct rlimit rlim; |
628 | | |
629 | | if (sysctl(mib, 5, NULL, &len, NULL, 0) != 0) { |
630 | | return false; |
631 | | } |
632 | | |
633 | | // kinfo_getvmmap uses the same formula, only we do not want to rely on libkvm |
634 | | len = len * 4 / 3 ; |
635 | | *ptr = malloc(len); |
636 | | |
637 | | if (sysctl(mib, 5, *ptr, &len, NULL, 0) != 0) { |
638 | | return false; |
639 | | } |
640 | | |
641 | | start = (char *)*ptr; |
642 | | end = start + len; |
643 | | |
644 | | while (start < end) { |
645 | | entry = (struct kinfo_vmentry *)start; |
646 | | if (entry->kve_start <= addr_on_stack && entry->kve_end >= addr_on_stack) { |
647 | | found = true; |
648 | | break; |
649 | | } |
650 | | |
651 | | start += sizeof(struct kinfo_vmentry); |
652 | | } |
653 | | |
654 | | if (!found) { |
655 | | return false; |
656 | | } |
657 | | |
658 | | if (getrlimit(RLIMIT_STACK, &rlim) || rlim.rlim_cur == RLIM_INFINITY) { |
659 | | return false; |
660 | | } |
661 | | |
662 | | max_size = rlim.rlim_cur; |
663 | | |
664 | | stack->base = (void *)entry->kve_end; |
665 | | stack->max_size = max_size; |
666 | | |
667 | | return true; |
668 | | } |
669 | | |
670 | | |
671 | | static bool zend_call_stack_get_netbsd(zend_call_stack *stack) |
672 | | { |
673 | | if (syscall(SYS__lwp_self) == 1) { |
674 | | void *ptr = NULL; |
675 | | bool r = zend_call_stack_get_netbsd_vm(stack, &ptr); |
676 | | free(ptr); |
677 | | return r; |
678 | | } |
679 | | |
680 | | return zend_call_stack_get_netbsd_pthread(stack); |
681 | | } |
682 | | #else |
683 | | static bool zend_call_stack_get_netbsd(zend_call_stack *stack) |
684 | 0 | { |
685 | 0 | return false; |
686 | 0 | } |
687 | | #endif /* defined(__NetBSD__) */ |
688 | | |
689 | | #if defined(__sun) |
690 | | static bool zend_call_stack_get_solaris_pthread(zend_call_stack *stack) |
691 | | { |
692 | | stack_t s; |
693 | | if (thr_stksegment(&s) < 0) { |
694 | | return false; |
695 | | } |
696 | | |
697 | | stack->max_size = s.ss_size; |
698 | | stack->base = s.ss_sp; |
699 | | return true; |
700 | | } |
701 | | |
702 | | #ifdef HAVE_LIBPROC_H |
703 | | static bool zend_call_stack_get_solaris_proc_maps(zend_call_stack *stack) |
704 | | { |
705 | | uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); |
706 | | bool found = false, r = false; |
707 | | struct ps_prochandle *proc; |
708 | | prmap_t *map, *orig; |
709 | | struct rlimit rlim; |
710 | | char path[PATH_MAX]; |
711 | | size_t size; |
712 | | ssize_t len = -1; |
713 | | pid_t pid; |
714 | | int error, fd; |
715 | | |
716 | | pid = getpid(); |
717 | | proc = Pgrab(pid, PGRAB_RDONLY, &error); |
718 | | if (!proc) { |
719 | | return false; |
720 | | } |
721 | | |
722 | | size = (1 << 20); |
723 | | snprintf(path, sizeof(path), "/proc/%d/map", (int)pid); |
724 | | |
725 | | if ((fd = open(path, O_RDONLY)) == -1) { |
726 | | Prelease(proc, 0); |
727 | | return false; |
728 | | } |
729 | | |
730 | | orig = malloc(size); |
731 | | if (!orig) { |
732 | | Prelease(proc, 0); |
733 | | close(fd); |
734 | | return false; |
735 | | } |
736 | | |
737 | | while (size > 0 && (len = pread(fd, orig, size, 0)) == size) { |
738 | | prmap_t *tmp; |
739 | | size <<= 1; |
740 | | tmp = realloc(orig, size); |
741 | | if (!tmp) { |
742 | | goto end; |
743 | | } |
744 | | orig = tmp; |
745 | | } |
746 | | |
747 | | for (map = orig; len > 0; ++map) { |
748 | | if ((uintptr_t)map->pr_vaddr <= addr_on_stack && (uintptr_t)map->pr_vaddr + map->pr_size >= addr_on_stack) { |
749 | | found = true; |
750 | | break; |
751 | | } |
752 | | len -= sizeof(*map); |
753 | | } |
754 | | |
755 | | if (!found) { |
756 | | goto end; |
757 | | } |
758 | | |
759 | | error = getrlimit(RLIMIT_STACK, &rlim); |
760 | | if (error || rlim.rlim_cur == RLIM_INFINITY) { |
761 | | goto end; |
762 | | } |
763 | | |
764 | | stack->base = (void *)map->pr_vaddr + map->pr_size; |
765 | | stack->max_size = rlim.rlim_cur; |
766 | | r = true; |
767 | | |
768 | | end: |
769 | | free(orig); |
770 | | Prelease(proc, 0); |
771 | | close(fd); |
772 | | return r; |
773 | | } |
774 | | #endif |
775 | | |
776 | | static bool zend_call_stack_get_solaris(zend_call_stack *stack) |
777 | | { |
778 | | #ifdef HAVE_LIBPROC_H |
779 | | if (_lwp_self() == 1) { |
780 | | return zend_call_stack_get_solaris_proc_maps(stack); |
781 | | } |
782 | | #endif |
783 | | return zend_call_stack_get_solaris_pthread(stack); |
784 | | } |
785 | | #else |
786 | | static bool zend_call_stack_get_solaris(zend_call_stack *stack) |
787 | 0 | { |
788 | 0 | return false; |
789 | 0 | } |
790 | | #endif /* defined(__sun) */ |
791 | | |
792 | | /** Get the stack information for the calling thread */ |
793 | | ZEND_API bool zend_call_stack_get(zend_call_stack *stack) |
794 | 16 | { |
795 | 16 | if (zend_call_stack_get_linux(stack)) { |
796 | 16 | return true; |
797 | 16 | } |
798 | | |
799 | 0 | if (zend_call_stack_get_freebsd(stack)) { |
800 | 0 | return true; |
801 | 0 | } |
802 | | |
803 | 0 | if (zend_call_stack_get_win32(stack)) { |
804 | 0 | return true; |
805 | 0 | } |
806 | | |
807 | 0 | if (zend_call_stack_get_macos(stack)) { |
808 | 0 | return true; |
809 | 0 | } |
810 | | |
811 | 0 | if (zend_call_stack_get_openbsd(stack)) { |
812 | 0 | return true; |
813 | 0 | } |
814 | | |
815 | 0 | if (zend_call_stack_get_netbsd(stack)) { |
816 | 0 | return true; |
817 | 0 | } |
818 | | |
819 | 0 | if (zend_call_stack_get_haiku(stack)) { |
820 | 0 | return true; |
821 | 0 | } |
822 | | |
823 | 0 | if (zend_call_stack_get_solaris(stack)) { |
824 | 0 | return true; |
825 | 0 | } |
826 | | |
827 | 0 | return false; |
828 | 0 | } |
829 | | |
830 | | #endif /* ZEND_CHECK_STACK_LIMIT */ |