From: Jijie Shao <shaojijie@huawei.com>
To: <netdev-bot+sashiko@kernel.org>
Cc: <shaojijie@huawei.com>, <davem@davemloft.net>,
<edumazet@kernel.org>, <kuba@kernel.org>, <pabeni@redhat.com>,
<andrew+netdev@lunn.ch>, <shenjian15@huawei.com>,
<liuyonglong@huawei.com>, <chenhao418@huawei.com>,
<yangshuaisong@h-partners.com>, <ningwei15@huawei.com>,
<netdev@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 net 3/3] net: hns3: fix use-after-free in debugfs read during reset/unload
Date: Mon, 26 Oct 2026 17:27:07 +0800 [thread overview]
Message-ID: <adf8edb1-d622-4741-aac2-682e4f0ff0b9@huawei.com> (raw)
In-Reply-To: <179293217840.3134.10721469015486402363@kernel.org>
on 2026/10/25 20:42, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider.
>
> Critical: 0 · High: 1 (1) · Medium: 1 (2) · Low: 0
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [High] dbg_mutex is taken only on the teardown paths
> (hns3_reset_notify_uninit_enet, hns3_client_uninit,…
> - [Medium] The Fixes tag and changelog put the whole root cause on
> eced3d1c41db ("net: hns3: use seq_file for files in queue/ in…
>
> Pre-existing issues:
> - [High] This problem wasn't introduced by this patch, but the patch
> rewrites the guard and makes the outcome worse.
> - [Medium] This problem wasn't introduced by this patch, but
> hns3_set_ringparam() reports success for a failed ring resize when the…
> - [Medium] This problem wasn't introduced by this patch, but
> hns3_alloc_ring_memory() leaks a newly created RX page pool when RX…
>
> --
>
> net: hns3: fix use-after-free in debugfs read during reset/unload
>
> This patch adds a dbg_mutex to struct hnae3_handle. The enet-layer
> debugfs show callbacks take it, and so do the reset uninit, client
> uninit and ethtool ring resize paths while they free rings and vectors.
> The debugfs readers also re-check INITED/RESETTING on every loop
> iteration.
>
> Closes: https://lore.kernel.org/all/a0853cd9-cab5-441d-b181-8ba97f2f58b0@huawei.com/
>
>> The seq_file refactoring of hns3 debugfs removed the per-file entry
>> state checks. Without them, debugfs show callbacks can access
>> priv->ring, priv->tqp_vector, and ring->desc while these resources
>> are being freed by reset, driver unload, or ethtool ring resize.
> [ ... ]
>
>> Fixes: eced3d1c41db ("net: hns3: use seq_file for files in queue/ in debugfs")
> [Severity: Medium]
> Is this Fixes tag complete?
>
> Before eced3d1c41db, hns3_dbg_read() did a single INITED/RESETTING test
> before running the dump. It shared no lock with
> hns3_reset_notify_uninit_enet() or hns3_client_uninit. That is the same
> window the changelog describes as "Plain state-bit checks leave a TOCTOU
> window between check and use".
>
> The ring resize race is older still. Since a723fb8efe29d3 ("net: hns3:
> refine for set ring parameters"), hns3_set_ringparam() has freed the old
> descriptors with:
>
> for (i = 0; i < h->kinfo.num_tqps * 2; i++)
> hns3_fini_ring(&tmp_rings[i]);
>
> It never clears HNS3_NIC_STATE_INITED or sets HNS3_NIC_STATE_RESETTING.
> So no state-bit check, old or new, would stop a concurrent tx_bd_info or
> rx_bd_info reader from walking ring->desc. Also, eced3d1c41db only
> converted rx_queue_info and queue_map.
>
> Stable trees that predate eced3d1c41db have the debugfs vs ring resize
> use-after-free. If backports go by this tag alone, would those trees be
> skipped? Should a723fb8efe29d3 get a Fixes tag as well, and should the
> changelog's wording about ethtool ring resize be adjusted?
Agreed. v3 will add the root commit (2019):
Fixes: a723fb8efe29d3 ("net: hns3: refine for set ring parameters")
Fixes: eced3d1c41db ("net: hns3: use seq_file for files in queue/ in debugfs")
>
>> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
>> index 1347edac76993..40d715924e588 100644
>> --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
>> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
> [ ... ]
>
>> @@ -796,13 +823,9 @@ static int hns3_dbg_page_pool_info(struct seq_file *s, void *data)
>> struct hns3_enet_ring *ring;
>> u32 i;
>>
>> - if (!priv->ring) {
>> - dev_err(&h->pdev->dev, "priv->ring is NULL\n");
>> - return -EFAULT;
>> - }
>> -
>> - if (!priv->ring[h->kinfo.num_tqps].page_pool) {
>> - dev_err(&h->pdev->dev, "page pool is not initialized\n");
>> + mutex_lock(&priv->ae_handle->dbg_mutex);
>> + if (!priv->ring || !priv->ring[h->kinfo.num_tqps].page_pool) {
>> + mutex_unlock(&priv->ae_handle->dbg_mutex);
>> return -EFAULT;
>> }
>>
>> @@ -810,14 +833,17 @@ static int hns3_dbg_page_pool_info(struct seq_file *s, void *data)
>> seq_puts(s, "POOL_SIZE(PAGE_NUM) ORDER NUMA_ID MAX_LEN\n");
>>
>> for (i = 0; i < h->kinfo.num_tqps; i++) {
>> - if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
>> - test_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
>> - return -EPERM;
>> + /* Per-iteration check bounds reset latency to one iteration. */
>> + if (hns3_dbg_is_device_busy(priv)) {
>> + mutex_unlock(&priv->ae_handle->dbg_mutex);
>> + return -EBUSY;
>> + }
>>
>> ring = &priv->ring[(u32)(i + h->kinfo.num_tqps)];
>> hns3_dump_page_pool_info(ring, s, i);
> [Severity: High]
> This is a pre-existing issue, but the check above only looks at the
> page_pool of the first RX ring. The loop then passes every RX ring to
> hns3_dump_page_pool_info(), which dereferences ring->page_pool without
> a NULL check:
>
> READ_ONCE(ring->page_pool->pages_state_hold_cnt),
> atomic_read(&ring->page_pool->pages_state_release_cnt),
> ring->page_pool->p.pool_size,
>
> hns3_alloc_page_pool() creates the pool separately for each RX ring. On
> failure it leaves the pool NULL and ring init carries on:
>
> ring->page_pool = page_pool_create(&pp_params);
> if (IS_ERR(ring->page_pool)) {
> dev_warn(...);
> ring->page_pool = NULL;
> }
>
> If the first RX ring got a pool and a later one did not (for example
> after a transient allocation failure), can reading page_pool_info oops on
> the NULL page_pool?
>
> With this patch the oops would happen while dbg_mutex is held. Would
> later calls to hns3_reset_notify_uninit_enet(), hns3_client_uninit() or
> hns3_set_ringparam() then block forever in
> mutex_lock(&handle->dbg_mutex)?
Confirmed. Entry checks only ring 0; loop derefs ring->page_pool
without per-ring NULL check. v3 fix:
for (i = 0; i < h->kinfo.num_tqps; i++) {
if (hns3_dbg_is_device_busy(priv)) { ... return -EBUSY; }
ring = &priv->ring[i + h->kinfo.num_tqps];
if (!ring->page_pool) continue;
hns3_dump_page_pool_info(ring, s, i);
}
>
>> }
>>
>> + mutex_unlock(&priv->ae_handle->dbg_mutex);
>> return 0;
>> }
>>
>> @@ -826,17 +852,23 @@ static int hns3_dbg_bd_info_show(struct seq_file *s, void *private)
>> struct hns3_dbg_data *data = s->private;
>> struct hnae3_handle *h = data->handle;
>> struct hns3_nic_priv *priv = h->priv;
>> + int ret;
>>
>> - if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
>> - test_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
>> - return -EBUSY;
>> + mutex_lock(&priv->ae_handle->dbg_mutex);
>> + if (!priv->ring) {
>> + mutex_unlock(&priv->ae_handle->dbg_mutex);
>> + return -EFAULT;
>> + }
> [Severity: High]
> Can a debugfs reader still dereference a freed priv->ring during reset
> re-init?
>
> hns3_reset_notify_uninit_enet(), hns3_client_uninit() and
> hns3_set_ringparam() all take dbg_mutex, but
> hns3_reset_notify_init_enet() never does. That function publishes a new
> priv->ring while INITED is still clear. Its error unwind then frees
> everything again without the mutex:
>
> hns3_reset_notify_init_enet() {
> ret = hns3_get_ring_config(priv);
> ...
> err_init_irq_fail:
> hns3_uninit_all_ring(priv);
> err_uninit_vector:
> hns3_nic_uninit_vector_data(priv);
> err_dealloc_vector:
> hns3_nic_dealloc_vector_data(priv);
> err_put_ring:
> hns3_put_ring_config(priv);
> ...
> }
>
> The INITED/RESETTING entry check has been replaced with a bare priv->ring
> check. So a reader in this window takes dbg_mutex unopposed and passes
> the check. It then enters hns3_dbg_tx_bd_info() or hns3_dbg_rx_bd_info(),
> and both dereference priv->ring before their first busy check:
>
> ring = &priv->ring[data->qid + data->handle->kinfo.num_tqps];
> for (i = 0; i < ring->desc_num; i++) {
> if (hns3_dbg_is_device_busy(priv))
> return -EBUSY;
>
> If hns3_put_ring_config() runs at the same moment, the ring->desc_num
> load reads devm-freed memory. If priv->ring has already been set back to
> NULL, it faults on NULL plus an offset.
>
> hns3_dbg_page_pool_info() looks to have the same problem, because its
> check indexes priv->ring[h->kinfo.num_tqps] before any busy check.
>
> Before this patch, hns3_dbg_bd_info_show() returned -EBUSY in this
> window because INITED was clear. The changelog says the mutex "rejects a
> reader that arrives after the writer has finished tearing down resources
> (priv->ring is NULL by then)". That doesn't seem to hold once re-init has
> reallocated priv->ring.
Does not hold. Uninit clears INITED before mutex_lock, and init
sets it only after ring is fully ready. Throughout the re-init
window INITED=0 so every reader hits -EBUSY from
hns3_dbg_is_device_busy() before touching priv->ring. The TOCTOU
case (reader holds mutex, writer clears INITED mid-loop) is also
safe: writer's put_ring_config runs after mutex_lock, so ring
stays alive while reader holds it. No change for v3.
>
> Would it work to take dbg_mutex in hns3_reset_notify_init_enet() around
> the allocation, the error unwind and set_bit(HNS3_NIC_STATE_INITED)?
> Alternatively, the busy check could stay ahead of the first priv->ring
> dereference.
>
> [ ... ]
>
>> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
>> index 392653635bda2..cb6a23085be48 100644
>> --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
>> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
>> @@ -1258,6 +1258,7 @@ static int hns3_set_ringparam(struct net_device *ndev,
>> if (if_running)
>> ndev->netdev_ops->ndo_stop(ndev);
>>
>> + mutex_lock(&h->dbg_mutex);
>> hns3_change_all_ring_bd_num(priv, new_ringparam.tx_desc_num,
>> new_ringparam.rx_desc_num);
>> hns3_change_rx_buf_len(ndev, new_ringparam.rx_buf_len);
> [Severity: Medium]
> This is a pre-existing issue, but can the hns3_init_all_ring() call that
> follows here leak an RX page pool when buffer allocation fails?
>
> hns3_alloc_ring_memory() creates the pool and then allocates the
> buffers:
>
> if (!HNAE3_IS_TX_RING(ring)) {
> if (page_pool_enabled)
> hns3_alloc_page_pool(ring);
>
> ret = hns3_alloc_ring_buffers(ring);
> if (ret)
> goto out_with_desc;
> ...
> out_with_desc:
> hns3_free_desc(ring);
> out_with_desc_cb:
> devm_kfree(ring_to_dev(ring), ring->desc_cb);
>
> The error path never calls page_pool_destroy(ring->page_pool). And
> hns3_init_all_ring() only unwinds the rings before the failing one:
>
> for (j = i - 1; j >= 0; j--)
> hns3_fini_ring(&priv->ring[j]);
>
> After that the pointer is lost. hns3_set_ringparam() memcpy()s the saved
> ring over priv->ring[i], and the probe/reset paths free priv->ring via
> hns3_put_ring_config().
>
> Would the page_pool, its ptr_ring and any pages that
> hns3_alloc_ring_buffers() already returned to it be leaked for good?
Confirmed. out_with_desc: skips page_pool_destroy. This only affects
the reset init path (hns3_reset_notify_init_enet) — the failed ring
is not cleaned up by the rollback loop (j = i-1), and
hns3_put_ring_config doesn't call fini_ring either. Probe path is
safe (hns3_uninit_all_ring covers all rings).
v3 will add a separate patch to fix this:
out_with_desc:
if (!HNAE3_IS_TX_RING(ring) && ring->page_pool) {
page_pool_destroy(ring->page_pool);
ring->page_pool = NULL;
}
hns3_free_desc(ring);
>
>> @@ -1276,6 +1277,7 @@ static int hns3_set_ringparam(struct net_device *ndev,
>> for (i = 0; i < h->kinfo.num_tqps * 2; i++)
>> hns3_fini_ring(&tmp_rings[i]);
>> }
>> + mutex_unlock(&h->dbg_mutex);
>>
>> kfree(tmp_rings);
>>
> [Severity: Medium]
> This is a pre-existing issue, but suppose if_running is true and
> hns3_init_all_ring() fails. The revert path restores the old ring
> parameters, and then ret is overwritten just after this point:
>
> if (if_running)
> ret = ndev->netdev_ops->ndo_open(ndev);
>
> return ret;
>
> If the reopen succeeds, does hns3_set_ringparam() return 0 for a resize
> that was rolled back, so that ethnl_set_rings() reports success to
> userspace?
This is unrelated to this bugfix series; no change in v3.
--
pw-bot: cr
next prev parent reply other threads:[~2026-10-26 9:27 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 12:14 [PATCH v2 net 0/3] net: hns3: three fixes for hns3 driver Jijie Shao
2026-10-21 12:14 ` [PATCH v2 net 1/3] net: hns3: fix vector resource leak in hns3_nic_alloc_vector_data Jijie Shao
2026-10-25 12:42 ` netdev-bot+sashiko
2026-10-21 12:14 ` [PATCH v2 net 2/3] net: hns3: fix interface stuck after OOM with full hardware rx queue Jijie Shao
2026-10-25 12:42 ` netdev-bot+sashiko
2026-10-21 12:14 ` [PATCH v2 net 3/3] net: hns3: fix use-after-free in debugfs read during reset/unload Jijie Shao
2026-10-25 12:42 ` netdev-bot+sashiko
2026-10-26 9:27 ` Jijie Shao [this message]
2026-10-21 12:20 ` [PATCH v2 net 0/3] net: hns3: three fixes for hns3 driver netdev-bot+sinfo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=adf8edb1-d622-4741-aac2-682e4f0ff0b9@huawei.com \
--to=shaojijie@huawei.com \
--cc=andrew+netdev@lunn.ch \
--cc=chenhao418@huawei.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=liuyonglong@huawei.com \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=ningwei15@huawei.com \
--cc=pabeni@redhat.com \
--cc=shenjian15@huawei.com \
--cc=yangshuaisong@h-partners.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®