923a60
From ac46d01c5f6a211bbbbb43e20f63ecae2549da20 Mon Sep 17 00:00:00 2001
923a60
From: Jan Synacek <jsynacek@redhat.com>
923a60
Date: Tue, 2 Apr 2019 10:23:30 +0200
923a60
Subject: [PATCH] sd-bus: deal with cookie overruns
923a60
923a60
Apparently this happens IRL. Let's carefully deal with issues like this:
923a60
when we overrun, let's not go back to zero but instead leave the highest
923a60
cookie bit set. We use that as indication that we are in "overrun
923a60
territory", and then are particularly careful with checking cookies,
923a60
i.e. that they haven't been used for still outstanding replies yet. This
923a60
should retain the quick cookie generation behaviour we used to have, but
923a60
permits dealing with overruns.
923a60
923a60
Replaces: #11804
923a60
Fixes: #11809
923a60
923a60
(cherry picked from commit 1f82f5bb4237ed5f015daf93f818e9db95e764b8)
923a60
Resolves: #1693559
923a60
---
923a60
 src/libsystemd/sd-bus/sd-bus.c | 49 +++++++++++++++++++++++++++++++++-
923a60
 src/shared/macro.h             |  2 ++
923a60
 2 files changed, 50 insertions(+), 1 deletion(-)
923a60
923a60
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
923a60
index b0a323792b..44ed2c7497 100644
923a60
--- a/src/libsystemd/sd-bus/sd-bus.c
923a60
+++ b/src/libsystemd/sd-bus/sd-bus.c
923a60
@@ -1495,7 +1495,50 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
+#define COOKIE_CYCLED (UINT32_C(1) << 31)
923a60
+
923a60
+static uint64_t cookie_inc(uint64_t cookie) {
923a60
+
923a60
+        /* Stay within the 32bit range, since classic D-Bus can't deal with more */
923a60
+        if (cookie >= UINT32_MAX)
923a60
+                return COOKIE_CYCLED; /* Don't go back to zero, but use the highest bit for checking
923a60
+                                       * whether we are looping. */
923a60
+
923a60
+        return cookie + 1;
923a60
+}
923a60
+
923a60
+static int next_cookie(sd_bus *b) {
923a60
+        uint64_t new_cookie;
923a60
+
923a60
+        assert(b);
923a60
+
923a60
+        new_cookie = cookie_inc(b->cookie);
923a60
+
923a60
+        /* Small optimization: don't bother with checking for cookie reuse until we overran cookiespace at
923a60
+         * least once, but then do it thorougly. */
923a60
+        if (FLAGS_SET(new_cookie, COOKIE_CYCLED)) {
923a60
+                uint32_t i;
923a60
+
923a60
+                /* Check if the cookie is currently in use. If so, pick the next one */
923a60
+                for (i = 0; i < COOKIE_CYCLED; i++) {
923a60
+                        if (!ordered_hashmap_contains(b->reply_callbacks, &new_cookie))
923a60
+                                goto good;
923a60
+
923a60
+                        new_cookie = cookie_inc(new_cookie);
923a60
+                }
923a60
+
923a60
+                /* Can't fulfill request */
923a60
+                return -EBUSY;
923a60
+        }
923a60
+
923a60
+good:
923a60
+        b->cookie = new_cookie;
923a60
+        return 0;
923a60
+}
923a60
+
923a60
 static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
923a60
+        int r;
923a60
+
923a60
         assert(b);
923a60
         assert(m);
923a60
 
923a60
@@ -1510,7 +1553,11 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
923a60
         if (timeout == 0)
923a60
                 timeout = BUS_DEFAULT_TIMEOUT;
923a60
 
923a60
-        return bus_message_seal(m, ++b->cookie, timeout);
923a60
+        r = next_cookie(b);
923a60
+        if (r < 0)
923a60
+                return r;
923a60
+
923a60
+        return bus_message_seal(m, b->cookie, timeout);
923a60
 }
923a60
 
923a60
 static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) {
923a60
diff --git a/src/shared/macro.h b/src/shared/macro.h
923a60
index 26df270d51..d4cdb1d08b 100644
923a60
--- a/src/shared/macro.h
923a60
+++ b/src/shared/macro.h
923a60
@@ -401,6 +401,8 @@ do {                                                                    \
923a60
 
923a60
 #define SET_FLAG(v, flag, b) \
923a60
         (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
923a60
+#define FLAGS_SET(v, flags) \
923a60
+        ((~(v) & (flags)) == 0)
923a60
 
923a60
 #define IN_SET(x, y, ...)                                               \
923a60
         ({                                                              \