Updated: 2022/Sep/29

Please read Privacy Policy. It's for your privacy.


STACK(7)               Miscellaneous Information Manual               STACK(7)

NAME
     stack - layout of program execution stack memory

DESCRIPTION
     When executing a program, with the execve(2) or posix_spawn(3) families
     of system calls, NetBSD reserves a region in the new program image's
     virtual address space for the stack, which stores return addresses and
     local variables for nested procedure calls in program execution.
     Similarly, threads created with pthread_create(3) have regions allocated
     for per-thread stacks.

     The stack grows from the base, where information of the outermost
     procedure call is stored, fixed at program start, to the stack pointer, a
     CPU register that points to information used by the current procedure
     call, varying during execution as procedures are called.

     On most architectures, the stack base is at higher-numbered virtual
     addresses and the stack pointer is at lower-numbered virtual addresses --
     on these architectures, the stack grows down.  On some other
     architectures, notably HP PA-RISC (`hppa'), the stack base is at lower-
     numbered virtual addresses and the stack pointer is at higher-numbered
     virtual addresses, so on those architectures the stack grows up.

     In the kernel, the C preprocessor macro __HAVE_MACHINE_STACK_GROWS_UP is
     defined in <machine/types.h> on architectures where the stack grows up.

   Main thread
     For single-threaded programs, and for the main thread of multi-threaded
     programs, NetBSD reserves virtual addresses as follows on architectures
     where the stack grows down:

     +--------------------+ USRSTACK
     | stack gap          |
     +--------------------+ stack base
     | accessible pages   |
     | .                  |
     | .                  | <-- stack pointer (varies during execution)
     | .                  |
     +--------------------+ (stack base) - (soft stack rlimit)
     | inaccessible pages |
     +--------------------+ (stack base) - (hard stack rlimit)
     | guard/redzone      |
     +--------------------+ USRSTACK - MAXSSIZ

     On architectures where the stack grows up, the layout is:

     +--------------------+ USRSTACK + MAXSSIZ
     | guard/redzone      |
     +--------------------+ (stack base) + (hard stack rlimit)
     | inaccessible pages |
     +--------------------+ (stack base) + (soft stack rlimit)
     | .                  |
     | .                  | <-- stack pointer (varies during execution)
     | .                  |
     | accessible pages   |
     +--------------------+ stack base
     | stack gap          |
     +--------------------+ USRSTACK

        The stack guard is allocated so that any access -- read, write, or
         execute -- will deliver SIGSEGV to the process.  This serves to
         detect stack overflow and crash rather than silently overwrite other
         memory in the program's virtual address space.  The size of the stack
         guard is tuned by the vm.guard_size sysctl(7) knob.

         The stack guard is also sometimes known as the `redzone' or `red
         zone', although the term `red zone' is also sometimes used to mean a
         fixed space above the stack pointer (in the direction of stack
         growth) that the system guarantees it will not overwrite when calling
         a signal handler in the ABI of some architectures; see also
         sigaltstack(2) to specify an alternate stack base for the kernel to
         use when invoking signal handlers on signal delivery.

        The inaccessible pages of the stack region are allocated so that any
         access will also deliver SIGSEGV to the process, but they can be made
         accessible by changing the soft stack rlimit with setrlimit(2).

        The accessible pages of the stack region are allocated with
         read/write access permitted, and are used to store the actual data in
         the program stack.

        When PaX ASLR, address space layout randomization, is enabled, the
         stack gap is an unallocated space of a size chosen unpredictably at
         random at program startup time.  When PaX ASLR is disabled, the stack
         gap is empty.

     All of the boundaries -- USRSTACK, the stack base, and the boundaries
     between the accessible, inaccessible, and guard pages -- are page-
     aligned, or rounded to be page-aligned even if the rlimits are not
     themselves page-aligned, rounding so that the sizes of the regions do not
     exceed the rlimits.

     The stack base is exposed to programs via the AT_STACKBASE elf(5)
     auxiliary info vector entry.

     The per-architecture constants USRSTACK and MAXSSIZ are defined in
     <machine/vmparam.h>.

   Non-main threads
     Threads created with pthread_create(3) have stacks allocated at
     dynamically chosen addresses outside the main thread's stack region by
     default, and their stacks cannot be resized after creation.  On
     architectures where the stack grows down, the layout is:

     +--------------------+ stack base = stackaddr + stacksize + guardsize
     | stack              |
     | .                  |
     | .                  | <-- stack pointer (varies during execution)
     | .                  |
     +--------------------+ stackaddr
     | guard/redzone      |
     +--------------------+ stackaddr - guardsize

     On architectures where the stack grows up, the layout is:

     +--------------------+ stackaddr + stacksize + guardsize
     | guard/redzone      |
     +--------------------+ stackaddr + stacksize
     | .                  |
     | .                  | <-- stack pointer (varies during execution)
     | .                  |
     | stack              |
     +--------------------+ stack base = stackaddr

     The parameters stackaddr, stacksize, and guardsize can be obtained from
     an existing thread using pthread_getattr_np(3),
     pthread_attr_getguardsize(3), and the pthread_attr_getstack(3) family of
     functions.

     When creating a thread, the stack can be manually allocated and the
     parameters can be set using pthread_attr_setguardsize(3) and the
     pthread_attr_setstack(3) family of functions.  However, the stack
     parameters cannot be changed after thread creation.  The default guard
     size is tuned by the vm.thread_guard_size sysctl(7) knob.

     For the main thread, pthread_getattr_np(3) returns a snapshot of the
     parameters as they existed at program startup, so that stackaddr and
     stacksize reflect the current accessible pages of the stack, and
     guardsize is the value of the vm.guard_size sysctl(7) knob at the time of
     program startup.  (Note that this means the pthread(3) view of the main
     thread's stack guard may not coincide with the actual stack guard -- it
     may overlap with, or lie entirely in, the inaccessible pages of the stack
     reserved on program start.) However, if the program changes its soft
     stack rlimit with setrlimit(2), this snapshot may become stale.

SEE ALSO
     execve(2), mmap(2), mprotect(2), sigaltstack(2), ucontext(2),
     posix_spawn(3), pthread(3), security(7), sysctl(7), paxctl(8)

BUGS
     PaX ASLR doesn't actually guarantee an accessible stack reservation of
     length equal to the soft stack rlimit -- owing to a bug (XXX which PR
     number?), NetBSD may sometimes reserve less space than the soft rlimit,
     in which case the accessible pages of the stack cannot be extended.

     There is a race between the kernel's access of vm.guard_size at exec
     time, and userland's access of vm.guard_size in pthread(3)
     initialization.

NetBSD 10.99                   November 23, 2023                  NetBSD 10.99