-- Leo's gemini proxy

-- Connecting to gemini.omarpolo.com:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini;lang=en

Fun with seccomp & signals

> debugging fiters using signals when signals themselves are the problem

Written while listening to “Lucy in the Sky with Diamonds” by The Beatles.

Published: 2021-02-08

Tagged with:

#linux


Debugging for something unrelated, I noticed that on linux gmid’ server process would crash upon SIGHUP. I never noticed it before because (unfortunately) ‘ulimit -c 0’ seems to be the default on various systems (i.e. no core files) and I started testing on-the-fly reconfiguration only recently.


What was particularly strange was that I got not logging whatsoever. I have a compile-time switch for seccomp to raise a catchable SIGSYS to dump the number of the forbidden system call and exit, but in this case my server processes were killed by a SIGSYS without any debugging info.


My first theory was that during the process shutdown (server process gracefully shuts down after a SIGHUP) an unwanted syscall was done, maybe after stderr was flushed and closed and thus my signal handler wasn’t able to print info. But it didn’t seemed the case.


On OpenBSD I have used in the past ktrace(1) to trace the system calls done by a process, so I searched for something similar for linux. Turns out, strace is quite flexible.


I attached strace to the server process:


-bash-5.1# strace -p 30232
strace: Process 30232 attached
epoll_pwait(6,

Good, the server process is waiting on epoll as it should, let’s send it a SIGHUP:


-bash-5.1# strace -p 30232
strace: Process 30232 attached
epoll_pwait(6, 0x55724496a0, 32, -1, NULL, 8) = -1 EINTR (Interrupted system call)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=30251, si_uid=1000} ---
write(8, "\1", 1)                       = 1
rt_sigreturn({mask=[]})                 = ?
+++ killed by SIGSYS +++

Oh, what do we have here. rt_sigreturn(2)! (the write is libevent handling the signal)


After an event handler is called, to restore the program stack linux injects a call to rt_sigreturn. If that syscall gets blocked by a BPF filter, it fails to handle the SIGSYS caused by the filter itself and just crash.


But why for SIGHUP it crashes and for the catchable SIGSYS I was using for the debugging it doesn’t? Well, the SIGSYS handler calls directly _exit, so we don’t rearch the sigreturn.


This is just a daily remainder of how low-level seccomp is, and how sometimes it just leaves you clueless, but also a nice opportunity to learn how signals are implemented on linux.





-- text: CC0 1.0; code: public domain (unless specified otherwise). No copyright here.

-- Response ended

-- Page fetched on Tue Apr 16 22:57:22 2024