diff options
| author | giacomo <delleceste@gmail.com> | 2026-07-15 12:10:54 +0000 |
|---|---|---|
| committer | Christos Margiolis <christos@FreeBSD.org> | 2026-07-24 11:20:09 +0000 |
| commit | d83e42234f76504a1ff7f4309ad629b6644bfb16 (patch) | |
| tree | 4295419d74118d65c6e944c4b2fbc1403dacf068 | |
| parent | 755685dd665ef209912c59da6a7d0e7f2c9f464b (diff) | |
cuse: Fix server reference leak in cuse_client_open()
If the server is closing (or the device node is going away), or if
devfs_set_cdevpriv() fails, cuse_client_open() returns with the server
reference taken at the top of the function still held and the newly
allocated client still linked on pcs->hcli. Since cuse_client_free()
has not been registered as the cdevpriv destructor at that point,
nothing ever undoes this work: every open() that races the is_closing
window permanently leaks one server reference and one cuse_client.
A leaked reference is fatal on server exit: cuse_server_free()
busy-waits in an uninterruptible pause("W", hz) loop until pcs->refs
drops to 1, which now never happens, so the exiting server process
(e.g. virtual_oss(8)) is left wedged in state "D", immune to SIGKILL,
cuse.ko is pinned (kldunload hangs too), and only a reboot recovers.
Before 634e578ac7b0 the is_closing error path dropped the reference by
calling devfs_clear_cdevpriv(), which ran the cuse_client_free()
destructor. That commit moved devfs_set_cdevpriv() after the
is_closing check to fix the panic paths, but left both error returns
without any cleanup.
Fix by calling cuse_client_free() directly on both error paths. The
client is fully constructed and linked on pcs->hcli at these points,
which is exactly the state cuse_client_free() expects.
PR: 296291
Fixes: 634e578ac7b0 ("cuse: Fix cdevpriv bugs in cuse_client_open()")
Assisted-By: Claude Opus 4.8 (claude-opus-4-8)
Signed-off-by: giacomo <delleceste@gmail.com>
MFC after: 2 weeks
Reviewed by: christos
Pull-Request: https://github.com/freebsd/freebsd-src/pull/2324
| -rw-r--r-- | sys/fs/cuse/cuse.c | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c index 8f67c4b5572b..ef786d125c15 100644 --- a/sys/fs/cuse/cuse.c +++ b/sys/fs/cuse/cuse.c @@ -1546,11 +1546,19 @@ cuse_client_open(struct cdev *dev, int fflags, int devtype, struct thread *td) } cuse_server_unlock(pcs); - if (error != 0) + /* + * On error, free the client and unref the server, so that the + * exiting server process does not become unkillable. + */ + if (error != 0) { + cuse_client_free(pcc); return (error); + } - if ((error = devfs_set_cdevpriv(pcc, &cuse_client_free)) != 0) + if ((error = devfs_set_cdevpriv(pcc, &cuse_client_free)) != 0) { + cuse_client_free(pcc); return (error); + } pccmd = &pcc->cmds[CUSE_CMD_OPEN]; |
