blob: 4cba5ae4ded3293045833b6f21e4540ce7565378 [file] [log] [blame]
https://bugs.launchpad.net/libnih/+bug/518921
fix race in signal processing leading to lost signals
The current loop will walk all signals one by one and run the handler
for it if any signals were pending. Then it clears the array. But if
a signal comes in for an earlier checked signal, it will get clobbered
when the final clear runs.
Change the logic so that we only clear entries for signals whose handler
we explicitly call. If a different signal comes in, we'll process it
the next time around.
This was discovered & triaged by Jeffy Chen from Rockchip.
=== modified file 'nih/signal.c'
--- nih/signal.c 2009-06-23 09:29:37 +0000
+++ nih/signal.c 2015-05-21 08:12:11 +0000
@@ -337,17 +337,37 @@
nih_signal_init ();
+ /* Since this poller runs w/out signals masked, we do not want to try
+ * and clear any other signals (like zeroing the caught array at the
+ * end). If we do that, we open a race:
+ * - Walk the list of signals.
+ * - First one is not set so we move on to the second one.
+ * - First signal comes in while processing second and increments the
+ * caught array entry.
+ * - Finish walking the whole list.
+ * - Zero out the whole list and thus throw away the first signal.
+ * Since the signal handlers can take any length of time, this race
+ * can be open for a variable amount of time.
+ */
+
NIH_LIST_FOREACH_SAFE (nih_signals, iter) {
NihSignal *signal = (NihSignal *)iter;
if (! signals_caught[signal->signum])
continue;
+ /* Now that we know we're going to process this signal, clear
+ * out all pending counts for it. There is a slight race here
+ * where the same signal can come in, but the API has never
+ * guaranteed exact coverage since POSIX does not provide it --
+ * more than one signal can be collapsed into one event. All
+ * we can guarantee is that we'll notice signals that come in
+ * once the handler runs.
+ */
+ signals_caught[signal->signum] = 0;
+
signal->handler (signal->data, signal);
}
-
- for (s = 0; s < NUM_SIGNALS; s++)
- signals_caught[s] = 0;
}