Throw stack trace from program kill

H. S. Teoh hsteoh at quickfur.ath.cx
Mon Jan 17 02:18:32 UTC 2022


On Sun, Jan 16, 2022 at 01:42:21PM -0500, Steven Schveighoffer via Digitalmars-d-learn wrote:
> On 1/16/22 1:33 PM, Paul Backus wrote:
[...]
> > ```d
> > extern(C) void handleCtrlC(int)
> > {
> >      import core.stdc.stdlib: exit;
> >      import std.stdio: writeln;
> > 
> >      try throw new Exception("Killed by CTRL+C");
> >      catch (Exception e)
> >      {
> >          writeln(e.message);
> >          writeln(e.info);
> >          exit(1);
> >      }
> > }
> > ```
> 
> This too is not going to be a good idea. writeln(e.info) is going to
> possibly start allocating. A signal can come at any time, even when
> locks are held or things are in an intermediate state.

Yeah, this is generally a bad idea.  Code registered as a signal handler
will get called in what POSIX calls "signal handler context", where
you're not allowed to call a lot of C library functions because the
signal handler could be invoked while inside a non-reentrant library
function or syscall.  While this may sometimes work, it may crash
horribly when the signal happens to arrive at the wrong time, or
deadlock.

Many POSIX functions are marked as unsafe to call from signal handler
context, and this in particular includes anything that may allocate
memory (bad things will happen if you try to call, e.g., malloc from a
signal handler while the code happens to be inside another malloc call
-- it will likely deadlock on the malloc internal mutex).

Generally, a signal handler should do the absolute minimum to inform the
main program that the signal was received, and then quickly return back
to the interrupted code and let the main program react to the signal
later at a more convenient time.  The usual idiom is to write a single
byte (or a small number of bytes) to a pipe that's read by the program's
main loop. The write syscall is one of the few syscalls that are
signal-handler safe, and is a convenient way to flag the receipt of the
signal without needing to worry about thread synchronization issues (sin
ce the OS takes care of that inside the write() syscall).


> I use Adam's approach normally -- set a flag and act on it later.
[...]

Yes, this is generally the recommended way of dealing with a signal.

Though I'd add, as an interesting footnote, that sometimes you *can* do
trickier things inside a signal handler. One example is something we
came up with once, a hack to convert a SEGV into a D exception. The way
it works is by the signal handler take advantage of a piece of
information that the SEGV signal provides to it, which is the EIP value
of the code that caused the segfault, and the location in the code that
the signal handler would return to when it exits.  The signal handler
uses what amounts to a stack overflow exploit by overwriting this return
address to point instead to a function that allocates and throws a
SegfaultException.  Once the signal handler returns, any syscalls the
code may have been in will finish running, then return. Now we're
officially outside signal handler context, so it's safe to now allocate
and throw the exception.

Of course, the special function also needs to reconstruct a proper
stack frame so that the stack unwinding of the thrown exception won't go
haywire.  The gory details are here:

	https://forum.dlang.org/thread/jjn6dj$193c$1@digitalmars.com

Obviously, this is extremely tricky and system-dependent stuff that has
to be done with extreme care.  NOT recommended if you don't know exactly
what you're doing!


T

-- 
By understanding a machine-oriented language, the programmer will tend to use a much more efficient method; it is much closer to reality. -- D. Knuth


More information about the Digitalmars-d-learn mailing list