Received: by 2002:a05:6a10:9e8c:0:0:0:0 with SMTP id y12csp613557pxx; Wed, 28 Oct 2020 12:25:18 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwDK6LoLLmQRdlyhCvqXJV7j1qj5ZYUNmlNun71dMY1B3piT3uG9jQ0veM/Go18RYYr6aKt X-Received: by 2002:a17:906:329b:: with SMTP id 27mr568847ejw.329.1603913118176; Wed, 28 Oct 2020 12:25:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1603913118; cv=none; d=google.com; s=arc-20160816; b=XzvCq9fTycDgCTe8Xa8qtIRbCevkiyc0Li2P3vzvIASt3yZv8V4uYcMqQIkj9RYRZF OQBH0nJEE3Gxv+WZw9+9V8llxbnFksH5jDD1pwhm2gwRjWq/wU20l96/77+lKadDPAJO iUwbQ+7U9xSPb8moiCw/3VriXx/FR+LkKm5YqR1dp/Cxcdstsl7FV6vEXwAW2rZT0jfN 9kheMlsHh3yOuprw/2QpU1XlA9M+cLQzyhUvI6b+TcjzBFtFw0csO3jkpQHYuphUKxzY zCT5KcZ7nKKpUcWgnz4Nr/2udYUQZ9r7Ap0Isiik4ZHkfghcvCSwHVP5iQfY6eaYBqKY ZR4w== 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=2Jdf0mNOZFNWcJV3hbQ6IU9zTiEKpmMQ5BhfPcWOQS0=; b=yJFTvzKi4Cuo+wMkOUz+bMsbDdiZGY+DJCUm71YIGtbBmfq6J3LJVWieo0pGC+Kan9 hmqioBPIgd2y2kahCQ/UH8ln7DhX0k4gBLs7iw84G/QRpDMbadfnZrBUA0Z2t7aTU7pG lcHyFI7wzFf06mPlX8WWlXtJ63WmkccoD1ncUtFOvUa+viqmSDTnRceCNzp2Qg9b0+nX IfbBz5B0AlQCjE2lewFcmJdxzEq1LLuQeF0eX2V2WU3xcq8my9UPEirfD9qYnl9UFDS4 L1a90dWfAC9hRh5biVE50sdC2Fnbqs8WnWUcN/QAAOXIZP3qlYAO7B2RNdxXuKgqOGPR YtHA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=W4fPy8aF; 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 w23si165eds.571.2020.10.28.12.24.54; Wed, 28 Oct 2020 12:25:18 -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=W4fPy8aF; 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 S1827348AbgJ0SXM (ORCPT + 99 others); Tue, 27 Oct 2020 14:23:12 -0400 Received: from mail.kernel.org ([198.145.29.99]:49208 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753700AbgJ0OBc (ORCPT ); Tue, 27 Oct 2020 10:01:32 -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 2D98B2224A; Tue, 27 Oct 2020 14:01:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1603807291; bh=mjwY4YlUglXQYPptEJS3AgT5BsnvI1OXrejNh5lcs1c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=W4fPy8aFTNIprHW36wbsND/IIQIUFP2+7cEnIDX7TUrK6FP8vIzC9qycHzfOdyWxB Xpm0HyJibUeTpxx3hF+bvan3pREpv8pb5Up41D4TchRdfk78KSCHsdr8Zx8UNVbyuc Yqx3dbux51CDAfFwZ01Q7E6LTd+nsmd44PCXwrfY= 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.4 112/112] USB: cdc-wdm: Make wdm_flush() interruptible and add wdm_fsync(). Date: Tue, 27 Oct 2020 14:50:22 +0100 Message-Id: <20201027134905.851280689@linuxfoundation.org> X-Mailer: git-send-email 2.29.1 In-Reply-To: <20201027134900.532249571@linuxfoundation.org> References: <20201027134900.532249571@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 | 72 +++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 17 deletions(-) --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -61,6 +61,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 @@ -151,7 +154,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); } static void wdm_in_callback(struct urb *urb) @@ -382,6 +385,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; @@ -413,6 +419,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; @@ -573,28 +580,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)); - - /* cannot dereference desc->intf if WDM_DISCONNECTING */ + /* + * 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); + + /* + * 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) @@ -719,6 +756,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,