All of lore.kernel.org
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: shaojijie@huawei.com
Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, andrew+netdev@lunn.ch, horms@kernel.org,
	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 net 3/3] net: hns3: fix use-after-free in debugfs read during reset/unload
Date: Sat, 19 Sep 2026 15:37:29 +0000	[thread overview]
Message-ID: <178983224991.22033.11290112238743369174@kernel.org> (raw)
In-Reply-To: <20260915132434.1141742-4-shaojijie@huawei.com>

Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider:

Critical: 0 · High: 1 · Medium: 3 · Low: 1

- [High] On the PF side the new locking is inoperative.
- [Medium] The commit message justifies the change with "The seq_file
  refactoring of hns3 debugfs removed the per-file entry state checks"…
- [Medium] The new device-busy gate is added to only one of the two
  helpers that produce the output of the single `vlan_config` debugfs…
- [Medium] The reader/writer handshake the patch relies on has no
  publication ordering on the reset-reinitialization side.
- [Low] The Fixes: tag cites only eced3d1c41db ("net: hns3: use seq_file
  for files in queue/ in debugfs"), which is the correct origin for the…

--

net: hns3: fix use-after-free in debugfs read during reset/unload

This adds a new mutex (dbg_mutex) to struct hnae3_handle, has the debugfs
show callbacks take it plus a device-busy state check at entry and per
loop iteration, and has the enet teardown/ring-resize paths take it before
freeing rings and vectors. Two hclge (PF layer) debugfs callbacks also
take the same mutex through hdev->vport[0].nic.

Closes: https://lore.kernel.org/all/a0853cd9-cab5-441d-b181-8ba97f2f58b0@huawei.com/

A few questions on the commit message and the code below.

>     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.

[Severity: Medium]
Does this description cover the hclge_debugfs.c part of the patch?  At the
baseline, grepping hclge_debugfs.c for HCLGE_STATE_RST_HANDLING or any
test_bit(HCLGE_STATE...) returns no matches, so there were no per-file
state checks there to be removed by the seq_file conversion.  The two
guarded PF callbacks also do not touch priv->ring, priv->tqp_vector or
ring->desc; they read hdev->* and hdev->vport[] and issue firmware
commands, and hdev->vport is only freed in hclge_uninit_ae_dev().

The selection also looks arbitrary.  Of the roughly fifty
hclge_dbg_dump_* seq_file callbacks registered in hclge_dbg_cmd_func[],
only hclge_dbg_dump_umv_info() and hclge_dbg_dump_vlan_offload_config()
gained the new guard, while structurally identical ones that read
hdev-owned state during reset (hclge_dbg_dump_tm_*, hclge_dbg_dump_fd_*,
hclge_dbg_dump_mng_table, hclge_dbg_dump_mac_uc/mc, the register dumps)
were left alone.

If the PF layer needs this protection, should the rest of the callbacks
get it too?  If it does not, could the hclge_debugfs.c hunks be dropped?

>     Fixes: eced3d1c41db ("net: hns3: use seq_file for files in queue/ in debugfs")

[Severity: Low]
This tag matches the hns3_debugfs.c (enet) change, but the seq_file
conversion of hclge_dbg_dump_umv_info() blames to 9e1545b48818e0
("net: hns3: use seq_file for debugfs in umv_info and vlan_config"-era
sibling commit by Yonglong Liu), not to eced3d1c41db.  Should a second
Fixes: tag be added so the hclge_debugfs.c hunks can be mapped to the
commit that introduced them?

> diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
> index 4286af9239b01..e94cc33da8643 100644
> --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
> +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
> @@ -944,6 +944,7 @@ struct hnae3_handle {
>  
>  	u8 netdev_flags;
>  	struct dentry *hnae3_dbgfs;
> +	struct mutex dbg_mutex;	/* protect debugfs against reset/unload */
>  
>  	/* Network interface message level enabled bits */
>  	u32 msg_enable;
> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
> index 1347edac76993..7d913302f4424 100644
> --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
> @@ -389,6 +389,12 @@ static const char * const dim_state_str[] = { "START", "IN_PROG", "APPLY" };
>  static const char * const
>  dim_tune_stat_str[] = { "ON_TOP", "TIRED", "RIGHT", "LEFT" };
>  
> +static bool hns3_dbg_is_device_busy(struct hns3_nic_priv *priv)
> +{
> +	return !test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
> +	       test_bit(HNS3_NIC_STATE_RESETTING, &priv->state);
> +}
> +

[ ... ]

> @@ -504,22 +524,16 @@ static int hns3_dbg_rx_queue_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;
> -	}
> +	guard(mutex)(&priv->ae_handle->dbg_mutex);
> +	if (hns3_dbg_is_device_busy(priv))
> +		return -EBUSY;

[ ... ]

> @@ -604,9 +612,14 @@ static int hns3_dbg_queue_map(struct seq_file *s, void *data)
>  
>  	seq_puts(s, "local_queue_id  global_queue_id  vector_id\n");
>  
> +	guard(mutex)(&priv->ae_handle->dbg_mutex);
> +	if (hns3_dbg_is_device_busy(priv))
> +		return -EBUSY;
> +
>  	for (i = 0; i < h->kinfo.num_tqps; i++) {
> -		if (!priv->ring || !priv->ring[i].tqp_vector)
> -			continue;
> +		if (hns3_dbg_is_device_busy(priv))
> +			return -EBUSY;
> +
>  		seq_printf(s, "%-16u%-17u%d\n", i,
>  			   h->ae_algo->ops->get_global_queue_id(h, i),
>  			   priv->ring[i].tqp_vector->vector_irq);

[Severity: Medium]
Is the reader/writer handshake ordered on the reinitialization side?

The teardown side does what the changelog describes:

hns3_reset_notify_uninit_enet()
	test_and_clear_bit(HNS3_NIC_STATE_INITED, &priv->state);
	guard(mutex)(&handle->dbg_mutex);
	...
	hns3_put_ring_config(priv);	/* priv->ring = NULL */

But the reinit side never takes dbg_mutex and publishes with a plain
bitop:

hns3_reset_notify_init_enet()
	... rebuilds priv->ring, priv->tqp_vector, descriptors ...
	set_bit(HNS3_NIC_STATE_INITED, &priv->state);

hns3_reset_notify_up_enet()
	clear_bit(HNS3_NIC_STATE_RESETTING, &priv->state);

Non-returning bitops carry no memory barriers
(Documentation/atomic_bitops.txt), and since the initializer never holds
dbg_mutex, the reader's mutex acquisition pairs only with the teardown
release, which stored priv->ring = NULL.  On a weakly ordered machine can
a reader observe INITED set and RESETTING clear while still reading the
stale priv->ring, and then dereference priv->ring[i].tqp_vector here?

The "!priv->ring || !priv->ring[i].tqp_vector" test that used to absorb
that case is being deleted in this hunk, as are the "if (!priv->ring)"
guards in hns3_dbg_rx_queue_info(), hns3_dbg_tx_queue_info() and
hns3_dbg_page_pool_info().  Would either keeping those NULL checks or
having hns3_reset_notify_init_enet() publish under dbg_mutex (or with
release/acquire semantics) close this?

[ ... ]

> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
> index 0c088feae03ca..bae8b32ffc5b8 100644
> --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c

[ ... ]

> @@ -5875,6 +5880,7 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
>  		return 0;
>  	}
>  
> +	guard(mutex)(&handle->dbg_mutex);
>  	hns3_free_rx_cpu_rmap(netdev);
>  	hns3_nic_uninit_irq(priv);
>  	hns3_clear_all_ring(handle, true);
> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
> index 9a4e29bfa1666..1e9d3e08b7ded 100644
> --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
> @@ -15,6 +15,12 @@
>  #define hclge_seq_file_to_hdev(s)	\
>  		(((struct hnae3_ae_dev *)hnae3_seq_file_to_ae_dev(s))->priv)
>  
> +static bool hclge_dbg_is_device_busy(struct hclge_dev *hdev)
> +{
> +	return test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
> +	       test_bit(HCLGE_STATE_RST_FAIL, &hdev->state);
> +}
> +
>  static const char * const hclge_mac_state_str[] = {
>  	"TO_ADD", "TO_DEL", "ACTIVE"
>  };
> @@ -2621,6 +2627,10 @@ static int hclge_dbg_dump_umv_info(struct seq_file *s, void *data)
>  	struct hclge_vport *vport;
>  	u8 i;
>  
> +	guard(mutex)(&hdev->vport[0].nic.dbg_mutex);
> +	if (hclge_dbg_is_device_busy(hdev))
> +		return -EBUSY;
> +

[Severity: High]
Does this mutex exclude anything on the PF side?

Grepping the driver for dbg_mutex after this patch gives:

  hnae3.h:1
  hns3_debugfs.c:6
  hns3_enet.c:6
  hns3_ethtool.c:1
  hns3pf/hclge_debugfs.c:2

hclge_main.c has no matches, so no writer of the state these two
callbacks read ever takes dbg_mutex:

hclge_reset_service_task()
	down(&hdev->reset_sem);
	set_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
	hclge_reset_subtask(hdev);
	clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);

and hclge_reset_err_handle() sets HCLGE_STATE_RST_FAIL the same way.  The
only reset-side acquisition is hns3_reset_notify_uninit_enet(), which
covers priv->ring and priv->tqp_vector, not the UMV counters or the
per-function VLAN offload registers read here.

So isn't hclge_dbg_is_device_busy() here exactly the check-then-use
window the changelog says the mutex removes?  A reader admitted before
RST_HANDLING is set keeps issuing cmdq queries across the reset boundary
and prints values sampled from both sides of it.

Two follow-on questions:

Does the comment on the new struct member, "protect debugfs against
reset/unload", hold for hclge state given the above?

Does taking dbg_mutex and then hdev->vport_lock here create a new lock
order that the writer side is not aware of?

> @@ -2831,6 +2841,10 @@ static int hclge_dbg_dump_vlan_offload_config(struct hclge_dev *hdev,
>  	int ret;
>  	u8 i;
>  
> +	guard(mutex)(&hdev->vport[0].nic.dbg_mutex);
> +	if (hclge_dbg_is_device_busy(hdev))
> +		return -EBUSY;
> +

[Severity: Medium]
Should the gate be in the show callback instead of in this helper?

Both halves of the single vlan_config file run from one callback:

hclge_dbg_dump_vlan_config()
	ret = hclge_dbg_dump_vlan_filter_config(hdev, s);
	if (ret)
		return ret;

	return hclge_dbg_dump_vlan_offload_config(hdev, s);

hclge_dbg_dump_vlan_filter_config() issues
hclge_get_vlan_filter_state() and
hclge_get_port_vlan_filter_bypass_state() for every function and emits
its section with no mutex and no state check, so reading vlan_config
during a reset still runs that half against the resetting device and only
then returns -EBUSY from the second half.  Since seq_read_iter() resets
m->count to the record offset on a negative show() return, the output the
first half produced is discarded and the read fails anyway.

Also, unlike the enet readers, this loop holds dbg_mutex across
2 * (pci_num_vf() + 1) synchronous firmware commands with no
per-iteration busy re-check.  Given that the reset path reaches
guard(mutex)(&handle->dbg_mutex) via

  hclge_reset_rebuild() -> rtnl_lock() -> hclge_reset_stack() ->
  hclge_notify_client(HNAE3_UNINIT_CLIENT) ->
  hns3_reset_notify_uninit_enet()

can a PF debugfs reader stall the reset, and rtnl_lock with it, for the
duration of the dump?  The changelog says the per-iteration check keeps
reset latency bounded, which does not appear to apply to these two PF
callbacks.

>  	seq_puts(s, "FUNC_ID  PVID  ACCEPT_TAG1  ACCEPT_TAG2 ACCEPT_UNTAG1  ");
>  	seq_puts(s, "ACCEPT_UNTAG2  INSERT_TAG1  INSERT_TAG2  SHIFT_TAG  ");
>  	seq_puts(s, "STRIP_TAG1  STRIP_TAG2  DROP_TAG1  DROP_TAG2  ");

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260915132434.1141742-1-shaojijie%40huawei.com

  reply	other threads:[~2026-09-19 15:37 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-15 13:24 [PATCH net 0/3] net: hns3: three fixes for hns3 driver Jijie Shao
2026-09-15 13:24 ` [PATCH net 1/3] net: hns3: fix vector resource leak in hns3_nic_alloc_vector_data Jijie Shao
2026-09-15 13:24 ` [PATCH net 2/3] net: hns3: fix interface stuck after OOM with full hardware rx queue Jijie Shao
2026-09-19 15:37   ` netdev-bot+sashiko
2026-09-15 13:24 ` [PATCH net 3/3] net: hns3: fix use-after-free in debugfs read during reset/unload Jijie Shao
2026-09-19 15:37   ` netdev-bot+sashiko [this message]
2026-09-19 23:47   ` Jakub Kicinski

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=178983224991.22033.11290112238743369174@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=chenhao418@huawei.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuyonglong@huawei.com \
    --cc=netdev@vger.kernel.org \
    --cc=ningwei15@huawei.com \
    --cc=pabeni@redhat.com \
    --cc=shaojijie@huawei.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.