Coverage Report

Created: 2026-10-28 06:20

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
    defined(_AIX)
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
692
  while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) {
227
692
    if (start <= addr_on_stack && end >= addr_on_stack) {
228
16
      found = true;
229
16
      break;
230
16
    }
231
676
    prev_end = end;
232
676
  }
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 usable */
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 usable 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
    pthread_attr_destroy(&attr);
594
    return false;
595
  }
596
597
  error = pthread_attr_getguardsize(&attr, &guard_size);
598
  if (error) {
599
    pthread_attr_destroy(&attr);
600
    return false;
601
  }
602
603
  addr = (char *)addr + guard_size;
604
  max_size -= guard_size;
605
606
  stack->base = (char *)addr + max_size;
607
  stack->max_size = max_size;
608
609
  pthread_attr_destroy(&attr);
610
  return true;
611
}
612
# else
613
static bool zend_call_stack_get_netbsd_pthread(zend_call_stack *stack)
614
{
615
  return false;
616
}
617
# endif /* HAVE_PTHREAD_GETATTR_NP */
618
static bool zend_call_stack_get_netbsd_vm(zend_call_stack *stack, void **ptr)
619
{
620
  /**
621
   * NetBSD supports procfs in a similar fashion as Linux
622
   * however NetBSD's mid/long term plan is to remove it completely.
623
   */
624
  char *start, *end;
625
  struct kinfo_vmentry *entry;
626
  size_t len, max_size;
627
  uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
628
  int mib[5] = { CTL_VM, VM_PROC, VM_PROC_MAP, getpid(), sizeof(struct kinfo_vmentry) };
629
  bool found = false;
630
  struct rlimit rlim;
631
632
  if (sysctl(mib, 5, NULL, &len, NULL, 0) != 0) {
633
    return false;
634
  }
635
636
  // kinfo_getvmmap uses the same formula, only we do not want to rely on libkvm
637
  len = len * 4 / 3 ;
638
  *ptr = malloc(len);
639
640
  if (sysctl(mib, 5, *ptr, &len, NULL, 0) != 0) {
641
    return false;
642
  }
643
644
  start = (char *)*ptr;
645
  end = start + len;
646
647
  while (start < end) {
648
    entry = (struct kinfo_vmentry *)start;
649
    if (entry->kve_start <= addr_on_stack && entry->kve_end >= addr_on_stack) {
650
      found = true;
651
      break;
652
    }
653
654
    start += sizeof(struct kinfo_vmentry);
655
  }
656
657
  if (!found) {
658
    return false;
659
  }
660
661
  if (getrlimit(RLIMIT_STACK, &rlim) || rlim.rlim_cur == RLIM_INFINITY) {
662
    return false;
663
  }
664
665
  max_size = rlim.rlim_cur;
666
667
  stack->base = (void *)entry->kve_end;
668
  stack->max_size = max_size;
669
670
  return true;
671
}
672
673
674
static bool zend_call_stack_get_netbsd(zend_call_stack *stack)
675
{
676
  if (syscall(SYS__lwp_self) == 1) {
677
    void *ptr = NULL;
678
    bool r = zend_call_stack_get_netbsd_vm(stack, &ptr);
679
    free(ptr);
680
    return r;
681
  }
682
683
  return zend_call_stack_get_netbsd_pthread(stack);
684
}
685
#else
686
static bool zend_call_stack_get_netbsd(zend_call_stack *stack)
687
0
{
688
0
  return false;
689
0
}
690
#endif /* defined(__NetBSD__) */
691
692
#if defined(__sun)
693
static bool zend_call_stack_get_solaris_pthread(zend_call_stack *stack)
694
{
695
  stack_t s;
696
  if (thr_stksegment(&s) < 0) {
697
    return false;
698
  }
699
700
  stack->max_size = s.ss_size;
701
  stack->base = s.ss_sp;
702
  return true;
703
}
704
705
#ifdef HAVE_LIBPROC_H
706
static bool zend_call_stack_get_solaris_proc_maps(zend_call_stack *stack)
707
{
708
  uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position();
709
  bool found = false, r = false;
710
  struct ps_prochandle *proc;
711
  prmap_t *map, *orig;
712
  struct rlimit rlim;
713
  char path[PATH_MAX];
714
  size_t size;
715
  ssize_t len = -1;
716
  pid_t pid;
717
  int error, fd;
718
719
  pid = getpid();
720
  proc = Pgrab(pid, PGRAB_RDONLY, &error);
721
  if (!proc) {
722
    return false;
723
  }
724
725
  size = (1 << 20);
726
  snprintf(path, sizeof(path), "/proc/%d/map", (int)pid);
727
728
  if ((fd = open(path, O_RDONLY)) == -1) {
729
    Prelease(proc, 0);
730
    return false;
731
  }
732
733
  orig = malloc(size);
734
  if (!orig) {
735
    Prelease(proc, 0);
736
    close(fd);
737
    return false;
738
  }
739
740
  while (size > 0 && (len = pread(fd, orig, size, 0)) == size) {
741
    prmap_t *tmp;
742
    size <<= 1;
743
    tmp = realloc(orig, size);
744
    if (!tmp) {
745
      goto end;
746
    }
747
    orig = tmp;
748
  }
749
750
  for (map = orig; len > 0; ++map) {
751
    if ((uintptr_t)map->pr_vaddr <= addr_on_stack && (uintptr_t)map->pr_vaddr + map->pr_size >= addr_on_stack) {
752
      found = true;
753
      break;
754
    }
755
    len -= sizeof(*map);
756
  }
757
758
  if (!found) {
759
    goto end;
760
  }
761
762
  error = getrlimit(RLIMIT_STACK, &rlim);
763
  if (error || rlim.rlim_cur == RLIM_INFINITY) {
764
    goto end;
765
  }
766
767
  stack->base = (void *)map->pr_vaddr + map->pr_size;
768
  stack->max_size = rlim.rlim_cur;
769
  r = true;
770
771
end:
772
  free(orig);
773
  Prelease(proc, 0);
774
  close(fd);
775
  return r;
776
}
777
#endif
778
779
static bool zend_call_stack_get_solaris(zend_call_stack *stack)
780
{
781
#ifdef HAVE_LIBPROC_H
782
  if (_lwp_self() == 1) {
783
    return zend_call_stack_get_solaris_proc_maps(stack);
784
  }
785
#endif
786
  return zend_call_stack_get_solaris_pthread(stack);
787
}
788
#else
789
static bool zend_call_stack_get_solaris(zend_call_stack *stack)
790
0
{
791
0
  return false;
792
0
}
793
#endif /* defined(__sun) */
794
795
#if defined(_AIX)
796
static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack)
797
{
798
#ifdef HAVE_PTHREAD_GETTHRDS_NP
799
  pthread_t pt = pthread_self();
800
  struct __pthrdsinfo thread_info = {0};
801
  /*
802
   * We don't need the register buffer since we only call the function
803
   * on our own thread, and since the register buffer is only used for
804
   * suspended threads...
805
   */
806
  int regsz = 0;
807
808
  if (pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_ALL, &thread_info,
809
        sizeof(thread_info), NULL, &regsz)) {
810
    return false;
811
  }
812
813
  /*
814
   * These can be null in rare situations, allegedly with user-provided
815
   * stacks with pthread (according to OpenJDK)
816
   */
817
  if (!(thread_info.__pi_stackend && thread_info.__pi_stackaddr)) {
818
    return false;
819
  }
820
821
  /*
822
   * The top of the stack (stackend) is not page aligned, there's some
823
   * internal stuff above it. Thankfully, we don't need page alignment.
824
   *
825
   * The size is a little weird. The stacksize field for child threads
826
   * is smaller than subtracting the bottom (stackaddr) from the top;
827
   * it's about 0x888 to 0x1888 above stackaddr. I'm assuming it rounds
828
   * the bottom of the stack to page alignment? The main thread size is
829
   * the same as end - addr, but it is variable between systems; also
830
   * assuming there's stuff at the top of the stack that gets taken off,
831
   * regardless of maximum declared size.
832
   *
833
   * A somewhat crude diagram is available here:
834
   * https://www.ibm.com/docs/en/aix/7.2.0?topic=tuning-thread-environment-variables
835
   *
836
   * pthread->pt_stk.st_limit is __pi_stackend,
837
   * above that is internal pthread junk close to the end of page
838
   * pthread->pt_stk.st_base is __pi_stackaddr,
839
   * below that is the red zone
840
   */
841
  stack->base = thread_info.__pi_stackend;
842
  stack->max_size = thread_info.__pi_stackend - thread_info.__pi_stackaddr;
843
  return true;
844
#else
845
  /* pthread likely not linked in; default NTS build behaviour */
846
  return false;
847
#endif
848
}
849
850
static bool zend_call_stack_get_aix(zend_call_stack *stack)
851
{
852
  /*
853
   * While we could use /proc on AIX (and the implementation basically
854
   * like the Solaris one, as the procfs is similar), it doesn't work on
855
   * PASE. The pthread API works even on the main thread, so we should
856
   * use it when we have pthread linked (always with ZTS, maybe not with
857
   * NTS builds).
858
   */
859
  return zend_call_stack_get_aix_pthread(stack);
860
}
861
#else
862
static bool zend_call_stack_get_aix(zend_call_stack *stack)
863
0
{
864
0
  return false;
865
0
}
866
#endif /* defined(_AIX) */
867
868
/** Get the stack information for the calling thread */
869
ZEND_API bool zend_call_stack_get(zend_call_stack *stack)
870
16
{
871
16
  if (zend_call_stack_get_linux(stack)) {
872
16
    return true;
873
16
  }
874
875
0
  if (zend_call_stack_get_freebsd(stack)) {
876
0
    return true;
877
0
  }
878
879
0
  if (zend_call_stack_get_win32(stack)) {
880
0
    return true;
881
0
  }
882
883
0
  if (zend_call_stack_get_macos(stack)) {
884
0
    return true;
885
0
  }
886
887
0
  if (zend_call_stack_get_openbsd(stack)) {
888
0
    return true;
889
0
  }
890
891
0
  if (zend_call_stack_get_netbsd(stack)) {
892
0
    return true;
893
0
  }
894
895
0
  if (zend_call_stack_get_haiku(stack)) {
896
0
    return true;
897
0
  }
898
899
0
  if (zend_call_stack_get_solaris(stack)) {
900
0
    return true;
901
0
  }
902
903
0
  if (zend_call_stack_get_aix(stack)) {
904
0
    return true;
905
0
  }
906
907
0
  return false;
908
0
}
909
910
#endif /* ZEND_CHECK_STACK_LIMIT */