Coverage Report

Created: 2025-06-13 06:43

/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 */