]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usbip: stub-dev synchronize sysfs code paths
authorShuah Khan <skhan@linuxfoundation.org>
Fri, 16 Apr 2021 20:53:17 +0000 (15:53 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Apr 2021 10:07:16 +0000 (12:07 +0200)
commit 9dbf34a834563dada91366c2ac266f32ff34641a 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.

Use sysfs_lock to protect sysfs paths in stub-dev.

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/2b182f3561b4a065bf3bf6dce3b0e9944ba17b3f.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/stub_dev.c

index 6b643e6c8f0bd3f29ca44af742c5e12781ba3629..cec5805feb254e410ead34a9a2829ca5dc95b392 100644 (file)
@@ -77,6 +77,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 
                dev_info(dev, "stub up\n");
 
+               mutex_lock(&sdev->ud.sysfs_lock);
                spin_lock_irq(&sdev->ud.lock);
 
                if (sdev->ud.status != SDEV_ST_AVAILABLE) {
@@ -101,13 +102,13 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
                tcp_rx = kthread_create(stub_rx_loop, &sdev->ud, "stub_rx");
                if (IS_ERR(tcp_rx)) {
                        sockfd_put(socket);
-                       return -EINVAL;
+                       goto unlock_mutex;
                }
                tcp_tx = kthread_create(stub_tx_loop, &sdev->ud, "stub_tx");
                if (IS_ERR(tcp_tx)) {
                        kthread_stop(tcp_rx);
                        sockfd_put(socket);
-                       return -EINVAL;
+                       goto unlock_mutex;
                }
 
                /* get task structs now */
@@ -126,6 +127,8 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
                wake_up_process(sdev->ud.tcp_rx);
                wake_up_process(sdev->ud.tcp_tx);
 
+               mutex_unlock(&sdev->ud.sysfs_lock);
+
        } else {
                dev_info(dev, "stub down\n");
 
@@ -136,6 +139,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
                spin_unlock_irq(&sdev->ud.lock);
 
                usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
+               mutex_unlock(&sdev->ud.sysfs_lock);
        }
 
        return count;
@@ -144,6 +148,8 @@ sock_err:
        sockfd_put(socket);
 err:
        spin_unlock_irq(&sdev->ud.lock);
+unlock_mutex:
+       mutex_unlock(&sdev->ud.sysfs_lock);
        return -EINVAL;
 }
 static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
@@ -309,6 +315,7 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev)
        sdev->ud.side           = USBIP_STUB;
        sdev->ud.status         = SDEV_ST_AVAILABLE;
        spin_lock_init(&sdev->ud.lock);
+       mutex_init(&sdev->ud.sysfs_lock);
        sdev->ud.tcp_socket     = NULL;
        sdev->ud.sockfd         = -1;