]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/queue: increase the reference counter of the request before processing it
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 16 Jan 2024 04:50:23 +0000 (13:50 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 16 Jan 2024 14:25:32 +0000 (23:25 +0900)
To prevent the request freed in req->process().

This also makes a request that is not requested by a link detached on failure.
Otherwise, the request may periodically processed and failed forever.

src/network/networkd-queue.c

index 0b89ec97364b2971c36d8413ad5db7aeb97d51d5..d1e9fec57a4b6198d9ee3cb6197f9978d4fe7c5a 100644 (file)
@@ -231,42 +231,40 @@ int manager_process_requests(Manager *manager) {
         manager->request_queued = false;
 
         ORDERED_SET_FOREACH(req, manager->request_queue) {
-                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
-
-                assert(req->process);
-
                 if (req->waiting_reply)
-                        continue; /* Waiting for netlink reply. */
+                        continue; /* Already processed, and waiting for netlink reply. */
 
                 /* Typically, requests send netlink message asynchronously. If there are many requests
                  * queued, then this event may make reply callback queue in sd-netlink full. */
                 if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
                     netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
                     fw_ctx_get_reply_callback_count(manager->fw_ctx) >= REPLY_CALLBACK_COUNT_THRESHOLD)
-                        return 0;
+                        break;
 
-                r = req->process(req, link, req->userdata);
-                if (r == 0) { /* The request is not ready. */
-                        if (manager->request_queued)
-                                break; /* a new request is queued during processing the request. */
-                        continue;
-                }
+                /* Avoid the request and link freed by req->process() and request_detach(). */
+                _unused_ _cleanup_(request_unrefp) Request *req_unref = request_ref(req);
+                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
 
-                /* If the request sends netlink message, e.g. for Address or so, the Request object is
-                 * referenced by the netlink slot, and will be detached later by its destroy callback.
-                 * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
-                if (!req->waiting_reply)
+                assert(req->process);
+                r = req->process(req, link, req->userdata);
+                if (r < 0) {
                         request_detach(manager, req);
 
-                if (r < 0 && link) {
-                        link_enter_failed(link);
-                        /* link_enter_failed() may remove multiple requests,
-                         * hence we need to exit from the loop. */
-                        break;
+                        if (link) {
+                                link_enter_failed(link);
+                                /* link_enter_failed() may detach multiple requests from the queue.
+                                 * Hence, we need to exit from the loop. */
+                                break;
+                        }
                 }
+                if (r > 0 && !req->waiting_reply)
+                        /* If the request sends netlink message, e.g. for Address or so, the Request object is
+                         * referenced by the netlink slot, and will be detached later by its destroy callback.
+                         * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
+                        request_detach(manager, req);
 
                 if (manager->request_queued)
-                        break;
+                        break; /* New request is queued. Exit from the loop. */
         }
 
         return 0;