The previous implementation mapped a stack below the shared page and
inserted a randomly sized gap between PS_STRINGS and other process
metadata, below which the main stack stack begins. This has a couple of
- The size of the gap is included in the stack limit, which means that small RLIMIT_STACK values cannot be used.
- The gap is mapped, and thus mlockall(MCL_CURRENT) will wire it.
Fix the problem by moving the gap before PS_STRINGS. This means that
the gap region is unmapped and in particular is not covered by the stack
mapping. Thus RLIMIT_STACK no longer includes the size of the gap.
As a consequence, the location of PS_STRINGS is no longer fixed. If no
stack gap is configured then it is located in the same place as before.
Very old binaries depend on the location being fixed, but they predate
the introduction of any surviving 64-bit platform ports. Thus,
re-enable the stack gap only on 64-bit platforms (ASLR is not enabled
for elf32 by default anyway) and disallow enabling it for 32-bit
executables if COMPAT_43 is configured.
Since the PS_STRINGS location is now variable, add a field to struct
proc to store it. This field is copied on fork and initialized by image
Remove the newly KERN_STACKTOP sysctl and modify KERN_USRSTACK to return
the true top of the stack. This effectively reverts commits
78df56ccfcb4 ("libthr: Use kern.stacktop for thread stack calculation.")
a97d697122da ("kern_exec: Add kern.stacktop sysctl.")
The KERN_STACKTOP sysctl does not provide compatibility for old copies
of libthr.so in any case.
Modify setrlimit() to stop including the stack gap in RLIMIT_STACK, it
is no longer necessary to do so. Remove the vm_stkgap field from struct
vmspace, as it is no longer needed.
At this point we could go further and randomize more of the stack
address bits, but I didn't attempt it.