803fb7
From 175c446fc5ca6adbeeb25dfe0ef725e2f1914259 Mon Sep 17 00:00:00 2001
803fb7
From: Tom Gundersen <teg@jklm.no>
803fb7
Date: Mon, 9 Mar 2015 16:16:23 +0100
803fb7
Subject: [PATCH] udevd: close race in udev settle
803fb7
803fb7
The udev-settle guarantees that udevd is no longer processing any of the
803fb7
events casued by udev-trigger. The way this works is that it sends a
803fb7
synchronous PING to udevd after udev-trigger has ran, and when that returns
803fb7
it knows that udevd has started processing the events from udev-trigger.
803fb7
udev-settle will then wait for the event queue to empty before returning.
803fb7
803fb7
However, there was a race here, as we would only update the /run state at
803fb7
the beginning of the event loop, before reading out new events and before
803fb7
processing the ping.
803fb7
803fb7
That means that if the first uevent arrived in the same event-loop iteration
803fb7
as the PING, we would return the ping before updating the queue state in /run
803fb7
(which would happen on the next iteration).
803fb7
803fb7
The race window here is tiny (as the /run state would probably get updated
803fb7
before udev-settle got a chance to read /run), but still a possibility.
803fb7
803fb7
Fix the problem by updating the /run state as the last step before returning
803fb7
the PING.
803fb7
803fb7
We must still update it at the beginning of the loop as well, otherwise we
803fb7
risk being stuck in poll() with a stale state in /run.
803fb7
803fb7
Reported-by: Daniel Drake <drake@endlessm.com>
803fb7
(cherry picked from commit db93e063bdffe0a8b95fcc522aeacddf62d1a9f9)
803fb7
---
803fb7
 src/udev/udevd.c | 26 +++++++++++++++++---------
803fb7
 1 file changed, 17 insertions(+), 9 deletions(-)
803fb7
803fb7
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
803fb7
index 99d4c8983..e98c1fd6d 100644
803fb7
--- a/src/udev/udevd.c
803fb7
+++ b/src/udev/udevd.c
803fb7
@@ -909,6 +909,17 @@ static void handle_signal(struct udev *udev, int signo) {
803fb7
         }
803fb7
 }
803fb7
 
803fb7
+static void event_queue_update(void) {
803fb7
+        if (!udev_list_node_is_empty(&event_list)) {
803fb7
+                int fd;
803fb7
+
803fb7
+                fd = open("/run/udev/queue", O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
803fb7
+                if (fd >= 0)
803fb7
+                       close(fd);
803fb7
+        } else
803fb7
+                unlink("/run/udev/queue");
803fb7
+}
803fb7
+
803fb7
 static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) {
803fb7
         int ctrl = -1, netlink = -1;
803fb7
         int fd, n;
803fb7
@@ -1369,15 +1380,7 @@ int main(int argc, char *argv[]) {
803fb7
                 }
803fb7
 
803fb7
                 /* tell settle that we are busy or idle */
803fb7
-                if (!udev_list_node_is_empty(&event_list)) {
803fb7
-                        int fd;
803fb7
-
803fb7
-                        fd = open("/run/udev/queue", O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
803fb7
-                        if (fd >= 0)
803fb7
-                                close(fd);
803fb7
-                } else {
803fb7
-                        unlink("/run/udev/queue");
803fb7
-                }
803fb7
+                event_queue_update();
803fb7
 
803fb7
                 fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
803fb7
                 if (fdcount < 0)
803fb7
@@ -1502,6 +1505,11 @@ int main(int argc, char *argv[]) {
803fb7
                 if (is_inotify)
803fb7
                         handle_inotify(udev);
803fb7
 
803fb7
+                /* tell settle that we are busy or idle, this needs to be before the
803fb7
+                 * PING handling
803fb7
+                 */
803fb7
+                event_queue_update();
803fb7
+
803fb7
                 /*
803fb7
                  * This needs to be after the inotify handling, to make sure,
803fb7
                  * that the ping is send back after the possibly generated