From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f175.google.com (mail-pg1-f175.google.com [209.85.215.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A148D285CAE for ; Fri, 19 Jun 2026 15:03:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.175 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781881431; cv=none; b=OXcqeWMjPZs7YWJZ6afb1et2mr7NNMhe5YChAhoGnoem7KYXU5o05K2Jv6zlNUbOKGs1IHuVMB6X8Ut+AnJ++BoJ2jLHE39i/5vBcVfrlaFwUkvzDaIgdNRuaYxQx5rcVxq6XRIv19VvXTIYbftg72QQVzkxxKWDcPowe0pgAwc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781881431; c=relaxed/simple; bh=JormiDBFlQAyp+V29xM8gPCDfAHS9ZmMDBYT696xbYU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=ODBGs8H6WGxCYS5oXEKbvXDLbaMw4d+ZjGL7+7qIunW0gPOIUdS2/5BTviqAl1PeK/gP2xyaGCKq9QBp9fU6Xv1O1Hm4qR5jYoGAUgNA01thEvKcHgseo/l5+WtlhmUwWyaOTZBdy/RMjeJblx+3Svputqaa/E6zWDfljIeuGZE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=SdM0hxXn; arc=none smtp.client-ip=209.85.215.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SdM0hxXn" Received: by mail-pg1-f175.google.com with SMTP id 41be03b00d2f7-c89370405aaso1032884a12.0 for ; Fri, 19 Jun 2026 08:03:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781881430; x=1782486230; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=w9Fob+RGKZ6nU9HFu5yaRULjj6OpWiXjaViAAqz66iA=; b=SdM0hxXn7h8cSCVdCnB67jVFNdY9zF/KZKI/8MDCFFeL6w64GmN2lFu8OffPisWuTF 41xMekUcffEPeluQGnPRIhT80jxICwbB3UR2igvzZcP6pf3cHWvC1aycp168ofpucq63 OOJ1xKHA5IbWN5BBWD3TTQJHMHRm5DHNZTuxQ1LP1DSbBkNzJpFKen00Hf1LTMDLDnfW fFZ8vE7XbaOlM2xz7HFpAGGIif3Ru/vh/l2PUwbq3gpzHmdmAEehF60BJLy8OeuXKxt8 Ed+s3EoahglUm+LUNYZZcO07gCz7dSGtYUs7Ztq0a1Oxw9mj7PtLzHrTXpfRJoH/x8lM PpwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781881430; x=1782486230; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=w9Fob+RGKZ6nU9HFu5yaRULjj6OpWiXjaViAAqz66iA=; b=cI/8N1XzsDidqJcrQ5zJz1v4dkhztB7ZCl5tCOi+HXbaTA9rCyKj/WMbjqnA7S8AqD I/puM76NQnsVNWJXPvrA5+c0HqjZHJWK0vj7GnR8E36cm2cMDIMD5DhMaByQPRRrD7Fe c+/Aaw3s0q1XnIa6kiWWzqFK0dyHp/FqJ4r9DJOHTCOEpEHLfcFeyltfMCrYzmVU3Emx sY3alwxn95CNykj5Hd2U1SRZgqyoYLhb4LuQV3iJ4DbIfK81mWy+dRpxz4QczHydIqag eir3rv6vdiKYPqO1okxbB/NMi/Fp5kb9gAupuWAmBK3FhP1+8YuOQ7ZoZyr64z9dQ+VZ exIg== X-Forwarded-Encrypted: i=1; AFNElJ+HL8+D6LNFKj2OWPJ3qOLzgxVX5q35G76skKsm/+X3Pb/tIoUAphoY4RAk57U0z2SdB3USM4lCj/Xv0PA=@vger.kernel.org X-Gm-Message-State: AOJu0YyS5L4fS6ugBwhk/h8/33gYXsdk5ygKVRmC41mmClyf7B6y0XCh SkJhZSXaLbRGrymRD4Y6CGS3C7xv+7MfG2LUkHS7E1yiogDwhC/3JLoO X-Gm-Gg: AfdE7ckjWC9D2mQih3QfbsimqK5quEs16TqkiM5IKTwl2WR4Ho62EJJkcWPQuTuBNoY qC/4+llSqVUdu4t94a4PTYl3555chgrU3DDPF7MKKiGWupd453SjbYLHRawQ6Xw4j4buP4vmCdW 5TNdUHIlI9X+ftB3+bV0BkjvkPB/iB9j9nubWT/r5GXRlTzVKSqU98qUajlcjldmU6uumk4KfaL eCOtNNW4f7oUJU0SN5BWHXyZ9cu8vjGNAgvEbR+7KzZ7tVIGzOcVGqVsnROuj6YdVRYoJ11SLfS v/sdTwc+KC1ic86Gk1JyKjM6hLtZtjAW5luIEHJwQo9HxNzyiyMM+VHDrcYqodVrOzdwP+iLaak NTZt28aDrhfKpTgpNEav8YKRxIOAzD/mXdNEGt1Dpg8yr+EeSjhBYyZfGK43ASE67//z/0+GsUh qTsK+ENjU22FnHbGOUrNg= X-Received: by 2002:a17:902:e787:b0:2c6:afa8:c181 with SMTP id d9443c01a7336-2c725a65917mr32307925ad.1.1781881429487; Fri, 19 Jun 2026 08:03:49 -0700 (PDT) Received: from gmail.com (vps-787f8425.vps.ovh.us. [2604:2dc0:202:300::17a]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c72089a149sm26675655ad.9.2026.06.19.08.03.47 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Fri, 19 Jun 2026 08:03:49 -0700 (PDT) From: Yue Sun To: Greg Kroah-Hartman , Oliver Neukum Cc: Sam Sun , 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 Message-ID: <20260619150340.65058-1-samsun1006219@gmail.com> X-Mailer: git-send-email 2.54.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Sam Sun 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 Closes: https://lore.kernel.org/r/20260618080204.38322-1-samsun1006219@gmail.com Signed-off-by: Sam Sun --- 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 #include #include +#include #include #include #include @@ -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