The "SafeStack" [1] scheme is probably the most well-known that does that. The "safe" stack is the normal stack, and the "unsafe" stack contains all automatic variables that could get dereferenced - including arrays that could overflow.
The security of the scheme depends on keeping the location of the safe stack secret, which for one thing means that all code in the program needs to use the SafeStack ABI.
It is used more or less in several BSD Unices (most notably HardenedBSD), and it is the default in Fuchsia (except when the program was written in Go).
CPU architectures that have register windows [2] tend to spill shifted-out registers onto a separate stack lazily, and those registers could contain the return pointer.
[1] <https://clang.llvm.org/docs/SafeStack.html>
[2] <https://en.wikipedia.org/wiki/Register_window>