]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usbip: add sysfs_lock to synchronize sysfs code paths
authorShuah Khan <skhan@linuxfoundation.org>
Fri, 16 Apr 2021 20:53:16 +0000 (15:53 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Apr 2021 10:07:16 +0000 (12:07 +0200)
commit 4e9c93af7279b059faf5bb1897ee90512b258a12 upstream.

Fuzzing uncovered race condition between sysfs code paths in usbip
drivers. Device connect/disconnect code paths initiated through
sysfs interface are prone to races if disconnect happens during
connect and vice versa.

This problem is common to all drivers while it can be reproduced easily
in vhci_hcd. Add a sysfs_lock to usbip_device struct to protect the paths.

Use this in vhci_hcd to protect sysfs paths. For a complete fix, usip_host
and usip-vudc drivers and the event handler will have to use this lock to
protect the paths. These changes will be done in subsequent patches.

Cc: stable@vger.kernel.org # 4.9.x
Reported-and-tested-by: syzbot+a93fba6d384346a761e3@syzkaller.appspotmail.com
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
Link: https://lore.kernel.org/r/b6568f7beae702bbc236a545d3c020106ca75eac.1616807117.git.skhan@linuxfoundation.org
Signed-off-by: Tom Seewald <tseewald@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/usbip/usbip_common.h
drivers/usb/usbip/vhci_hcd.c
drivers/usb/usbip/vhci_sysfs.c

index 0b199a2664c009630f0772fc82e752fc04ba252d..3d47c681aea2fa8a191300a25cb32c2df6185bcf 100644 (file)
@@ -278,6 +278,9 @@ struct usbip_device {
        /* lock for status */
        spinlock_t lock;
 
+       /* mutex for synchronizing sysfs store paths */
+       struct mutex sysfs_lock;
+
        int sockfd;
        struct socket *tcp_socket;
 
index 8bda6455dfcb2c95342c820bbed5d735474e36ea..fb7b03029b8e67df3e8d750ae033df4709365fae 100644 (file)
@@ -907,6 +907,7 @@ static void vhci_device_init(struct vhci_device *vdev)
        vdev->ud.side   = USBIP_VHCI;
        vdev->ud.status = VDEV_ST_NULL;
        spin_lock_init(&vdev->ud.lock);
+       mutex_init(&vdev->ud.sysfs_lock);
 
        INIT_LIST_HEAD(&vdev->priv_rx);
        INIT_LIST_HEAD(&vdev->priv_tx);
index ca00d38d22afd70e28d3c964fabb9ab91831610b..3496b402aa1b7c61e10a08aa534ff876b98c40cf 100644 (file)
@@ -161,6 +161,8 @@ static int vhci_port_disconnect(struct vhci_hcd *vhci, __u32 rhport)
 
        usbip_dbg_vhci_sysfs("enter\n");
 
+       mutex_lock(&vdev->ud.sysfs_lock);
+
        /* lock */
        spin_lock_irqsave(&vhci->lock, flags);
        spin_lock(&vdev->ud.lock);
@@ -171,6 +173,7 @@ static int vhci_port_disconnect(struct vhci_hcd *vhci, __u32 rhport)
                /* unlock */
                spin_unlock(&vdev->ud.lock);
                spin_unlock_irqrestore(&vhci->lock, flags);
+               mutex_unlock(&vdev->ud.sysfs_lock);
 
                return -EINVAL;
        }
@@ -181,6 +184,8 @@ static int vhci_port_disconnect(struct vhci_hcd *vhci, __u32 rhport)
 
        usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
 
+       mutex_unlock(&vdev->ud.sysfs_lock);
+
        return 0;
 }
 
@@ -309,30 +314,36 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
        vhci = hcd_to_vhci(hcd);
        vdev = &vhci->vdev[rhport];
 
+       mutex_lock(&vdev->ud.sysfs_lock);
+
        /* Extract socket from fd. */
        socket = sockfd_lookup(sockfd, &err);
        if (!socket) {
                dev_err(dev, "failed to lookup sock");
-               return -EINVAL;
+               err = -EINVAL;
+               goto unlock_mutex;
        }
        if (socket->type != SOCK_STREAM) {
                dev_err(dev, "Expecting SOCK_STREAM - found %d",
                        socket->type);
                sockfd_put(socket);
-               return -EINVAL;
+               err = -EINVAL;
+               goto unlock_mutex;
        }
 
        /* create threads before locking */
        tcp_rx = kthread_create(vhci_rx_loop, &vdev->ud, "vhci_rx");
        if (IS_ERR(tcp_rx)) {
                sockfd_put(socket);
-               return -EINVAL;
+               err = -EINVAL;
+               goto unlock_mutex;
        }
        tcp_tx = kthread_create(vhci_tx_loop, &vdev->ud, "vhci_tx");
        if (IS_ERR(tcp_tx)) {
                kthread_stop(tcp_rx);
                sockfd_put(socket);
-               return -EINVAL;
+               err = -EINVAL;
+               goto unlock_mutex;
        }
 
        /* get task structs now */
@@ -353,7 +364,8 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
                kthread_stop_put(tcp_tx);
 
                dev_err(dev, "port %d already used\n", rhport);
-               return -EINVAL;
+               err = -EINVAL;
+               goto unlock_mutex;
        }
 
        dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n",
@@ -378,7 +390,15 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 
        rh_port_connect(vdev, speed);
 
+       dev_info(dev, "Device attached\n");
+
+       mutex_unlock(&vdev->ud.sysfs_lock);
+
        return count;
+
+unlock_mutex:
+       mutex_unlock(&vdev->ud.sysfs_lock);
+       return err;
 }
 static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);