From: Luca Boccassi Date: Thu, 2 Jul 2026 17:58:11 +0000 (+0100) Subject: sd-netlink: disconnect the slot on async/match error paths X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=25c016f472b775cd6d8bcf7fd29506663cbf81de;p=thirdparty%2Fsystemd.git sd-netlink: disconnect the slot on async/match error paths 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 --- diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 65d731a20b1..8c2790e94b4 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -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;