From: netdev-bot+sashiko@kernel.org
To: sinyavin@gmail.com
Cc: stephan@gerhold.net, stephan.gerhold@linaro.org,
loic.poulain@oss.qualcomm.com, ryazanov.s.a@gmail.com,
johannes@sipsolutions.net, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, linux-arm-msm@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [net,v2] net: wwan: qcom_bam_dmux: account network packets
Date: Wed, 02 Sep 2026 21:11:22 +0000 [thread overview]
Message-ID: <178838348238.3394541.9061693826070810058@kernel.org> (raw)
In-Reply-To: <20260831181006.1382372-1-sinyavin@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 0 (1) · Low: 0 (1)
- [High] Use-after-free write of the netdevice's core-allocated per-CPU
statistics from the asynchronous BAM TX completion callback.
Pre-existing issues:
- [Medium] The driver allocates netdevs with alloc_netdev() in
bam_dmux_register_netdev_work() but bam_dmux_remove() only calls…
- [Low] Drop paths remain uncounted after the patch: the `drop:` label in
bam_dmux_netdev_start_xmit() (drivers/net/wwan/qcom_bam_dmux.c,…
--
net: wwan: qcom_bam_dmux: account network packets
The patch makes the BAM-DMUX driver maintain rx/tx packet and byte counters
using the core per-CPU software statistics (NETDEV_PCPU_STAT_TSTATS plus
dev_get_tstats64()). TX is accounted in the DMA completion callback and RX
after the BAM-DMUX header and padding are stripped.
Link: https://lore.kernel.org/netdev/20260830085400.2542956-1-sinyavin@gmail.com/ (v1)
> diff --git a/drivers/net/wwan/qcom_bam_dmux.c b/drivers/net/wwan/qcom_bam_dmux.c
> index cc6ace8d6437..5bcac2ccaabd 100644
> --- a/drivers/net/wwan/qcom_bam_dmux.c
> +++ b/drivers/net/wwan/qcom_bam_dmux.c
> @@ -177,8 +177,12 @@ static void bam_dmux_tx_callback(void *data)
> {
> struct bam_dmux_skb_dma *skb_dma = data;
> struct sk_buff *skb = skb_dma->skb;
> + struct net_device *netdev = skb->dev;
>
> bam_dmux_tx_done(skb_dma);
> + if (netdev)
> + dev_sw_netstats_tx_add(netdev, 1,
> + ((struct bam_dmux_hdr *)skb->data)->len);
> dev_consume_skb_any(skb);
> }
[Severity: High]
Can this write to freed per-CPU memory? bam_dmux_tx_callback() runs from
the BAM dmaengine completion (vchan tasklet), and skb->dev is not a
counted reference. dev_sw_netstats_tx_add() then does:
include/linux/netdevice.h:dev_sw_netstats_tx_add() {
struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
u64_stats_add(&tstats->tx_bytes, len);
...
}
With dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS, dev->tstats is
allocated by register_netdevice() and released at unregistration time,
independent of needs_free_netdev/free_netdev:
net/core/dev.c:netdev_run_todo() {
...
netdev_do_free_pcpu_stats(dev);
if (dev->priv_destructor)
dev->priv_destructor(dev);
if (dev->needs_free_netdev)
free_netdev(dev);
...
}
In bam_dmux_remove() the TX channel is still armed when that happens:
drivers/net/wwan/qcom_bam_dmux.c:bam_dmux_remove() {
...
unregister_netdevice_many(&list);
rtnl_unlock(); /* netdev_run_todo() frees tstats */
cancel_work_sync(&dmux->tx_wakeup_work);
...
if (!wait_event_timeout(dmux->pc_wait, !dmux->rx, BAM_DMUX_REMOTE_TIMEOUT))
...
disable_irq(dmux->pc_irq);
bam_dmux_power_off(dmux); /* dmaengine_terminate_sync(dmux->tx) */
bam_dmux_free_skbs(dmux->tx_skbs, DMA_TO_DEVICE);
}
The only synchronous TX quiesce point is dmaengine_terminate_sync(dmux->tx)
inside bam_dmux_power_off(), which is reached after rtnl_unlock(),
cancel_work_sync(), the pm_runtime teardown and a wait_event_timeout() of
up to BAM_DMUX_REMOTE_TIMEOUT. bam_dmux_netdev_stop() only does
netif_stop_queue() plus a CLOSE command, so it neither terminates nor
drains outstanding descriptors:
drivers/net/wwan/qcom_bam_dmux.c:bam_dmux_netdev_stop() {
netif_stop_queue(netdev);
bam_dmux_send_cmd(bndev, BAM_DMUX_CMD_CLOSE);
return 0;
}
Does any TX descriptor still in flight during that window end up doing
this_cpu_ptr() on an already freed (possibly recycled) tstats pointer?
The "if (netdev)" test only filters the driver's own command skbs, which
come from alloc_skb() and leave skb->dev NULL, so it does not say anything
about registration state.
Would accounting TX in bam_dmux_netdev_start_xmit(), where the netdev is
provably live and hdr->len is already known, avoid this? Alternatively,
would terminating/draining the DMA channels and clearing dmux->netdevs[]
before unregister_netdevice_many() close the window?
[ ... ]
> @@ -421,6 +426,7 @@ static void bam_dmux_netdev_setup(struct net_device *dev)
> dev->needed_headroom = sizeof(struct bam_dmux_hdr);
> dev->needed_tailroom = sizeof(u32); /* word-aligned */
> dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
> + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
[Severity: Medium]
This is a pre-existing issue and not introduced by this patch, but it
interacts with the new stats memory, so it is worth mentioning here.
bam_dmux_register_netdev_work() allocates the netdev with alloc_netdev(),
bam_dmux_netdev_setup() never sets dev->needs_free_netdev, and
bam_dmux_remove() only calls unregister_netdevice_queue() /
unregister_netdevice_many() with no free_netdev().
net/core/dev.c:netdev_run_todo() {
...
if (dev->needs_free_netdev)
free_netdev(dev);
cnt++;
/* Free network device */
kobject_put(&dev->dev.kobj);
...
}
The kobject_put() there only balances the kobject_get() taken in
netdev_unregister_kobject(), so the device_initialize() reference is never
dropped and netdev_release() (kvfree(dev)) never runs. Does this leak the
struct net_device, its private data and its queue arrays on every driver
removal?
Would setting dev->needs_free_netdev = true here (next to the new
pcpu_stat_type assignment), or pairing the alloc_netdev() with an explicit
free_netdev() in bam_dmux_remove(), be the right fix?
Note also that the still-allocated, still-readable net_device is what makes
the freed dev->tstats access above silent rather than an obvious fault.
> @@ -533,6 +539,7 @@ static void bam_dmux_cmd_data(struct bam_dmux_skb_dma *skb_dma)
> break;
> }
>
> + dev_sw_netstats_rx_add(netdev, skb->len);
> netif_receive_skb(skb);
> }
[Severity: Low]
This is a pre-existing gap rather than something this patch introduces, but
now that rx/tx counters become non-zero the missing drop counters are more
visible.
The TX error path frees the skb and reports success without any
tx_dropped/tx_errors accounting:
drivers/net/wwan/qcom_bam_dmux.c:bam_dmux_netdev_start_xmit() {
...
drop:
bam_dmux_tx_done(skb_dma);
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
and the RX early returns are not counted either:
drivers/net/wwan/qcom_bam_dmux.c:bam_dmux_cmd_data() {
...
if (!netdev || !netif_running(netdev)) {
dev_warn(dmux->dev, "Data for inactive channel %u\n", hdr->ch);
return;
}
if (hdr->len > BAM_DMUX_MAX_DATA_SIZE) {
...
return;
}
...
}
Would it make sense to also bump rx_dropped/tx_dropped on these paths, so
that ip -s link can explain the loss instead of showing traffic counters
with permanently zero drops?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831181006.1382372-1-sinyavin%40gmail.com
next prev parent reply other threads:[~2026-09-02 21:11 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 18:10 [PATCH net v2] net: wwan: qcom_bam_dmux: account network packets Dmitry Sinyavin
2026-09-01 8:08 ` Loic Poulain
2026-09-02 12:43 ` Dmitry Sinyavin
2026-09-02 21:11 ` netdev-bot+sashiko [this message]
2026-09-03 8:24 ` [net,v2] " Dmitry Sinyavin
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=178838348238.3394541.9061693826070810058@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=johannes@sipsolutions.net \
--cc=kuba@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=loic.poulain@oss.qualcomm.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=ryazanov.s.a@gmail.com \
--cc=sinyavin@gmail.com \
--cc=stephan.gerhold@linaro.org \
--cc=stephan@gerhold.net \
/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.