Linux USB
 help / color / mirror / Atom feed
From: Yue Sun <samsun1006219@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Oliver Neukum <oneukum@suse.com>
Cc: Sam Sun <samsun1006219@gmail.com>,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] USB: misc: iowarrior: fix use-after-free in release
Date: Fri, 19 Jun 2026 23:03:37 +0800	[thread overview]
Message-ID: <20260619150340.65058-1-samsun1006219@gmail.com> (raw)

From: Sam Sun <samsun1006219@gmail.com>

iowarrior_release() clears dev->opened while holding dev->mutex and then
unlocks the mutex before freeing the device when it has already been
disconnected. If iowarrior_disconnect() is waiting for the same mutex, it
can acquire it while mutex_unlock() in release is still in progress,
observe dev->opened == 0, and free the same object. This violates the
mutex_unlock() lifetime rule because the mutex storage can be freed before
mutex_unlock() has returned.

Fix this by adding a kref to struct iowarrior and holding one reference
for the USB interface, one for each opened file, and one for each
in-flight asynchronous write URB. Release, disconnect and write completion
now only drop their references after they are done using the object, and
the object is freed only after all users have released it.

Reported-by: Sam Sun <samsun1006219@gmail.com>
Closes: https://lore.kernel.org/r/20260618080204.38322-1-samsun1006219@gmail.com
Signed-off-by: Sam Sun <samsun1006219@gmail.com>
---
 drivers/usb/misc/iowarrior.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 22504c0a2841..abeed76379d7 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/mutex.h>
+#include <linux/kref.h>
 #include <linux/poll.h>
 #include <linux/hid.h>
 #include <linux/usb/iowarrior.h>
@@ -73,6 +74,7 @@ static struct usb_driver iowarrior_driver;
 /* Structure to hold all of our device specific stuff */
 struct iowarrior {
 	struct mutex mutex;			/* locks this structure */
+	struct kref kref;
 	struct usb_device *udev;		/* save off the usb device pointer */
 	struct usb_interface *interface;	/* the interface for this device */
 	struct usb_endpoint_descriptor *int_out_endpoint;	/* endpoint for reading (needed for IOW56 only) */
@@ -95,6 +97,10 @@ struct iowarrior {
 	struct usb_anchor submitted;
 };
 
+#define to_iowarrior_dev(d) container_of(d, struct iowarrior, kref)
+
+static void iowarrior_delete(struct kref *kref);
+
 /*--------------*/
 /*    globals   */
 /*--------------*/
@@ -235,13 +241,16 @@ static void iowarrior_write_callback(struct urb *urb)
 	/* tell a waiting writer the interrupt-out-pipe is available again */
 	atomic_dec(&dev->write_busy);
 	wake_up_interruptible(&dev->write_wait);
+	kref_put(&dev->kref, iowarrior_delete);
 }
 
 /*
  *	iowarrior_delete
  */
-static inline void iowarrior_delete(struct iowarrior *dev)
+static void iowarrior_delete(struct kref *kref)
 {
+	struct iowarrior *dev = to_iowarrior_dev(kref);
+
 	kfree(dev->int_in_buffer);
 	usb_free_urb(dev->int_in_urb);
 	kfree(dev->read_queue);
@@ -454,12 +463,14 @@ static ssize_t iowarrior_write(struct file *file,
 			goto error;
 		}
 		usb_anchor_urb(int_out_urb, &dev->submitted);
+		kref_get(&dev->kref);
 		retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
 		if (retval) {
 			dev_dbg(&dev->interface->dev,
 				"submit error %d for urb nr.%d\n",
 				retval, atomic_read(&dev->write_busy));
 			usb_unanchor_urb(int_out_urb);
+			kref_put(&dev->kref, iowarrior_delete);
 			goto error;
 		}
 		/* submit was ok */
@@ -637,6 +648,7 @@ static int iowarrior_open(struct inode *inode, struct file *file)
 	}
 	/* increment our usage count for the driver */
 	++dev->opened;
+	kref_get(&dev->kref);
 	/* save our object in the file's private structure */
 	file->private_data = dev;
 	retval = 0;
@@ -657,13 +669,13 @@ static int iowarrior_release(struct inode *inode, struct file *file)
 	dev = file->private_data;
 	if (!dev)
 		return -ENODEV;
+	file->private_data = NULL;
 
 	/* lock our device */
 	mutex_lock(&dev->mutex);
 
 	if (dev->opened <= 0) {
 		retval = -ENODEV;	/* close called more than once */
-		mutex_unlock(&dev->mutex);
 	} else {
 		dev->opened = 0;	/* we're closing now */
 		retval = 0;
@@ -675,13 +687,11 @@ static int iowarrior_release(struct inode *inode, struct file *file)
 			usb_kill_urb(dev->int_in_urb);
 			wake_up_interruptible(&dev->read_wait);
 			wake_up_interruptible(&dev->write_wait);
-			mutex_unlock(&dev->mutex);
-		} else {
-			/* The device was unplugged, cleanup resources */
-			mutex_unlock(&dev->mutex);
-			iowarrior_delete(dev);
 		}
 	}
+	mutex_unlock(&dev->mutex);
+
+	kref_put(&dev->kref, iowarrior_delete);
 	return retval;
 }
 
@@ -768,6 +778,7 @@ static int iowarrior_probe(struct usb_interface *interface,
 		return retval;
 
 	mutex_init(&dev->mutex);
+	kref_init(&dev->kref);
 
 	atomic_set(&dev->intr_idx, 0);
 	atomic_set(&dev->read_idx, 0);
@@ -885,7 +896,7 @@ static int iowarrior_probe(struct usb_interface *interface,
 	return retval;
 
 error:
-	iowarrior_delete(dev);
+	kref_put(&dev->kref, iowarrior_delete);
 	return retval;
 }
 
@@ -918,8 +929,8 @@ static void iowarrior_disconnect(struct usb_interface *interface)
 	} else {
 		/* no process is using the device, cleanup now */
 		mutex_unlock(&dev->mutex);
-		iowarrior_delete(dev);
 	}
+	kref_put(&dev->kref, iowarrior_delete);
 }
 
 /* usb specific object needed to register this driver with the usb subsystem */
-- 
2.34.1


             reply	other threads:[~2026-06-19 15:03 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-19 15:03 Yue Sun [this message]
2026-06-22 15:49 ` [PATCH] USB: misc: iowarrior: fix use-after-free in release Johan Hovold

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260619150340.65058-1-samsun1006219@gmail.com \
    --to=samsun1006219@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=oneukum@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox