setcontext
is a C library function that along with getcontext
allows you to perform a non-local jump from one context to another. They are often used when implementing coroutines or custom threading libraries. longjmp
and setjmp
provide similar functionality but setcontext
was an attempt to fix the shortcomings of these functions and standardize behaviour, although in POSIX 2008 the specification of setcontext
and related functions were removed due to the difficulty of implementing them in a truly portable manner.
In the beginning there was setjmp
and longjmp
. setjmp
would capture the current register values into a data structure and longjmp
would restore those values at a later point in program execution, causing the control flow to jump back to the point where setjmp
was called. This works fine unless the place where you are jumping from is a signal handler. In this case the problem you have is that setjmp
will not restore the signal mask so the signal you were handling will not be unmasked. To fix this functions that saved and restored the signal mask called sigsetjmp
and siglongjmp
were introduced.
However, this doesn’t mean it is necessarily safe to jump out of a signal handler even if you are using siglongjmp
. The problem you will often hit is that if the signal was delivered at an arbitrary point in program execution there may be locks or other global resource that need to be deallocated. The only way to avoid this is to block the appropriate signal across any part of the code that may not behave well when interrupted in this way. Unfortunately without auditing all third party libraries that probably means you can only enable handling of such a signal across very small regions of your program.
setcontext
and getcontext
also restore and save the signal mask and setcontext
can also be used to exit from a signal handler with the caveats expressed above. However there is another way in which signal handling and setcontext
interact. The context structure used by setcontext
and getcontext
is of type ucontext_t
. If you installed your signal handler using sigaction
and the sa_handler
field of struct sigaction
you get a third argument to the signal handler which is of type void *
but allows casting to ucontext_t *
. So what happens if you pass this structure to setcontext?
Well the answer is, on Linux at least, “probably nothing good”. The original definition of setcontext
specified that “program execution continues with the program instruction following the instruction interrupted by the signal”, but more recent versions of the standards removed that requirement so the result is now unspecified. Some glibc ports such as powerpc, mips and tile do support restoring signal handler created contexts in the spirit of the original specification, but the rest, including x86 and ARM do not. As such it is not possible to rely on being able to restore a signal handler created context with setcontext
on Linux. It would be interesting to know if any proprietary Unixes support restoring these contexts and if any applications actually use the functionality.
Linux kernel: 3.11.10 x86_64
glibc: 2.18
Given the following signal handler, setcontext() initially does not provoke a segment fault.
But, after handling 22 additional signals, of which only 2 left the signal handler via setcontext(), a segment fault occurred.
static void signalHandler(int signalNumber, siginfo_t *signalInfo_ptr, void *userContext_void_ptr)
{
.
.
.
if (SI_USER == signalInfo_ptr->si_code || SIGCHLD == signalNumber)
{
if (getpid() != signalInfo_ptr->si_pid) setcontext((ucontext_t *) userContext_void_ptr);
}
return;
}
LikeLike