Skip to content

Various leak/cycle fixes#410

Merged
jow- merged 5 commits into
jow-:masterfrom
nbd168:fixes
Jun 3, 2026
Merged

Various leak/cycle fixes#410
jow- merged 5 commits into
jow-:masterfrom
nbd168:fixes

Conversation

@nbd168

@nbd168 nbd168 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Break potential reference cycles by clearing resource held values.
Fix fd leak on uloop tasks
Fix potential use-after-free on re-entrant ubus channel disconnect calls
Fix refcount leak on setting prototypes

blogic and others added 5 commits June 3, 2026 08:22
ucv_prototype_set() overwrote the prototype pointer without releasing
the previous prototype, even though ucv_free() does drop it. Anything
that re-installs a prototype, notably the ubus binding installing a
fresh request prototype on every incoming call, leaked the old one
each time.

Release the previous prototype before storing the new one, matching
the ownership convention of ucv_object_add() and ucv_array_push().

Signed-off-by: John Crispin <john@phrozen.org>
uc_uloop_task_clear() closed the task output pipe via uloop_fd_close()
without first calling uloop_fd_delete(). The descriptor stayed
registered with the event loop and any pending cur_fds[] entries were
left dangling, causing a use-after-free once the task struct was freed.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Callback resources can form a reference cycle: the resource owns a
closure (the callback) whose upvalues capture the resource itself, either
directly or via a wrapping userdata object. Pure reference counting cannot
break such a cycle on its own; the resource and its captured closure would
only ever be reclaimed by a subsequent mark-and-sweep gc pass, and would
otherwise be pinned for the lifetime of the process.

When releasing the callback resource in uc_uloop_cb_free(), drop all of
its stored upvalues first. This severs the cycle deterministically at the
point where uloop is done with the resource, allowing the closure and any
data it captured to be freed immediately via refcount, without waiting for
gc to come around.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
ubus connection and object resources can form a reference cycle: the
resource owns closures (disconnect/notify/request callbacks) whose
upvalues capture the resource itself, either directly or via a wrapping
userdata object. Pure reference counting cannot break such a cycle on
its own; the resource and its captured closures would only ever be
reclaimed by a subsequent mark-and-sweep gc pass, and would otherwise
be pinned for the lifetime of the process. In the connection case this
also leaked the underlying socket fd on every reconnect.

When releasing the resource in uc_ubus_put_res(), drop all of its
stored upvalues first. This severs the cycle deterministically at the
point where ubus is done with the resource, allowing the closures and
any data they captured to be freed immediately via refcount, without
waiting for gc to come around.

Reported-by: John Crispin <john@phrozen.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
…onnect

uc_ubus_channel_disconnect_cb invokes the user-supplied disconnect_cb
before tearing down the connection. The uc_ubus_connection_t (c)
lives in the payload of c->res, so its lifetime is tied to the resource.

If the handler calls c.disconnect() on the same channel,
uc_ubus_disconnect runs ubus_shutdown and drops the last reference via
uc_ubus_put_res, freeing c. After the callback returns, the outer
function still dereferences c->buf, c->ctx, c->fd_handle and c->res.

Pin an extra reference to c->res across the user callback and the
subsequent teardown so the resource cannot be freed until the outer
function is done with it.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
@jow- jow- merged commit 38167bf into jow-:master Jun 3, 2026
2 checks passed
@jow-

jow- commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Merged, thanks!

@nbd168 nbd168 deleted the fixes branch June 3, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants