ptrace and SIGNAL_UNKILLABLE

From: Keno Fischer
Date: Sat Apr 11 2020 - 03:14:22 EST


Hi folks,

I was seeing some unexpected hangs when ptracing
a buggy container init, that I didn't see when just running
it regularly. Some investigation pointed me to commit

[eb61b5911] signal: don't remove SIGNAL_UNKILLABLE for traced tasks

In particular, consider a container init that just segfaults:

segv.c:
int main(void) {
return *((int*)-1);
}

$ gcc -o segv segv.c
unshare -p -U -r -f ./segv
Segmentation fault
$ strace -f unshare -p -U -r -f ./segv
[hangs]

The reason this hangs is that above commit prevents ptrace'd
tasks from having their SIGNAL_UNKILLABLE flag removed,
so when the SIGSEGV gets generated, that flag does not get
unset. When strace re-injects the SIGSEGV, it simply gets
ignored, the process re-enters user space and the whole
ordeal starts over.

I considered sending a patch that updates that code-path
to only preserve SIGNAL_UNKILLABLE for SIGTRAP
signals, but I figured that may yet be insufficient as well.

I think a proper solution would probably refactor the signal
code to allow the call side to decide whether or not to clear
this flag (such that ptrace could elect not to, but otherwise
the process is undisturbed), but I'm not familiar enough with
the code to attempt a patch for that.

If this is what's supposed to happen, or it is infeasible to
correct it, it would be useful to have a ptrace option that
can be set, to nevertheless make the injected signal final.
At the moment, I can't think of a good way to inject such a
signal.

Thanks,
Keno