Geoffrey Thomas writes about Linux internals, kernel and userspace edge cases, Rust kernel modules, SSH and security.
Commenters link his Rust kernel module slides, his names-to-reserve list and his SSH ControlMaster post, and use his signalfd article in signal handling discussions.
> Can you please elaborate on why those two ideas are "horrible"?
The problems with fork are thoroughly explained by this Microsoft paper:
https://www.microsoft.com/en-us/research/uploads/prod/2019/04/fork-hotos19.pdf
As for asynchronous signals, they are a completely broken concept and implementation.
It's not safe to do pretty much anything in a signal handler other than setting a flag and returning. You cannot use any non-reentrant function which eliminates pretty much everything useful, including the vast majority of standard library functions.
The system drops events. Signals are essentially a pending delivery flag in the kernel, signaling a process just sets the flag to 1 so there's no difference between doing it once and 10000 times.
Was in a system call when your process was signaled? It will be interrupted or cancelled. It returns EINTR and your code needs robust retrying logic to handle such a case. I've seen code that couldn't handle it and crashed.
There's race conditions everywhere. Signals can arrive while you're handling other signals, you need to block block them if you don't want that. Every thread has its own signal handling behavior. Signals sent to a process are handled by "any" thread. I remember reading in some standard that using signals in multithreaded programs was undefined behavior. Who even knows what's going to happen?
The only borderline sane way to handle signals is with file descriptors: you block traditional signal handling on all…
https://jrock.us/
https://jgc.org/
http://kaledavis.com/
https://aegir.sexy/
https://www.gwern.net/
https://jacquesmattheij.com/
https://danwin.com/
https://www.kalzumeus.com/
https://www.magic.do/
https://edweissman.com/
https://jakeseliger.com/
http://www.fogus.me/
https://www.stavros.io/
https://jasonlefkowitz.net/
https://minimaxir.com/
https://www.robinwhittleton.com/
https://ciexinc.com/
https://rongarret.info/
https://williampietri.com/
https://bpier.re/
https://joshtriplett.org/
https://ooer.com/
https://feross.org/resume/
https://www.brandons.me/
https://benjaminpbreen.com/
http://hokstad.com/
https://www.scott-a-s.com/
https://www.bermita.com/
http://tlrobinson.net/
https://ldpreload.com/
https://dotat.at/
http://xavvy.com/
https://correa.in/
https://adriann.github.io/
https://leclan.ch/
https://joeyespo.com/
https://pius.me/
https://adamierymenko.com/
https://drewdevault.com/
https://battlepenguin.com/
https://chris.improbable.org/
https://www.snellman.net/
https://ocdtrekkie.com/
https://www.cryptolotus.com/
https://www.gregkogan.com/
https://david.kitchen/
In no particular order. From: https://jessimekirk.com/blog/hn_users_links/. I went through about half of the list looking for personal websites to use as inspiration for my own.
There are a few ways to handle this design question ("what happens if something needs to interact with a process while it's blocked in a system call?") more or less sanely, and UNIX chooses the least sane one (making userspace deal with the complexity of system calls possibly doing part or none of the work that was requested). Other operating systems might fully transparently guarantee that the system call completes before the process is notified of said interaction, but this isn't compatible with IPC as "lightweight" (unbuffered and without backpressure) as signals.
The Right Thing is to make system call submission atomic and asynchronous, only waiting on completion by explicit choice, and remove signals entirely in favor of buffered message-passing IPC. This is basically the world we're approaching with io_uring and signalfd, except for ugly interaction with coalescing and signal dispositions (see https://ldpreload.com/blog/signalfd-is-useless), and the fact that many syscalls still can't be performed through io_uring.
If UNIX had a better API for process management, people wouldn't see signals as necessary, but that's its own can of worms with its own Linux-specific partial fix (pidfd) and genre of gripe article (e.g. https://news.ycombinator.com/item?id=35264487).
There was a bug in a released kernel I ended up having to hunt down back last summer. It required a very specific set of circumstances to actually trigger the bug, but luckily for us we found some obvious side effects from the bug such that it was possible to get the relevant information via a very simple statically compiled C program (there would be a certain value under /sys).
I used that C program as the initramfs for qemu, with it spinning up an instance based on the test kernel, and could grep for what I wanted in the output to get a relevant exit code.
Combine that git bisect's automatic bisecting process, and I was able to automatically git bisect through a few thousand commits in the kernel to find the cause. It only took it about 50 minutes to find it in the end, which was helped a bunch by ccache (and the sheer build parallelism granted by building on a 52 core system).
That was a fun, somewhat out of the blue, task.
The process I took was based on https://ldpreload.com/blog/git-bisect-run, with various adaptations to suit the needs and the relevant build environment.
I recently rewrote my fork of cplay and got fed up with curses. Boy do I wish I had found this article earlier because it describes everything I painfully had to cobble together. This is great!
Just one nit: Doing stuff in a signal handler is usually a bad idea because it can interrupt your code at any time. The usual work around is a trick called "self-pipe". https://ldpreload.com/blog/signalfd-is-useless
For anyone interested, I created a library in the process: https://github.com/xi/boon (It takes some inspiration from react but bare with me, I think it actually makes sense.)
Check out out slides 20-21 from Linux Security Summit 2019 - there are specific reasons why Linux didn't like C++ that don't apply to Rust. https://ldpreload.com/p/kernel-modules-in-rust-lssna2019.pdf
Basically, we think (and we hope Linus agrees) that Rust has the relevant benefits of C++ without the downsides that have so far kept C++ from being adopted. (It has other great features that C++ doesn't have like compiler-enforced memory safety, too, but I agree with you that RAII is a pretty important and obvious win for the kernel just by itself.)