Coverage Report

Created: 2026-06-02 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */