]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fuse: fix device node leak in cuse_process_init_reply()
authorAlberto Ruiz <aruiz@redhat.com>
Wed, 8 Apr 2026 15:23:40 +0000 (17:23 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 15 Jun 2026 12:06:15 +0000 (14:06 +0200)
If device_add() succeeds during CUSE initialization but a subsequent
step (cdev_alloc() or cdev_add()) fails, the error path calls
put_device() without first calling device_del().  This leaks the
devtmpfs entry created by device_add(), leaving a stale /dev/<name>
node that persists until reboot.

Since the cuse_conn is never linked into cuse_conntbl on the failure
path, cuse_channel_release() sees cc->dev == NULL and skips
device_unregister(), so no other code path cleans up the node.

This has several consequences:

 - The device name is permanently poisoned: any subsequent attempt to
   create a CUSE device with the same name hits the stale sysfs entry,
   device_add() fails, and the new device is aborted.

 - The collision manifests as ENODEV returned to userspace with no
   dmesg diagnostic, making it very difficult to debug.

 - The failure is self-perpetuating: once a name is leaked, all future
   attempts with that name fail identically.

Fix this by introducing an err_dev label that calls device_del() to
undo device_add() before falling through to err_unlock.  The existing
err_unlock path from a device_add() failure correctly skips device_del()
since the device was never added.

Testing instructions can be found at the lore link below.

Link: https://lore.kernel.org/all/20260408-wip-cuse-leak-fix-v1-0-1c028d575e97@redhat.com/
Signed-off-by: Alberto Ruiz <aruiz@redhat.com>
Fixes: 151060ac1314 ("CUSE: implement CUSE - Character device in Userspace")
Cc: stable@vger.kernel.org
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/cuse.c

index 174333633471b2f05677d385d8e5d52c2cd30aba..3d38828af41fc261e32479938f7cf5ad2b61a251 100644 (file)
@@ -391,7 +391,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm,
        rc = -ENOMEM;
        cdev = cdev_alloc();
        if (!cdev)
-               goto err_unlock;
+               goto err_dev;
 
        cdev->owner = THIS_MODULE;
        cdev->ops = &cuse_frontend_fops;
@@ -417,6 +417,8 @@ out:
 
 err_cdev:
        cdev_del(cdev);
+err_dev:
+       device_del(dev);
 err_unlock:
        mutex_unlock(&cuse_lock);
        put_device(dev);