From: sashiko-bot@kernel.org
To: "Marc Kleine-Budde" <mkl@pengutronix.de>
Cc: "Oleksij Rempel" <o.rempel@pengutronix.de>,
"Vincent Mailhol" <mailhol@kernel.org>,
"Oliver Hartkopp" <socketcan@hartkopp.net>,
linux-can@vger.kernel.org,
"Marc Kleine-Budde" <mkl@pengutronix.de>
Subject: Re: [PATCH can-next] can: m_can: switch to rx-offload implementation
Date: Thu, 09 Jul 2026 15:45:14 +0000 [thread overview]
Message-ID: <20260709154514.846831F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260709-m_can-rx-offload-v1-1-af3efa8e4272@pengutronix.de>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] Double counting of received packets and bytes, alongside a data race on network statistics between HardIRQ and SoftIRQ contexts.
- [High] Processing RX frames directly in hardirq with a strict, non-replenishable quota can leave packets stranded indefinitely in the hardware FIFO.
- [Low] The napi_struct is no longer used but was left in struct m_can_classdev.
Pre-existing issues:
- [High] The driver teardown path uses the non-blocking `hrtimer_try_to_cancel()` to stop the polling/coalescing timer, leading to unclocked hardware accesses and Use-After-Free vulnerabilities.
- [High] The teardown sequence in m_can_close leaks socket buffers and creates a guaranteed NULL pointer dereference when flushing the transmission workqueue.
- [High] Uninitialized stack variable `frame_len` corrupts Byte Queue Limits (BQL) accounting.
--
--- Patch [1]: [PATCH can-next] can: m_can: switch to rx-offload implementation ---
commit 7f375f1ac288dad1360ddf2dd262a4c708abc626
Author: Marc Kleine-Budde <mkl@pengutronix.de>
can: m_can: switch to rx-offload implementation
This patch converts the m_can driver to unconditionally use rx-offload
for all devices, calling m_can_rx_handler() directly from the IRQ
handler instead of using NAPI.
> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> index eb856547ae7df..8175b030257b4 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -530,26 +530,17 @@ static void m_can_clean(struct net_device *net)
[ ... ]
> static void m_can_receive_skb(struct m_can_classdev *cdev,
> struct sk_buff *skb,
> u32 timestamp)
> {
> - if (cdev->is_peripheral) {
> - struct net_device_stats *stats = &cdev->net->stats;
> - int err;
> + struct net_device_stats *stats = &cdev->net->stats;
> + int err;
>
> - err = can_rx_offload_queue_timestamp(&cdev->offload, skb,
> - timestamp);
> - if (err)
> - stats->rx_fifo_errors++;
> - } else {
> - netif_receive_skb(skb);
> - }
> + err = can_rx_offload_queue_timestamp(&cdev->offload, skb,
> + timestamp);
[Severity: High]
Does this change introduce a data race and double counting for network stats?
When the mmio devices are switched to use rx-offload, m_can_read_fifo()
manually increments the stats in hardirq context before calling
m_can_receive_skb():
m_can_read_fifo()
stats->rx_bytes += cf->len;
stats->rx_packets++;
m_can_receive_skb(..., skb, ...);
The SKB is then queued to the offload queue. When the rx-offload NAPI poll
runs in softirq context, it unconditionally increments the exact same
counters again:
can_rx_offload_napi_poll()
stats->rx_packets++;
stats->rx_bytes += cf->len;
Does this completely inflate the RX statistics, as well as cause a concurrent
data race between hardirq and softirq contexts updating dev->stats?
Additionally, wouldn't TX echo packets queued via
can_rx_offload_get_echo_skb_queue_timestamp() also be dequeued by the poll
routine, erroneously counting transmitted frames as RX packets?
[ ... ]
> @@ -1065,32 +1052,6 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus)
[ ... ]
> static unsigned int m_can_tx_update_stats(struct m_can_classdev *cdev,
> unsigned int msg_mark, u32 timestamp)
> {
> @@ -1098,14 +1059,11 @@ static unsigned int m_can_tx_update_stats(struct m_can_classdev *cdev,
> struct net_device_stats *stats = &dev->stats;
> unsigned int frame_len;
>
> - if (cdev->is_peripheral)
> - stats->tx_bytes +=
> - can_rx_offload_get_echo_skb_queue_timestamp(&cdev->offload,
> - msg_mark,
> - timestamp,
> - &frame_len);
> - else
> - stats->tx_bytes += can_get_echo_skb(dev, msg_mark, &frame_len);
> + stats->tx_bytes +=
> + can_rx_offload_get_echo_skb_queue_timestamp(&cdev->offload,
> + msg_mark,
> + timestamp,
> + &frame_len);
>
> stats->tx_packets++;
>
> return frame_len;
[Severity: High]
This is a pre-existing issue, but is it possible for an uninitialized stack
variable to corrupt BQL accounting?
In m_can_tx_update_stats(), frame_len is passed by reference to
can_rx_offload_get_echo_skb_queue_timestamp(). If the echo SKB is missing
or invalid, the helper function returns 0 immediately and does not initialize
frame_len.
Will this return uninitialized stack garbage that gets passed to
netdev_completed_queue() in m_can_finish_tx(), potentially stalling the TX
queue by corrupting Byte Queue Limits accounting?
[ ... ]
> @@ -1265,21 +1223,10 @@ static int m_can_interrupt_handler(struct m_can_classdev *cdev)
[ ... ]
> if (ir & (IR_RF0N | IR_RF0W | IR_ERR_ALL_30X)) {
> - cdev->irqstatus = ir;
> - if (!cdev->is_peripheral) {
> - m_can_disable_all_interrupts(cdev);
> - napi_schedule(&cdev->napi);
> - } else {
> - ret = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, ir);
> - if (ret < 0)
> - return ret;
> - }
> + ret = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, ir);
> + if (ret < 0)
> + return ret;
> }
[Severity: High]
Could directly calling m_can_rx_handler() from the hardirq handler cause
packets to be stranded indefinitely?
The handler is now called with a strict quota of NAPI_POLL_WEIGHT. If the FIFO
contains more messages than the remaining quota (which can shrink if work_done
is consumed by state errors), the read loop in m_can_do_rx_poll() breaks
early.
Because the IR_RF0N hardware interrupt is edge-triggered (only set when a new
message is written) and the NAPI automatic rescheduling is removed, will the
remaining packets sit in the hardware FIFO indefinitely until another frame
happens to arrive on the bus?
[ ... ]
> @@ -1846,11 +1791,10 @@ static int m_can_close(struct net_device *dev)
> if (cdev->is_peripheral) {
> destroy_workqueue(cdev->tx_wq);
> cdev->tx_wq = NULL;
> - can_rx_offload_disable(&cdev->offload);
> - } else {
> - napi_disable(&cdev->napi);
> }
[Severity: High]
This is a pre-existing issue, but can the teardown sequence in m_can_close()
trigger a null pointer dereference?
During m_can_close(), m_can_clean() iterates through the tx_ops and sets
the skb pointers to NULL:
m_can_clean()
cdev->tx_ops[i].skb = NULL;
Immediately after, destroy_workqueue(cdev->tx_wq) is called, which drains the
queue and executes pending tasks. When the flushed m_can_tx_work_queue() task
runs, it passes the nullified skb pointer to the tx handler:
m_can_tx_work_queue()
struct sk_buff *skb = op->skb;
m_can_tx_handler(cdev, skb);
Will this result in a null pointer dereference since the tx handler
unconditionally accesses skb->data? Also, is there a memory leak since
m_can_clean() clears the pointer without freeing the SKB memory?
[Severity: High]
This is a pre-existing issue, but does the teardown path safely stop the
polling timer?
Looking at the teardown sequence in m_can_close(), the interface is shut
down and interrupts are disabled, which relies on hrtimer_try_to_cancel() in
m_can_disable_all_interrupts():
m_can_close()
m_can_stop()
m_can_disable_all_interrupts()
hrtimer_try_to_cancel(&cdev->hrtimer);
If the timer callback is actively running on another CPU,
hrtimer_try_to_cancel() returns -1 without waiting for the callback to finish.
The teardown path then continues to free data structures, disable hardware
clocks, and power off the PHY.
Could this lead to a use-after-free and synchronous bus fault when the
concurrently executing timer callback accesses unmapped or unclocked MMIO
regions?
Also, m_can_class_unregister() appears to lack explicit timer teardown, which
might leave the timer active during module unload.
[ ... ]
> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> index 4743342b2fba3..ca5833650dbfb 100644
> --- a/drivers/net/can/m_can/m_can.h
> +++ b/drivers/net/can/m_can/m_can.h
> @@ -96,7 +96,6 @@ struct m_can_classdev {
> const struct m_can_ops *ops;
>
> int version;
> - u32 irqstatus;
>
> int pm_clock_support;
> int pm_wake_source;
[Severity: Low]
Is it intentional to keep the napi structure in m_can_classdev?
The patch removes all manual napi handling from the driver, but it looks like
struct napi_struct napi; is still present in the struct.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260709-m_can-rx-offload-v1-1-af3efa8e4272@pengutronix.de?part=1
prev parent reply other threads:[~2026-07-09 15:45 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 15:26 [PATCH can-next] can: m_can: switch to rx-offload implementation Marc Kleine-Budde
2026-07-09 15:45 ` sashiko-bot [this message]
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=20260709154514.846831F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-can@vger.kernel.org \
--cc=mailhol@kernel.org \
--cc=mkl@pengutronix.de \
--cc=o.rempel@pengutronix.de \
--cc=sashiko-reviews@lists.linux.dev \
--cc=socketcan@hartkopp.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.