As brucehoult said, it's impossible to catch all "stack overflows" in general, as stack access, on most architectures, is just a normal memory operation.
Now, a commonly used approach is to set up "stack guards" - small buffers put between the stack and the heap, and then write protect those guards, either with an MPU if available, or watching for modifications in the guard buffer, initialized with sentinel values, on a regular basis, if not. This of course won't catch all issues, as Bruce explained. But it's better than nothing.
As newbrain mentioned, some MCUs have stack limit registers. Recent ARM Cortex-M cores, but also some automotive MCUs from NXP, IIRC. Of course, for this scheme to be effective, that implies that any running code comply with the official ABI, which in turn implies using the dedicated stack pointer register(s), and decrement them accordingly every time the stack "grows". This is usually the case when using conforming compilers, such as GCC or LLVM. Of course, it still won't catch some overflows - as Bruce again explained. But it will at least catch attempts to grow the stack beyond its limits, when the running code otherwise doesn't contain any buggy overflow through memory access.