From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB38037998B; Mon, 22 Jun 2026 15:27:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782142054; cv=none; b=RW+Dd00YxIgyFYC2exTgme9qN1oTBqGbfCdtCFe69AtYz+Emy8Mss9qphskYCUI5U9xgEttYExkBDWhKz1kM8gJXx1HXySun5gY2p399AqJghAHnyrOzgKMdNLPiRKMiMRiORo6s3KOFuUnqxcdFztUMKD4W4zar79+0Hy6MMF8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782142054; c=relaxed/simple; bh=ekePxrm2S6HQSGZpiHKKB1nbs+WPRuIjZ3TsfkKyc+0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KZRmERinSjThKKMG9NL8KPwsXokdcBThscdn/sDlU/uksqMIQsGdYpzWlxeQdTFNtlvh3jQKb/1IAjMds3+tfZAgGwI4HSBm+wUc9kOOdlAC/kQtH1wWAwsCb0jcKPwyBH99yYQZ18zteqq80pzsQiV3X/PRy3HjDpHvQthAGcI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WNWZ9I15; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WNWZ9I15" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 851811F000E9; Mon, 22 Jun 2026 15:27:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1782142051; bh=3yyo3T2+q3Ee+eG25RYcUNLGqNwIVD16XC+8UIG6WHs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=WNWZ9I15bwKg9wJ1PeUkMblBTymMrdE5Daq3XEbFuZbTYshjssbenlvj+vcXfwp+U CHqeiPIVK5/FHV63S6Nxcq3/sNOWTqw/S8/F4CTrZfjjuOiHOC4GuitAYrdqr8Ml9q nxSg7A4ihp0sVgwso/WVnI5TTpGTdZXu6MQ8HN9uYLH31d+0LCH+kC8TRO9tOIl7w8 oKQp88tzs/ka+NCAM0ydZpfqAZd+DUSou8fQL/Dh3lMuTaTIeJVA+VHzOAhWX3zJX4 6niilxqMUYJkh2ysVlUaxnVgBkWeVfSQA7uM2rX3uasPlUVQ5a02OLUDyW5hMu/L23 d5XwIb+Wvcs7g== Received: from johan by xi.lan with local (Exim 4.99.3) (envelope-from ) id 1wbgYb-00000000UJ3-1EXQ; Mon, 22 Jun 2026 17:27:29 +0200 From: Johan Hovold To: Greg Kroah-Hartman Cc: Juergen Stuber , Yue Sun , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold , stable@vger.kernel.org, Daniel Walker Subject: [PATCH 4/4] USB: legousbtower: fix use-after-free on disconnect race Date: Mon, 22 Jun 2026 17:26:12 +0200 Message-ID: <20260622152612.116422-5-johan@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260622152612.116422-1-johan@kernel.org> References: <20260622152612.116422-1-johan@kernel.org> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 18bcbcfe9ca2 ("USB: misc: legousbtower: semaphore to mutex") Cc: stable@vger.kernel.org # 2.6.25 Cc: Daniel Walker Signed-off-by: Johan Hovold --- drivers/usb/misc/legousbtower.c | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 052ffc2e71ee..18dd4115befb 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table); /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { + struct kref kref; struct mutex lock; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ @@ -220,7 +221,6 @@ struct lego_usb_tower { /* local function prototypes */ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); -static inline void tower_delete(struct lego_usb_tower *dev); static int tower_open(struct inode *inode, struct file *file); static int tower_release(struct inode *inode, struct file *file); static __poll_t tower_poll(struct file *file, poll_table *wait); @@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev, /* * tower_delete */ -static inline void tower_delete(struct lego_usb_tower *dev) +static inline void tower_delete(struct kref *kref) { + struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file) dev->open_count = 1; + kref_get(&dev->kref); + unlock_exit: mutex_unlock(&dev->lock); @@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file) mutex_lock(&dev->lock); - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - - /* unlock here as tower_delete frees dev */ - mutex_unlock(&dev->lock); - tower_delete(dev); - goto exit; - } + if (dev->disconnected) + goto out_unlock; /* wait until write transfer is finished */ if (dev->interrupt_out_busy) { @@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file) dev->open_count = 0; +out_unlock: mutex_unlock(&dev->lock); + kref_put(&dev->kref, tower_delete); exit: return retval; } @@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ if (!dev) goto exit; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = usb_get_dev(udev); spin_lock_init(&dev->read_buffer_lock); @@ -828,7 +829,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ return retval; error: - tower_delete(dev); + kref_put(&dev->kref, tower_delete); return retval; } @@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->lock); - tower_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->lock); } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, tower_delete); + dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", (minor - LEGO_USB_TOWER_MINOR_BASE)); } -- 2.53.0