Re: [PATCH] mmc: vub300: defer reset until cmd_mutex is unlocked

Ulf Hansson <[email protected]> Mon, 6 Jul 2026 17:46:55 +0200
Newsgroups org.kernel.vger.linux-mmc,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <CAPx+jO_ZDDvEGueT-7EMHJst7qoZPJr_Gcnx1Qn3+KGpGjd39A@mail.gmail.com>
On Wed, Jun 17, 2026 at 5:23 PM Runyu Xiao <[email protected]> wrote:
>
> vub300_cmndwork_thread() holds cmd_mutex while it sends a command and
> waits for the command response.  If the response wait times out,
> __vub300_command_response() kills the command URBs and then synchronously
> resets the USB device through usb_reset_device().
>
> That reset path re-enters the driver through vub300_pre_reset(), which
> also takes cmd_mutex.  The worker therefore tries to acquire the same
> mutex recursively while it is still holding it from the command path.
>
> This issue was found by our static analysis tool and then manually
> reviewed against the current tree.
>
> The grounded PoC kept the real worker and timeout/reset carrier:
>
>   vub300_cmndwork_thread()
>   __vub300_command_response()
>   usb_lock_device_for_reset()
>   usb_reset_device()
>   vub300_pre_reset()
>
> Lockdep reported the same-task recursive acquisition on cmd_mutex:
>
>   WARNING: possible recursive locking detected
>   ... (&test_vub300.cmd_mutex) ... at: usb_reset_device... [vuln_msv]
>   ... (&test_vub300.cmd_mutex) ... at: vub300_cmndwork_thread+0x12/0x20 [vuln_msv]
>   Workqueue: vub300_cmd_wq vub300_cmndwork_thread [vuln_msv]
>   *** DEADLOCK ***
>
> Return a flag from __vub300_command_response() when the timeout path needs
> a device reset, then perform the reset after vub300_cmndwork_thread() has
> cleared the in-flight command state and dropped cmd_mutex.  The reset is
> still attempted before mmc_request_done(), preserving the existing request
> completion ordering while avoiding the recursive lock.
>
> Fixes: 88095e7b473a ("mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver")
> Cc: [email protected]
> Signed-off-by: Runyu Xiao <[email protected]>

Applied for fixes, thanks!

Kind regards
Uffe


> ---
>  drivers/mmc/host/vub300.c | 26 +++++++++++++++++---------
>  1 file changed, 17 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
> index ff49d0770506..1c335e070741 100644
> --- a/drivers/mmc/host/vub300.c
> +++ b/drivers/mmc/host/vub300.c
> @@ -1583,7 +1583,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300,
>         return linear_length;
>  }
>
> -static void __vub300_command_response(struct vub300_mmc_host *vub300,
> +static bool __vub300_command_response(struct vub300_mmc_host *vub300,
>                                       struct mmc_command *cmd,
>                                       struct mmc_data *data, int data_length)
>  {
> @@ -1595,17 +1595,11 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
>                                             msecs_to_jiffies(msec_timeout));
>         if (respretval == 0) { /* TIMED OUT */
>                 /* we don't know which of "out" and "res" if any failed */
> -               int result;
>                 vub300->usb_timed_out = 1;
>                 usb_kill_urb(vub300->command_out_urb);
>                 usb_kill_urb(vub300->command_res_urb);
>                 cmd->error = -ETIMEDOUT;
> -               result = usb_lock_device_for_reset(vub300->udev,
> -                                                  vub300->interface);
> -               if (result == 0) {
> -                       result = usb_reset_device(vub300->udev);
> -                       usb_unlock_device(vub300->udev);
> -               }
> +               return true;
>         } else if (respretval < 0) {
>                 /* we don't know which of "out" and "res" if any failed */
>                 usb_kill_urb(vub300->command_out_urb);
> @@ -1701,6 +1695,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
>         } else {
>                 cmd->error = -EINVAL;
>         }
> +
> +       return false;
>  }
>
>  static void construct_request_response(struct vub300_mmc_host *vub300,
> @@ -1746,6 +1742,7 @@ static void vub300_cmndwork_thread(struct work_struct *work)
>                 struct mmc_request *req = vub300->req;
>                 struct mmc_command *cmd = vub300->cmd;
>                 struct mmc_data *data = vub300->data;
> +               bool reset_device;
>                 int data_length;
>                 mutex_lock(&vub300->cmd_mutex);
>                 init_completion(&vub300->command_complete);
> @@ -1768,7 +1765,8 @@ static void vub300_cmndwork_thread(struct work_struct *work)
>                         data_length = __command_read_data(vub300, cmd, data);
>                 else
>                         data_length = __command_write_data(vub300, cmd, data);
> -               __vub300_command_response(vub300, cmd, data, data_length);
> +               reset_device = __vub300_command_response(vub300, cmd,
> +                                                        data, data_length);
>                 vub300->req = NULL;
>                 vub300->cmd = NULL;
>                 vub300->data = NULL;
> @@ -1776,6 +1774,16 @@ static void vub300_cmndwork_thread(struct work_struct *work)
>                         if (cmd->error == -ENOMEDIUM)
>                                 check_vub300_port_status(vub300);
>                         mutex_unlock(&vub300->cmd_mutex);
> +                       if (reset_device) {
> +                               int result;
> +
> +                               result = usb_lock_device_for_reset(vub300->udev,
> +                                                                  vub300->interface);
> +                               if (result == 0) {
> +                                       result = usb_reset_device(vub300->udev);
> +                                       usb_unlock_device(vub300->udev);
> +                               }
> +                       }
>                         mmc_request_done(vub300->mmc, req);
>                         kref_put(&vub300->kref, vub300_delete);
>                         return;
> --
> 2.34.1