]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-netlink: disconnect the slot on async/match error paths
authorLuca Boccassi <luca.boccassi@gmail.com>
Thu, 2 Jul 2026 17:58:11 +0000 (18:58 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Mon, 6 Jul 2026 12:42:22 +0000 (13:42 +0100)
netlink_slot_allocate() links the slot into nl->slots and takes an nl
reference, but sd_netlink_call_async() and netlink_add_match_internal()
hold it in a _cleanup_free_, so an error after allocation frees the slot
with a bare free()

Follow-up for ee38400bbaf04ff093c5450a554d5cc70face217

src/libsystemd/sd-netlink/sd-netlink.c

index 65d731a20b139bce229b96d5e282b65e6864a5ee..8c2790e94b49e0e143c92a7194b8c8427ec56cac 100644 (file)
@@ -548,7 +548,7 @@ int sd_netlink_call_async(
                 uint64_t usec,
                 const char *description) {
 
-        _cleanup_free_ sd_netlink_slot *slot = NULL;
+        _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
         int r, k;
 
         assert_return(nl, -EINVAL);
@@ -859,34 +859,39 @@ int netlink_add_match_internal(
                 void *userdata,
                 const char *description) {
 
-        _cleanup_free_ sd_netlink_slot *slot = NULL;
+        _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
         int r;
 
         assert(groups);
         assert(n_groups > 0);
 
-        for (size_t i = 0; i < n_groups; i++) {
-                r = socket_broadcast_group_ref(nl, groups[i]);
-                if (r < 0)
-                        return r;
-        }
-
         r = netlink_slot_allocate(nl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback),
                                   userdata, description, &slot);
         if (r < 0)
                 return r;
 
-        slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
-        if (!slot->match_callback.groups)
-                return -ENOMEM;
-
-        slot->match_callback.n_groups = n_groups;
         slot->match_callback.callback = callback;
         slot->match_callback.type = type;
         slot->match_callback.cmd = cmd;
 
+        slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
+        if (!slot->match_callback.groups)
+                return -ENOMEM;
+
+        /* Link the callback before taking the broadcast group references, so that on a partial failure the
+         * cleanup path (sd_netlink_slot_unref() -> netlink_slot_disconnect()) can unlink it again and drop
+         * the references we already took. n_groups is bumped as each reference is acquired, so the disconnect
+         * only releases what we actually got. */
         LIST_PREPEND(match_callbacks, nl->match_callbacks, &slot->match_callback);
 
+        for (size_t i = 0; i < n_groups; i++) {
+                r = socket_broadcast_group_ref(nl, groups[i]);
+                if (r < 0)
+                        return r;
+
+                slot->match_callback.n_groups = i + 1;
+        }
+
         /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
         slot->destroy_callback = destroy_callback;