malloc
will return NULL when the allocation fails. Usually this is due to lack of memory. In a free-running model, where heap and stack don't have limits, it would always give you memory (even if that meant colliding with the stack).
Fortunately, that is never done. Usually the heap and the stack have fixed maximum sizes, so checks are easier. For example, if the library malloc
calls your sbrk
(typical situation), you could write sbrk
so that it refuses to extend the heap past the stack limit. This avoids the heap from colliding into the stack.
The other way around (stack collides into heap) is trickier. First off, there isn't much you can do to recover from a stack overflow, so the strategy here would be to produce useful debug info and halt the system to avoid working on corrupted memory. Technically, the compiler could add checks for every stack allocation, but that would noticeably slow down the code (I don't even know if any compiler supports it - probably yes, with some instrumentation). If your MCU has a Memory Protection Unit (MPU), you could configure a small unaccessible zone above the stack limit, so that stack overflows will generate an MPU fault. This technique is often called guard page (much like guard bytes with a memory breakpoint, if you want). For this to work, however, exceptions need to use a different stack (otherwise you're in the same boat). Note that stack canaries don't protect against stack-heap collision.
That being said, in bare metal you are the one designing the memory layout, and you can set it up however you want depending on your needs. malloc
is generally frowned upon because it's non-deterministic. Also, you should profile your stack usage and know your maximum stack depth in order to properly size the stack.