Message ID | 20230212145238.123055-2-pchelkin@ispras.ru |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp1948290wrn; Sun, 12 Feb 2023 07:41:46 -0800 (PST) X-Google-Smtp-Source: AK7set83/xMzDqh2qXEWoSbckcROmjf79k8cv6rD/Mt+1WXZ2eNK8niICC/v00U90+g6VyenB84V X-Received: by 2002:a05:6a20:430a:b0:c5:d784:a20b with SMTP id h10-20020a056a20430a00b000c5d784a20bmr1553710pzk.29.1676216505947; Sun, 12 Feb 2023 07:41:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676216505; cv=none; d=google.com; s=arc-20160816; b=SoSBFTRKVBjPUjWL/ywuv81eJYY0Z8WMsZkZWorBazaPRQUmeDsxTcYeMpx8/XIzy9 ur3sRj4jXhhQ/XmtUjPJBA3bMSbk9zCoJaawUc27WBY+zF4SvXtFymyDy44uihB7H08T hqLiSC71UsApGHR9t48JuZ2PR61veOjRWWEENoUoUANo9siOhLNbqEoqgwuKcC4XsA98 ZLdPjab98xlAnn2/N5jQGIqcIwrh0WUdAT+hquDXyj5nlkfCsdTHDMOaOmo2S/3xrKUe sYVjn49nte9i4ib0wq9iJXc26i/iIULQwUFjzof0//XxVt401OQaPO2uw1NKCVZcUi8T YdNw== 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 :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature:dkim-filter; bh=UAcNy4s3ciQEP3RErmHWu+rLoVRfErmjChDp+m1b2o4=; b=c9rtpMKewMaJaZzLmXy3HwDiZZ1TyuqXzX/vce/uypc/On59V6PCKy8NPzXqXLTvGG 0caqJU/UFiAqYU+draJkHSogErlcxdP++OK8rddaZpvILI7Bzy2XppuMNbF25OJ6YhKs F/q83YblxRhwCgy+abBronEndR7LswQaqLzLy+niqCjTSx5XT6tb4cX8G5qPCxIDidPJ MqsTogyYu1tGU75MjfbEd5xdr+Y3NSq/fzyAhgdasY9DkrmK75m84+0uJh87R3cLGhi8 zUSUbLA7EgEw14exi5lRd1V8KkiarR5jxT9UhwJ/NZ4bbiQvpHpaoHXKRW4vz7IhXmEa 4Wng== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@ispras.ru header.s=default header.b=O+VjZMC4; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=ispras.ru Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id f26-20020a63755a000000b004fb91be4643si2467215pgn.179.2023.02.12.07.41.33; Sun, 12 Feb 2023 07:41:45 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@ispras.ru header.s=default header.b=O+VjZMC4; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=ispras.ru Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229680AbjBLOxG (ORCPT <rfc822;tebrre53rla2o@gmail.com> + 99 others); Sun, 12 Feb 2023 09:53:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52974 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229679AbjBLOxD (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Sun, 12 Feb 2023 09:53:03 -0500 Received: from mail.ispras.ru (mail.ispras.ru [83.149.199.84]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C166F11E8F; Sun, 12 Feb 2023 06:53:01 -0800 (PST) Received: from fpc.. (unknown [46.242.14.200]) by mail.ispras.ru (Postfix) with ESMTPSA id 0730D44C101D; Sun, 12 Feb 2023 14:53:00 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.ispras.ru 0730D44C101D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ispras.ru; s=default; t=1676213580; bh=UAcNy4s3ciQEP3RErmHWu+rLoVRfErmjChDp+m1b2o4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=O+VjZMC49qlkZexnhD4yW9hADORqhf5tfgncJUuHBa2rcYCSUcuxyScRpertiGcAr tab3kdwH7MrvqCkW3ofbxS/ts5nyUzDpTfwJ/LO9QbxLPWtNflR/jpBIy3GDh40ZKK unEbzBqH8Fs3m7503RjagPPO6Vl+rfx2LFrRFo0E= From: Fedor Pchelkin <pchelkin@ispras.ru> To: =?utf-8?q?Toke_H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk> Cc: Fedor Pchelkin <pchelkin@ispras.ru>, Kalle Valo <kvalo@kernel.org>, "David S. Miller" <davem@davemloft.net>, Eric Dumazet <edumazet@google.com>, Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>, "John W. Linville" <linville@tuxdriver.com>, Vasanthakumar Thiagarajan <vasanth@atheros.com>, Senthil Balasubramanian <senthilkumar@atheros.com>, Sujith <Sujith.Manoharan@atheros.com>, linux-wireless@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexey Khoroshilov <khoroshilov@ispras.ru>, lvc-project@linuxtesting.org Subject: [PATCH 1/1] wifi: ath9k: hif_usb: fix memory leak of remain_skbs Date: Sun, 12 Feb 2023 17:52:38 +0300 Message-Id: <20230212145238.123055-2-pchelkin@ispras.ru> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230212145238.123055-1-pchelkin@ispras.ru> References: <20230212145238.123055-1-pchelkin@ispras.ru> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=1.2 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_SBL_CSS,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=3.4.6 X-Spam-Level: * X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1757638334074756081?= X-GMAIL-MSGID: =?utf-8?q?1757640399003498673?= |
Series |
wifi: ath9k: hif_usb: fix memory leak of remain_skbs
|
|
Commit Message
Fedor Pchelkin
Feb. 12, 2023, 2:52 p.m. UTC
hif_dev->remain_skb is allocated and used exclusively in ath9k_hif_usb_rx_stream(). It is implied that an allocated remain_skb is processed and subsequently freed (in error paths) only during the next call of ath9k_hif_usb_rx_stream(). So, if the device is deinitialized between those two calls or if the skb contents are incorrect, it is possible that ath9k_hif_usb_rx_stream() is not called next time and the allocated remain_skb is leaked. Our local Syzkaller instance was able to trigger that. Fix the leak by introducing a function to explicitly free remain_skb (if it is not NULL) when the device is being deinitialized. remain_skb is NULL when it has not been allocated at all (hif_dev struct is kzalloced) or when it has been proccesed in next call to ath9k_hif_usb_rx_stream(). Proper spinlocks are held to prevent possible concurrent access to remain_skb from the interrupt context ath9k_hif_usb_rx_stream(). These accesses should not happen as rx_urbs have been deallocated before but it prevents a dangerous race condition in these cases. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru> Signed-off-by: Alexey Khoroshilov <khoroshilov@ispras.ru> --- drivers/net/wireless/ath/ath9k/hif_usb.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
Comments
Fedor Pchelkin <pchelkin@ispras.ru> writes: > hif_dev->remain_skb is allocated and used exclusively in > ath9k_hif_usb_rx_stream(). It is implied that an allocated remain_skb is > processed and subsequently freed (in error paths) only during the next > call of ath9k_hif_usb_rx_stream(). > > So, if the device is deinitialized between those two calls or if the skb > contents are incorrect, it is possible that ath9k_hif_usb_rx_stream() is > not called next time and the allocated remain_skb is leaked. Our local > Syzkaller instance was able to trigger that. > > Fix the leak by introducing a function to explicitly free remain_skb (if > it is not NULL) when the device is being deinitialized. remain_skb is NULL > when it has not been allocated at all (hif_dev struct is kzalloced) or > when it has been proccesed in next call to ath9k_hif_usb_rx_stream(). > > Proper spinlocks are held to prevent possible concurrent access to > remain_skb from the interrupt context ath9k_hif_usb_rx_stream(). These > accesses should not happen as rx_urbs have been deallocated before but > it prevents a dangerous race condition in these cases. > > Found by Linux Verification Center (linuxtesting.org) with Syzkaller. > > Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") > Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru> > Signed-off-by: Alexey Khoroshilov <khoroshilov@ispras.ru> > --- > drivers/net/wireless/ath/ath9k/hif_usb.c | 18 ++++++++++++++++++ > 1 file changed, 18 insertions(+) > > diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c > index f521dfa2f194..e03ab972edf7 100644 > --- a/drivers/net/wireless/ath/ath9k/hif_usb.c > +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c > @@ -534,6 +534,23 @@ static struct ath9k_htc_hif hif_usb = { > .send = hif_usb_send, > }; > > +/* Need to free remain_skb allocated in ath9k_hif_usb_rx_stream > + * in case ath9k_hif_usb_rx_stream wasn't called next time to > + * process the buffer and subsequently free it. > + */ > +static void ath9k_hif_usb_free_rx_remain_skb(struct hif_device_usb *hif_dev) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&hif_dev->rx_lock, flags); > + if (hif_dev->remain_skb) { > + dev_kfree_skb_any(hif_dev->remain_skb); > + hif_dev->remain_skb = NULL; > + hif_dev->rx_remain_len = 0; > + } > + spin_unlock_irqrestore(&hif_dev->rx_lock, flags); > +} > + > static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, > struct sk_buff *skb) > { > @@ -1129,6 +1146,7 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) > static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) > { > ath9k_hif_usb_dealloc_urbs(hif_dev); > + ath9k_hif_usb_free_rx_remain_skb(hif_dev); > } Erm, does this actually fix the leak? AFAICT, ath9k_hif_usb_dev_deinit() is only called on the error path of ath9k_hif_usb_firmware_cb(), not when the device is subsequently torn down in ath9k_htc_disconnect_device()? I think the right place to put this is probably inside ath9k_hif_usb_dealloc_urbs()? That gets called on USB suspend as well, but it seems to me that if we're suspending the device to an extent that we're deallocating the urbs, we should be clearing out the cached skb in remain_skb anyway? -Toke
On 16.02.2023 19:15, Toke Høiland-Jørgensen wrote: > Erm, does this actually fix the leak? AFAICT, ath9k_hif_usb_dev_deinit() > is only called on the error path of ath9k_hif_usb_firmware_cb(), not > when the device is subsequently torn down in > ath9k_htc_disconnect_device()? ath9k_hif_usb_dev_deinit() is also called inside ath9k_hif_usb_disconnect(). I see it to be the only place wherehif_dev is freed (apart from an early error path), so the current patchimplementation actually fixes the leak. However, as you have noticed, itis not probably the best place to put the deallocation: we need to clearthe cached skb not only when freeing the device but in urbs deallocationcase, too - in order to avoid its irrelevant processing later. > I think the right place to put this is probably inside > ath9k_hif_usb_dealloc_urbs()? That gets called on USB suspend as well, > but it seems to me that if we're suspending the device to an extent that > we're deallocating the urbs, we should be clearing out the cached skb in > remain_skb anyway? > > -Toke Thank you for the advice! As I can see, remain_skb makes sense when receiving two consecutive urbs which are logically linked together, i.e. a specific data field from the first skb indicates a cached skb to be allocated, memcpy'd with some data and subsequently processed in the next call to rx callback (see 6ce708f54cc8 ("ath9k: Fix out-of-bound memcpy in ath9k_hif_usb_rx_stream")). Urbs deallocation, I suppose, makes that link irrelevant. So I agree with you that remain_skb freeing should be done when deallocating the urbs. I would just place that specifically into ath9k_hif_usb_dealloc_rx_urbs() as remain_skb is associated with rx urbs. RX_STAT_INC(hif_dev, skb_dropped), I think, should be also called when freeing afilled remain_skb? --- Regards, Fedor
Fedor Pchelkin <pchelkin@ispras.ru> writes: > On 16.02.2023 19:15, Toke Høiland-Jørgensen wrote: > > Erm, does this actually fix the leak? AFAICT, ath9k_hif_usb_dev_deinit() > > is only called on the error path of ath9k_hif_usb_firmware_cb(), not > > when the device is subsequently torn down in > > ath9k_htc_disconnect_device()? > > ath9k_hif_usb_dev_deinit() is also called inside > ath9k_hif_usb_disconnect(). No it's not, as of: f099c5c9e2ba ("wifi: ath9k: Fix use-after-free in ath9k_hif_usb_disconnect()") I guess you're looking at an older tree? Please base your patches on an up-to-date ath-next tree. > I see it to be the only place wherehif_dev is freed (apart from an > early error path), so the current patchimplementation actually fixes > the leak. However, as you have noticed, itis not probably the best > place to put the deallocation: we need to clearthe cached skb not only > when freeing the device but in urbs deallocationcase, too - in order > to avoid its irrelevant processing later. > > > I think the right place to put this is probably inside > > ath9k_hif_usb_dealloc_urbs()? That gets called on USB suspend as well, > > but it seems to me that if we're suspending the device to an extent that > > we're deallocating the urbs, we should be clearing out the cached skb in > > remain_skb anyway? > > > > -Toke > > Thank you for the advice! As I can see, remain_skb makes sense when > receiving two consecutive urbs which are logically linked together, i.e. > a specific data field from the first skb indicates a cached skb to be > allocated, memcpy'd with some data and subsequently processed in the > next call to rx callback (see 6ce708f54cc8 ("ath9k: Fix out-of-bound > memcpy in ath9k_hif_usb_rx_stream")). Urbs deallocation, I suppose, > makes that link irrelevant. > > So I agree with you that remain_skb freeing should be done when > deallocating the urbs. I would just place that specifically into > ath9k_hif_usb_dealloc_rx_urbs() as remain_skb is associated with rx > urbs. SGTM. > RX_STAT_INC(hif_dev, skb_dropped), I think, should be also called when > freeing afilled remain_skb? Well, if this is mostly something that happens if the device is going away I'm not sure that anyone will actually see that; but I suppose if it happens on suspend, the stat increase may be useful, and it shouldn't hurt otherwise, so sure, let's add that :) -Toke
On 16.02.2023 21:05, Toke Høiland-Jørgensen wrote: > Fedor Pchelkin <pchelkin@ispras.ru> writes: > >> On 16.02.2023 19:15, Toke Høiland-Jørgensen wrote: >> > Erm, does this actually fix the leak? AFAICT, ath9k_hif_usb_dev_deinit() >> > is only called on the error path of ath9k_hif_usb_firmware_cb(), not >> > when the device is subsequently torn down in >> > ath9k_htc_disconnect_device()? >> >> ath9k_hif_usb_dev_deinit() is also called inside >> ath9k_hif_usb_disconnect(). > No it's not, as of: > > f099c5c9e2ba ("wifi: ath9k: Fix use-after-free in ath9k_hif_usb_disconnect()") > > I guess you're looking at an older tree? Please base your patches on an > up-to-date ath-next tree. > Oops, that's my fault, I indeed patched the wrong tree. Thanks for clarifying!
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index f521dfa2f194..e03ab972edf7 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -534,6 +534,23 @@ static struct ath9k_htc_hif hif_usb = { .send = hif_usb_send, }; +/* Need to free remain_skb allocated in ath9k_hif_usb_rx_stream + * in case ath9k_hif_usb_rx_stream wasn't called next time to + * process the buffer and subsequently free it. + */ +static void ath9k_hif_usb_free_rx_remain_skb(struct hif_device_usb *hif_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&hif_dev->rx_lock, flags); + if (hif_dev->remain_skb) { + dev_kfree_skb_any(hif_dev->remain_skb); + hif_dev->remain_skb = NULL; + hif_dev->rx_remain_len = 0; + } + spin_unlock_irqrestore(&hif_dev->rx_lock, flags); +} + static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, struct sk_buff *skb) { @@ -1129,6 +1146,7 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) { ath9k_hif_usb_dealloc_urbs(hif_dev); + ath9k_hif_usb_free_rx_remain_skb(hif_dev); } /*