Reasons to limit your stack size even in non-threaded environments

October 12, 2021

One reaction to learning that 4BSD is where Unix started to have a stack size limit is to ask why you would bother with a stack size limit at all in an environment without threads (where a process will thus only ever have one stack). There are a number of reasons that operating systems have generally done this, and probably why it starts in Unix in the 4BSD line, which ran on 32-bit VAX systems instead of the 16-bit PDP-11s that V7 did.

In a modern operating system with the ability to map things into your process's virtual memory, one reason to limit the size of the process's main stack is to create room for these mappings in your virtual memory address space. Potential future mappings have to go somewhere and that means address space has to be left open for them, which requires limiting the address space that's reserved for the stack (even if it's a very large limit, as it could be on 64-bit systems). Since it's more common to have large or gigantic mappings than it is to have a large or gigantic stack, leaving most of the space for mappings makes sense (by default, at least).

(In an environment with threads, thread stacks take up some of this virtual address space, and similarly need their own limits.)

But 4BSD was an operating system without threads that had no mmap() to map memory into your process's address space. All there was in your process's memory address space that could grow was the heap and the stack (and with only two things, they could just be let grow toward each other until they met). Yet 4BSD still found it useful to add support for limiting the stack size.

The simple reason to limit stack size is that otherwise, a program with an accidental infinite recursion (or in general a huge stack space usage) can easily exhaust all of your RAM. Stack space is special for two reasons. First, it's easy to accidentally use a lot of it through means like deep recursion. Second, when you do use stack space, it's almost always written to (even if just for function return addresses and saved registers), so it has to really be there (either in RAM or in swap space). The combination makes recursion a great way to allocate and dirty a lot of RAM quite fast.

When a single process's virtual address space that would theoretically be available for its stack is even close to the amount of RAM in your entire machine, much less greater than it (as it was for 32-bit machines for a long time), it's suddenly quite sensible to limit the stack space usage. Otherwise you're one easy accident away from being entirely out of RAM.

Written on 12 October 2021.
« Unknown NMIs and counting hardware CPU events in eBPF programs
Web browsers drive what Certificate Authority root certificates are accepted »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Tue Oct 12 23:51:03 2021
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.