Received: by 2002:a25:d7c1:0:0:0:0:0 with SMTP id o184csp4607176ybg; Mon, 21 Oct 2019 11:30:58 -0700 (PDT) X-Google-Smtp-Source: APXvYqxaiYafx8FMCK4xGuzTnoSOaRnMTrGAs92i4W9CrtDan6njD9cRjlYTwiAHGPhEDN0iwbSD X-Received: by 2002:a17:906:24c9:: with SMTP id f9mr23484114ejb.201.1571682658419; Mon, 21 Oct 2019 11:30:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1571682658; cv=none; d=google.com; s=arc-20160816; b=nW0G3FuGxh4tdHjzFCUO2VFzW96bVnzLdsOJ1gk9tNkIyh0Zp3QmUoKZJAYSgZ2oVw Gb27ym9jUfpBMirsl0+TVvxhaiWjxhHjsV0JWG9ZYuGg731g1auBnPjkZCrUqGuYeVDJ 1cbSunqbCHjDR/7eMGgzeGnp+X7kmipbVg0ZasXsnraNN2G6B16yOSllX2jDV2KVXv54 qzgT2p2PDQGRPCZH3tSVGT1MZTDjNJF3/km5EBcDakc4BaXjXTmh39gHJuPfhWu4xm2T tc8fKdYiiWq4tJ3Woyh9P5Kmq0oBeMYUAf8RP8KF/MPtR32+/hQjtmzaSSA934aajnj+ CWlA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:user-agent:in-reply-to :content-disposition:mime-version:references:message-id:subject:cc :to:from:date; bh=ts+6T0Vx6W3F2XGuWMeDvC9HfhWnz9E3GMgfVXKqPzU=; b=NVV43ns9TPuYoLhm4x6caEDOaShqatmPkyXKMt5yyFx9EJguEvG0qpzm3CbSlgRkkj mtMMqWRZ1MoM4zVAVJbE803YbGAwwgOr+pqeat3Y2AMpLNd6L2rUn1/+tG/th+PuG/Yr VPE8hCkEpLR3sw5P2Up2ldpCALmb8yPGJ0HO8fFpXYOch5uqHnTzhEnkwMjpqKDabt4p GHJWHKC9++x2YMCJn/ESGhbwqx5XYkRM8VbWkrFgckHspf+9rdcuultl+cD/YSDM8weZ I4SRtEzTtBMWYEcA0+zGUubKyjrVw6tMK6JCrS9TLFdE+DHMuMSoHrh/E1IbGBRXjVSR JzVQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b20si9922743edc.359.2019.10.21.11.30.31; Mon, 21 Oct 2019 11:30:58 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729463AbfJUSaS (ORCPT + 99 others); Mon, 21 Oct 2019 14:30:18 -0400 Received: from mail-lj1-f195.google.com ([209.85.208.195]:34605 "EHLO mail-lj1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727110AbfJUSaS (ORCPT ); Mon, 21 Oct 2019 14:30:18 -0400 Received: by mail-lj1-f195.google.com with SMTP id j19so14437258lja.1; Mon, 21 Oct 2019 11:30:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=ts+6T0Vx6W3F2XGuWMeDvC9HfhWnz9E3GMgfVXKqPzU=; b=Xh0vRa1SUTsa5mon0D0odHCTz3qjmUG/i4uECjMHoMdQJ0gxmBaWuCQdoYZ3v+WgfL 9D31h2qMA+jKMawzrqP1ZVPeNr9DrYSt0oqBJJEfySO/jbADutZTYsp0HnOK0RiM+ZeN YZ8Yxc4DCAd/8fvTKFLq9Ag5hjxfRunYPzzh7v+DK/AwCFM43j7ngsk+NK5NzG3qmN1b bKgiuzq2Wq2ZLhJRfQVhv0cfOUCkHJ+9EHmVpwzZG8jSWAkRR/Z0MgWggXYFh3pQ0dw0 BFoysrSEJc4d7vXbRKG/S3JvRrEhIHYm/P4VHkOc0bbYlGHmselAlZSJZhUhNYAaRpnM +35g== X-Gm-Message-State: APjAAAUw/BhVy9Rn+hpTU+hfnX07+TcoCV3hzwjWuLHSFrDS/LI+gDCQ /R9BW5Dz1zu5XdAl1KWbI0k= X-Received: by 2002:a2e:a0d6:: with SMTP id f22mr16159186ljm.81.1571682614838; Mon, 21 Oct 2019 11:30:14 -0700 (PDT) Received: from xi.terra (c-51f1e055.07-184-6d6c6d4.bbcust.telenor.se. [85.224.241.81]) by smtp.gmail.com with ESMTPSA id 77sm7313934ljf.85.2019.10.21.11.30.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 21 Oct 2019 11:30:13 -0700 (PDT) Received: from johan by xi.terra with local (Exim 4.92.2) (envelope-from ) id 1iMcRn-00032x-L2; Mon, 21 Oct 2019 20:30:27 +0200 Date: Mon, 21 Oct 2019 20:30:27 +0200 From: Johan Hovold To: Alan Stern Cc: Johan Hovold , Greg Kroah-Hartman , Oliver Neukum , "Paul E . McKenney" , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, stable Subject: Re: [PATCH RFC v2 2/2] USB: ldusb: fix ring-buffer locking Message-ID: <20191021183027.GN24768@localhost> References: <20191018151955.25135-3-johan@kernel.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.12.2 (2019-09-21) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, Oct 21, 2019 at 11:17:11AM -0400, Alan Stern wrote: > On Fri, 18 Oct 2019, Johan Hovold wrote: > > > The custom ring-buffer implementation was merged without any locking > > whatsoever, but a spinlock was later added by commit 9d33efd9a791 > > ("USB: ldusb bugfix"). > > > > The lock did not cover the loads from the ring-buffer entry after > > determining the buffer was non-empty, nor the update of the tail index > > once the entry had been processed. The former could lead to stale data > > being returned, while the latter could lead to memory corruption on > > sufficiently weakly ordered architectures. > > Let's see if I understand this correctly. > > The completion routine stores a buffer-length value at the location > actual_buffer points to, and it stores the buffer contents themselves > in the immediately following bytes. All this happens while the > dev->rbsl spinlock is held. Right. > Later on the read routine loads a value from *actual_buffer while > holding the spinlock, but drops the spinlock before copying the > immediately following buffer contents to userspace. It doesn't currently hold the spinlock while reading *actual_buffer, only when checking if the ring-buffer is non-empty. The patch below extends the check to cover also the load from *actual_buffer. > Your question is whether the read routine needs to call smp_rmb() after > dropping the spinlock and before doing copy_to_user(), right? Right, or alternatively, if an smp_rmb() after dropping the spinlock and before loading *actual_buffer is needed. > The answer is: No, smp_rmb() isn't needed. All the data stored while > ld_usb_interrupt_in_callback() held the spinlock will be visible to > ld_usb_read() while it holds the spinlock and afterward (assuming the > critical section in ld_usb_read() runs after the critical section in > ld_usb_interrupt_in_callback() -- but you know this is true because of > the value you read from *actual_buffer). Did you mean the value "read from dev->ring_head" (which tells us the ring-buffer has been updated) here? We currently have something like this in ld_usb_read(): spin_lock_irq(&lock); while (head == tail) { spin_unlock(&lock); wait_event(event); spin_lock(&lock); } spin_unlock_irq(&lock); entry = &buffer[tail]; len = *entry; copy_to_user(buf, entry + 1, len); /* update tail */ And without an smp_rmb() after dropping the spinlock, what prevents the load from *entry from being done before the load from head? Nothing, right (the spin_unlock_irq() is only a compiler barrier for later loads)? But that's fine because all stores done by the completion handler under the spinlock would of course be visible at that point. So the current code is fine wrt to copy_to_user(), and only the tail bits below are actually needed. Thanks, Alan! Had myself confused there. Johan > > Fixes: 2824bd250f0b ("[PATCH] USB: add ldusb driver") > > Fixes: 9d33efd9a791 ("USB: ldusb bugfix") > > Cc: stable # 2.6.13 > > Signed-off-by: Johan Hovold > > --- > > drivers/usb/misc/ldusb.c | 15 ++++++++++++--- > > 1 file changed, 12 insertions(+), 3 deletions(-) > > > > diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c > > index 15b5f06fb0b3..6b5843b0071e 100644 > > --- a/drivers/usb/misc/ldusb.c > > +++ b/drivers/usb/misc/ldusb.c > > @@ -477,11 +477,11 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, > > > > spin_lock_irq(&dev->rbsl); > > } > > - spin_unlock_irq(&dev->rbsl); > > > > /* actual_buffer contains actual_length + interrupt_in_buffer */ > > actual_buffer = (size_t *)(dev->ring_buffer + dev->ring_tail * (sizeof(size_t)+dev->interrupt_in_endpoint_size)); > > if (*actual_buffer > dev->interrupt_in_endpoint_size) { > > + spin_unlock_irq(&dev->rbsl); > > retval = -EIO; > > goto unlock_exit; > > } > > @@ -489,17 +489,26 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count, > > if (bytes_to_read < *actual_buffer) > > dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n", > > *actual_buffer-bytes_to_read); > > + spin_unlock_irq(&dev->rbsl); > > + > > + /* > > + * Pairs with spin_unlock_irqrestore() in > > + * ld_usb_interrupt_in_callback() and makes sure the ring-buffer entry > > + * has been updated before copy_to_user(). > > + */ > > + smp_rmb(); > > > > /* copy one interrupt_in_buffer from ring_buffer into userspace */ > > if (copy_to_user(buffer, actual_buffer+1, bytes_to_read)) { > > retval = -EFAULT; > > goto unlock_exit; > > } > > - dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; > > - > > retval = bytes_to_read; > > > > spin_lock_irq(&dev->rbsl); > > + > > + dev->ring_tail = (dev->ring_tail + 1) % ring_buffer_size; > > + > > if (dev->buffer_overflow) { > > dev->buffer_overflow = 0; > > spin_unlock_irq(&dev->rbsl); > > > >