The alternative signal stack is allocated on the head and registered using sigaltstack(&ss,NULL). Also, the SA_ONSTACK flag is set in the sigaction struct to enable the alternative stack use for this specific action.

This basically resolves my problem, because now we see an endless stream of SIGSEGVs being caught. After all, the above catch_function() doesn't do much to fix the invalid stack pointer. As a solution, I now store the valid stack pointer for the main() in sbase and use that to restore it in the handler if it's invalid (through manipulation of the saved thread context).

To make all of this work, I also fixed my inline assembly to not just push a value but also pop it back afterwards, so the stack height remains unchanged. For the sake of replicability, I also included the includes this time.