Received: by 2002:a05:6a10:9e8c:0:0:0:0 with SMTP id y12csp193586pxx; Wed, 28 Oct 2020 02:19:45 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyYGYqrpaSg//+ne2/vYvz0JIKEwKXDuqWYoVWgFsh/gu9J8Vfg+0GeNWqudeZLxsejq7GA X-Received: by 2002:a17:906:add3:: with SMTP id lb19mr6195983ejb.130.1603876784763; Wed, 28 Oct 2020 02:19:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1603876784; cv=none; d=google.com; s=arc-20160816; b=VwpTsXfCs7l+C2o1ZWuC9AIWYz9ZS1X/A1B88abOVTJM1zUWcYwcPN6EosmbQlRWFs /IIee+LIl9ECcnnjyHBoZ+bX+677uHWtE7Z/o+DCJHs7WviAWZmSGCkw+PB1WmlUvY1w 9dzao+yx61LpJaFemR4zuVI4OQi69ByRcuURzD2bKniRenlaNC4+kO92HJyAsk8rGyAz WRKWU6LY9hs2Rxv3LhNx3qMSg4CJ/wBwNWcAnrQij+P6IHFM/ntxD5DEjzSaamajBkuZ zgPzJY8argHItypqhyne7dN5Tle5vXZXt4AMRSMKeAHZFTYTgK3AXN88b2OETxiL8VxU Ry1Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=NVYfF4CU3n42MfjQ6g79upPXKXo5aK6DvIOx/F5+mdU=; b=DNpKbqUAuG4H11AeMea0vL7aRnqlLJzGVbmSfnPNx4x4G7vTss6ugn71uKe4+ghL7j WjCTr01mARDsNlMe5DTimwBGcYnn2pLYmE22Z6BVfvI+GyJiSSu1uFPmToTyvza5CRV7 CKuA9jywknwc+jYQppyZ1RThn9xVigLcw95DzWT4r3ae0vW0muOS/ty7XvZsibE/kRtc ORpUv2GjV1C//ISGdQYllXru/mte48JmdwWtKEh0JhvoRbAV6LJRoruP71G3rWA0yBYv ayIrM3aTONNaw3bmW9Wtsa2Ap3Op8b+xkIz4dO9pn4ATvAJx5i9UpCjL19CQKfYaVrn5 MD1g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="d8bp/o5T"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id l12si2820211edk.163.2020.10.28.02.19.22; Wed, 28 Oct 2020 02:19:44 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="d8bp/o5T"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757407AbgJ0OTR (ORCPT + 99 others); Tue, 27 Oct 2020 10:19:17 -0400 Received: from mail.kernel.org ([198.145.29.99]:38576 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757151AbgJ0OQa (ORCPT ); Tue, 27 Oct 2020 10:16:30 -0400 Received: from localhost (83-86-74-64.cable.dynamic.v4.ziggo.nl [83.86.74.64]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 8698C2072D; Tue, 27 Oct 2020 14:16:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1603808190; bh=kNPdSGzpBNQUMJ8Q961fEH9S72mz6FKrkyRrrhmKC7U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=d8bp/o5TfaBYsux08/S8J26I0he7VLMjFUQJ6L+RRSzmXrKn3pi4dgYT18zxcD/em a+vH/rIx0YI7WpxcYioHjBASdzV8oNJ1JfaDcbuQfjSjlWQp4hf4u4tBTvYKfvo3Qr 1j/hLxvWL4rT94ry5CDdlg1QRJ69l8H8QPR72WxY= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, syzbot , Tetsuo Handa , Oliver Neukum , Alan Stern Subject: [PATCH 4.14 189/191] USB: cdc-wdm: Make wdm_flush() interruptible and add wdm_fsync(). Date: Tue, 27 Oct 2020 14:50:44 +0100 Message-Id: <20201027134918.812823123@linuxfoundation.org> X-Mailer: git-send-email 2.29.1 In-Reply-To: <20201027134909.701581493@linuxfoundation.org> References: <20201027134909.701581493@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Oliver Neukum commit 37d2a36394d954413a495da61da1b2a51ecd28ab upstream. syzbot is reporting hung task at wdm_flush() [1], for there is a circular dependency that wdm_flush() from flip_close() for /dev/cdc-wdm0 forever waits for /dev/raw-gadget to be closed while close() for /dev/raw-gadget cannot be called unless close() for /dev/cdc-wdm0 completes. Tetsuo Handa considered that such circular dependency is an usage error [2] which corresponds to an unresponding broken hardware [3]. But Alan Stern responded that we should be prepared for such hardware [4]. Therefore, this patch changes wdm_flush() to use wait_event_interruptible_timeout() which gives up after 30 seconds, for hardware that remains silent must be ignored. The 30 seconds are coming out of thin air. Changing wait_event() to wait_event_interruptible_timeout() makes error reporting from close() syscall less reliable. To compensate it, this patch also implements wdm_fsync() which does not use timeout. Those who want to be very sure that data has gone out to the device are now advised to call fsync(), with a caveat that fsync() can return -EINVAL when running on older kernels which do not implement wdm_fsync(). This patch also fixes three more problems (listed below) found during exhaustive discussion and testing. Since multiple threads can concurrently call wdm_write()/wdm_flush(), we need to use wake_up_all() whenever clearing WDM_IN_USE in order to make sure that all waiters are woken up. Also, error reporting needs to use fetch-and-clear approach in order not to report same error for multiple times. Since wdm_flush() checks WDM_DISCONNECTING, wdm_write() should as well check WDM_DISCONNECTING. In wdm_flush(), since locks are not held, it is not safe to dereference desc->intf after checking that WDM_DISCONNECTING is not set [5]. Thus, remove dev_err() from wdm_flush(). [1] https://syzkaller.appspot.com/bug?id=e7b761593b23eb50855b9ea31e3be5472b711186 [2] https://lkml.kernel.org/r/27b7545e-8f41-10b8-7c02-e35a08eb1611@i-love.sakura.ne.jp [3] https://lkml.kernel.org/r/79ba410f-e0ef-2465-b94f-6b9a4a82adf5@i-love.sakura.ne.jp [4] https://lkml.kernel.org/r/20200530011040.GB12419@rowland.harvard.edu [5] https://lkml.kernel.org/r/c85331fc-874c-6e46-a77f-0ef1dc075308@i-love.sakura.ne.jp Reported-by: syzbot Cc: stable Co-developed-by: Tetsuo Handa Signed-off-by: Tetsuo Handa Signed-off-by: Oliver Neukum Cc: Alan Stern Link: https://lore.kernel.org/r/20200928141755.3476-1-penguin-kernel@I-love.SAKURA.ne.jp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 70 +++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 16 deletions(-) --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -57,6 +57,9 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_MAX 16 +/* we cannot wait forever at flush() */ +#define WDM_FLUSH_TIMEOUT (30 * HZ) + /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ #define WDM_DEFAULT_BUFSIZE 256 @@ -147,7 +150,7 @@ static void wdm_out_callback(struct urb kfree(desc->outbuf); desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); - wake_up(&desc->wait); + wake_up_all(&desc->wait); } /* forward declaration */ @@ -390,6 +393,9 @@ static ssize_t wdm_write if (test_bit(WDM_RESETTING, &desc->flags)) r = -EIO; + if (test_bit(WDM_DISCONNECTING, &desc->flags)) + r = -ENODEV; + if (r < 0) { rv = r; goto out_free_mem_pm; @@ -421,6 +427,7 @@ static ssize_t wdm_write if (rv < 0) { desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); + wake_up_all(&desc->wait); /* for wdm_wait_for_response() */ dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); rv = usb_translate_errors(rv); goto out_free_mem_pm; @@ -580,28 +587,58 @@ err: return rv; } -static int wdm_flush(struct file *file, fl_owner_t id) +static int wdm_wait_for_response(struct file *file, long timeout) { struct wdm_device *desc = file->private_data; + long rv; /* Use long here because (int) MAX_SCHEDULE_TIMEOUT < 0. */ - wait_event(desc->wait, - /* - * needs both flags. We cannot do with one - * because resetting it would cause a race - * with write() yet we need to signal - * a disconnect - */ - !test_bit(WDM_IN_USE, &desc->flags) || - test_bit(WDM_DISCONNECTING, &desc->flags)); + /* + * Needs both flags. We cannot do with one because resetting it would + * cause a race with write() yet we need to signal a disconnect. + */ + rv = wait_event_interruptible_timeout(desc->wait, + !test_bit(WDM_IN_USE, &desc->flags) || + test_bit(WDM_DISCONNECTING, &desc->flags), + timeout); - /* cannot dereference desc->intf if WDM_DISCONNECTING */ + /* + * To report the correct error. This is best effort. + * We are inevitably racing with the hardware. + */ if (test_bit(WDM_DISCONNECTING, &desc->flags)) return -ENODEV; - if (desc->werr < 0) - dev_err(&desc->intf->dev, "Error in flush path: %d\n", - desc->werr); + if (!rv) + return -EIO; + if (rv < 0) + return -EINTR; + + spin_lock_irq(&desc->iuspin); + rv = desc->werr; + desc->werr = 0; + spin_unlock_irq(&desc->iuspin); + + return usb_translate_errors(rv); + +} - return usb_translate_errors(desc->werr); +/* + * You need to send a signal when you react to malicious or defective hardware. + * Also, don't abort when fsync() returned -EINVAL, for older kernels which do + * not implement wdm_flush() will return -EINVAL. + */ +static int wdm_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + return wdm_wait_for_response(file, MAX_SCHEDULE_TIMEOUT); +} + +/* + * Same with wdm_fsync(), except it uses finite timeout in order to react to + * malicious or defective hardware which ceased communication after close() was + * implicitly called due to process termination. + */ +static int wdm_flush(struct file *file, fl_owner_t id) +{ + return wdm_wait_for_response(file, WDM_FLUSH_TIMEOUT); } static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) @@ -726,6 +763,7 @@ static const struct file_operations wdm_ .owner = THIS_MODULE, .read = wdm_read, .write = wdm_write, + .fsync = wdm_fsync, .open = wdm_open, .flush = wdm_flush, .release = wdm_release,