linux-can-fixes-for-7.0-20260323

-----BEGIN PGP SIGNATURE-----
 
 iIkEABYKADEWIQSl+MghEFFAdY3pYJLMOmT6rpmt0gUCacEVXhMcbWtsQHBlbmd1
 dHJvbml4LmRlAAoJEMw6ZPquma3Si58BANpBSZYYe4PcLhowSJdr7KbT3oI3qiZS
 lnhk+ubPUWOUAQDzugYr+fUM9VHmCh2qCqsjgn83SzggCr110tzZUtRVBw==
 =VPfS
 -----END PGP SIGNATURE-----

Merge tag 'linux-can-fixes-for-7.0-20260323' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can

Marc Kleine-Budde says:

====================
pull-request: can 2026-03-23

this is a pull request of 5 patches for net/main.

The first patch is by me and adds missing error handling to the CAN
netlink device configuration code.

Wenyuan Li contributes a patch for the mcp251x drier to add missing
error handling for power enabling in th open and resume functions.

Oliver Hartkopp's patch adds missing atomic access in hot path for the
CAN procfs statistics.

A series by Ali Norouzi and Oliver Hartkopp fix a can-Out-of-Bounds
Heap R/W in the can-gw protocol and a UAF in the CAN isotp protocol.

linux-can-fixes-for-7.0-20260323

* tag 'linux-can-fixes-for-7.0-20260323' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can:
  can: isotp: fix tx.buf use-after-free in isotp_sendmsg()
  can: gw: fix OOB heap access in cgw_csum_crc8_rel()
  can: statistics: add missing atomic access in hot path
  can: mcp251x: add error handling for power enable in open and resume
  can: netlink: can_changelink(): add missing error handling to call can_ctrlmode_changelink()
====================

Link: https://patch.msgid.link/20260323103224.218099-1-mkl@pengutronix.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-03-24 12:22:52 +01:00
commit d9c2a509c9
7 changed files with 53 additions and 19 deletions

View File

@ -601,7 +601,9 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
/* We need synchronization with dev->stop() */
ASSERT_RTNL();
can_ctrlmode_changelink(dev, data, extack);
err = can_ctrlmode_changelink(dev, data, extack);
if (err)
return err;
if (data[IFLA_CAN_BITTIMING]) {
struct can_bittiming bt;

View File

@ -1225,7 +1225,11 @@ static int mcp251x_open(struct net_device *net)
}
mutex_lock(&priv->mcp_lock);
mcp251x_power_enable(priv->transceiver, 1);
ret = mcp251x_power_enable(priv->transceiver, 1);
if (ret) {
dev_err(&spi->dev, "failed to enable transceiver power: %pe\n", ERR_PTR(ret));
goto out_close_candev;
}
priv->force_quit = 0;
priv->tx_skb = NULL;
@ -1272,6 +1276,7 @@ static int mcp251x_open(struct net_device *net)
mcp251x_hw_sleep(spi);
out_close:
mcp251x_power_enable(priv->transceiver, 0);
out_close_candev:
close_candev(net);
mutex_unlock(&priv->mcp_lock);
if (release_irq)
@ -1516,11 +1521,25 @@ static int __maybe_unused mcp251x_can_resume(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct mcp251x_priv *priv = spi_get_drvdata(spi);
int ret = 0;
if (priv->after_suspend & AFTER_SUSPEND_POWER)
mcp251x_power_enable(priv->power, 1);
if (priv->after_suspend & AFTER_SUSPEND_UP)
mcp251x_power_enable(priv->transceiver, 1);
if (priv->after_suspend & AFTER_SUSPEND_POWER) {
ret = mcp251x_power_enable(priv->power, 1);
if (ret) {
dev_err(dev, "failed to restore power: %pe\n", ERR_PTR(ret));
return ret;
}
}
if (priv->after_suspend & AFTER_SUSPEND_UP) {
ret = mcp251x_power_enable(priv->transceiver, 1);
if (ret) {
dev_err(dev, "failed to restore transceiver power: %pe\n", ERR_PTR(ret));
if (priv->after_suspend & AFTER_SUSPEND_POWER)
mcp251x_power_enable(priv->power, 0);
return ret;
}
}
if (priv->after_suspend & (AFTER_SUSPEND_POWER | AFTER_SUSPEND_UP))
queue_work(priv->wq, &priv->restart_work);

View File

@ -469,7 +469,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
rcv->can_id = can_id;
rcv->mask = mask;
rcv->matches = 0;
atomic_long_set(&rcv->matches, 0);
rcv->func = func;
rcv->data = data;
rcv->ident = ident;
@ -573,7 +573,7 @@ EXPORT_SYMBOL(can_rx_unregister);
static inline void deliver(struct sk_buff *skb, struct receiver *rcv)
{
rcv->func(skb, rcv->data);
rcv->matches++;
atomic_long_inc(&rcv->matches);
}
static int can_rcv_filter(struct can_dev_rcv_lists *dev_rcv_lists, struct sk_buff *skb)

View File

@ -52,7 +52,7 @@ struct receiver {
struct hlist_node list;
canid_t can_id;
canid_t mask;
unsigned long matches;
atomic_long_t matches;
void (*func)(struct sk_buff *skb, void *data);
void *data;
char *ident;

View File

@ -375,10 +375,10 @@ static void cgw_csum_crc8_rel(struct canfd_frame *cf,
return;
if (from <= to) {
for (i = crc8->from_idx; i <= crc8->to_idx; i++)
for (i = from; i <= to; i++)
crc = crc8->crctab[crc ^ cf->data[i]];
} else {
for (i = crc8->from_idx; i >= crc8->to_idx; i--)
for (i = from; i >= to; i--)
crc = crc8->crctab[crc ^ cf->data[i]];
}
@ -397,7 +397,7 @@ static void cgw_csum_crc8_rel(struct canfd_frame *cf,
break;
}
cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
cf->data[res] = crc ^ crc8->final_xor_val;
}
static void cgw_csum_crc8_pos(struct canfd_frame *cf,

View File

@ -1248,12 +1248,6 @@ static int isotp_release(struct socket *sock)
so->ifindex = 0;
so->bound = 0;
if (so->rx.buf != so->rx.sbuf)
kfree(so->rx.buf);
if (so->tx.buf != so->tx.sbuf)
kfree(so->tx.buf);
sock_orphan(sk);
sock->sk = NULL;
@ -1622,6 +1616,21 @@ static int isotp_notifier(struct notifier_block *nb, unsigned long msg,
return NOTIFY_DONE;
}
static void isotp_sock_destruct(struct sock *sk)
{
struct isotp_sock *so = isotp_sk(sk);
/* do the standard CAN sock destruct work */
can_sock_destruct(sk);
/* free potential extended PDU buffers */
if (so->rx.buf != so->rx.sbuf)
kfree(so->rx.buf);
if (so->tx.buf != so->tx.sbuf)
kfree(so->tx.buf);
}
static int isotp_init(struct sock *sk)
{
struct isotp_sock *so = isotp_sk(sk);
@ -1666,6 +1675,9 @@ static int isotp_init(struct sock *sk)
list_add_tail(&so->notifier, &isotp_notifier_list);
spin_unlock(&isotp_notifier_lock);
/* re-assign default can_sock_destruct() reference */
sk->sk_destruct = isotp_sock_destruct;
return 0;
}

View File

@ -196,7 +196,8 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
" %-5s %03x %08x %pK %pK %8ld %s\n";
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
r->func, r->data, r->matches, r->ident);
r->func, r->data, atomic_long_read(&r->matches),
r->ident);
}
}