From: Marc Kleine-Budde <mkl@pengutronix.de>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, linux-can@vger.kernel.org,
kernel@pengutronix.de, Oliver Hartkopp <socketcan@hartkopp.net>,
stable@vger.kernel.org, Marc Kleine-Budde <mkl@pengutronix.de>
Subject: [PATCH net 4/9] can: raw: use a private mutex instead of lock_sock()
Date: Sun, 15 Nov 2026 10:54:55 +0100 [thread overview]
Message-ID: <20261115095746.709371-5-mkl@pengutronix.de> (raw)
In-Reply-To: <20261115095746.709371-1-mkl@pengutronix.de>
From: Oliver Hartkopp <socketcan@hartkopp.net>
raw_notify() takes lock_sock() of the sockets that are bound to a
vanishing CAN device while holding RTNL. Any path that holds the socket
lock while accessing user space can therefore block RTNL. Since
commit 43c973a0887d ("can: raw, isotp: copy socket options outside of
lock_sock()") the CAN_RAW specific socket options copy their values
outside of the socket lock, but the generic SOL_SOCKET options in
sk_setsockopt() and sk_getsockopt() still access user space under
sockopt_lock_sock(), e.g. for SO_ATTACH_FILTER, SO_LINGER or SO_TXTIME.
A stalled copy, e.g. on a FUSE backed mapping, then blocks raw_notify()
and RTNL on the unregistration or namespace change of the bound CAN
device.
CAN_RAW only uses lock_sock() to serialize its own state: the binding,
the filters and the CAN_RAW socket options. It does not use the socket
backlog, as raw_rcv() queues the frames directly to the socket.
Replace lock_sock() with the mutex conf_lock in struct raw_sock. The
generic socket options do not take this mutex, so they can not block
raw_notify() anymore. No user space access happens while the mutex is
held. The memory allocations for the CAN receivers in can_rx_register()
are still done under the mutex, as they have been under lock_sock()
before.
Sashiko suggested to hold not only the filter copy in raw_getsockopt()
under the new conf_lock mutex but to also protect ro->raw_vcid_opts.
This change will be addressed in a follow-up patch as it goes beyond a
simple lock replacement intended for stable.
Fixes: c18ce101f2e4 ("[CAN]: Add raw protocol")
Cc: stable@vger.kernel.org # depends on "can: raw, isotp: copy socket options outside of lock_sock()"
Tested-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20261113-can-bcmlock-memcg-v5-3-634c0e04bc72@hartkopp.net
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
net/can/raw.c | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/net/can/raw.c b/net/can/raw.c
index f14270b56f52..b69eaadc5e93 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -103,6 +103,7 @@ struct raw_sock {
struct can_filter dfilter; /* default/single filter */
struct can_filter *filter; /* pointer to filter(s) */
struct uniqframe __percpu *uniq;
+ struct mutex conf_lock; /* protects bind, filters and setsockopt */
};
static LIST_HEAD(raw_notifier_list);
@@ -319,7 +320,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
/* Lockless fast path, ro->bind_dev covers a concurrent bind().
* Read it before ro->dev, pairs with smp_store_release() in
- * raw_bind(). A stale ro->bind_dev only costs a lock_sock().
+ * raw_bind(). A stale ro->bind_dev only costs a mutex_lock().
*/
bind_dev = smp_load_acquire(&ro->bind_dev);
if (READ_ONCE(ro->dev) != dev &&
@@ -328,9 +329,9 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
switch (msg) {
case NETDEV_UNREGISTER:
- lock_sock(sk);
+ mutex_lock(&ro->conf_lock);
if (ro->dev != dev) {
- release_sock(sk);
+ mutex_unlock(&ro->conf_lock);
break;
}
@@ -351,7 +352,7 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
sk->sk_err = ENODEV;
if (!sock_flag(sk, SOCK_DEAD))
sk_error_report(sk);
- release_sock(sk);
+ mutex_unlock(&ro->conf_lock);
break;
case NETDEV_DOWN:
@@ -401,6 +402,7 @@ static int raw_init(struct sock *sk)
ro->ifindex = 0;
ro->dev = NULL;
ro->bind_dev = NULL;
+ mutex_init(&ro->conf_lock);
/* set default filter to single entry dfilter */
ro->dfilter.can_id = 0;
@@ -451,7 +453,7 @@ static int raw_release(struct socket *sock)
list_del(&ro->notifier);
spin_unlock(&raw_notifier_lock);
- lock_sock(sk);
+ mutex_lock(&ro->conf_lock);
/* remove current filters & unregister
* the tracked reference ro->dev keeps the device's receive lists
@@ -474,11 +476,11 @@ static int raw_release(struct socket *sock)
ro->dev = NULL;
ro->count = 0;
+ mutex_unlock(&ro->conf_lock);
+
sock_orphan(sk);
sock->sk = NULL;
- release_sock(sk);
-
sock_prot_inuse_add(net, sk->sk_prot, -1);
sock_put(sk);
@@ -500,8 +502,8 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
if (addr->can_family != AF_CAN)
return -EINVAL;
- /* lock_sock() serializes the binding with raw_notify() */
- lock_sock(sk);
+ /* ro->conf_lock serializes the binding with raw_notify() */
+ mutex_lock(&ro->conf_lock);
if (ro->bound && addr->can_ifindex == ro->ifindex)
goto out;
@@ -581,7 +583,7 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len
sk_error_report(sk);
}
- release_sock(sk);
+ mutex_unlock(&ro->conf_lock);
return err;
}
@@ -621,7 +623,7 @@ static int raw_setsockopt_locked(struct socket *sock, int optname,
*
* The reg_state check is only a shortcut: On a concurrent
* unregistration or namespace change raw_notify() waits for
- * lock_sock() and removes the filters set here.
+ * ro->conf_lock and removes the filters set here.
*/
dev = ro->dev;
if (ro->bound && dev &&
@@ -730,6 +732,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
+ struct raw_sock *ro = raw_sk(sk);
struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
struct can_filter sfilter; /* single filter */
union {
@@ -744,9 +747,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (level != SOL_CAN_RAW)
return -EINVAL;
- /* Copy the option values from user space before lock_sock(), so a
- * stalled copy can not block raw_notify() waiting for lock_sock()
- * under RTNL.
+ /* Copy the option values from user space before taking ro->conf_lock,
+ * so a stalled copy can not block raw_notify() waiting for
+ * ro->conf_lock under RTNL.
*/
switch (optname) {
case CAN_RAW_FILTER:
@@ -798,11 +801,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return -EFAULT;
}
- lock_sock(sk);
+ mutex_lock(&ro->conf_lock);
err = raw_setsockopt_locked(sock, optname, &val, filter, count);
- release_sock(sk);
+ mutex_unlock(&ro->conf_lock);
/* on error free the filters allocated for CAN_RAW_FILTER above */
if (err && count > 1)
@@ -831,7 +834,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
struct can_filter *filter = NULL;
int err = 0;
- lock_sock(sk);
+ mutex_lock(&ro->conf_lock);
if (ro->count > 0) {
int fsize = ro->count * sizeof(struct can_filter);
@@ -843,7 +846,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
} else {
if (len > fsize)
len = fsize;
- /* copy to user space after release_sock() */
+ /* copy to user space after mutex_unlock() */
filter = kmemdup(ro->filter, len, GFP_KERNEL);
if (!filter)
err = -ENOMEM;
@@ -851,7 +854,7 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
} else {
len = 0;
}
- release_sock(sk);
+ mutex_unlock(&ro->conf_lock);
if (filter) {
if (copy_to_iter(filter, len, &opt->iter_out) != len)
--
2.53.0
next prev parent reply other threads:[~2026-11-15 9:57 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-15 9:54 [PATCH net 0/9] pull-request: can 2026-11-15 Marc Kleine-Budde
2026-11-15 9:54 ` [PATCH net 1/9] can: bcm: copy the CAN frames outside of lock_sock() Marc Kleine-Budde
2026-11-15 9:54 ` [PATCH net 2/9] can: bcm: do not remove the procfs entry in bcm_notify() Marc Kleine-Budde
2026-11-15 9:54 ` [PATCH net 3/9] can: netlink: fix static FD-NON-ISO mode validation Marc Kleine-Budde
2026-11-15 9:54 ` Marc Kleine-Budde [this message]
2026-11-15 9:54 ` [PATCH net 5/9] can: netlink: fix IFLA_CAN_BITRATE_MAX attribute sizing Marc Kleine-Budde
2026-11-15 9:54 ` [PATCH net 6/9] can: isotp: use a private mutex instead of lock_sock() Marc Kleine-Budde
2026-11-15 9:54 ` [PATCH net 7/9] can: netlink: fix CAN XL PWM attribute sizing Marc Kleine-Budde
2026-11-15 9:54 ` [PATCH net 8/9] can: bcm: use a private mutex instead of lock_sock() Marc Kleine-Budde
2026-11-15 9:55 ` [PATCH net 9/9] can: gs_usb: gs_can_start_xmit(): don't leak uninitialized memory over USB Marc Kleine-Budde
2026-11-15 10:18 ` [PATCH net 0/9] pull-request: can 2026-11-15 Marc Kleine-Budde
2026-11-15 11:23 ` Oliver Hartkopp
2026-11-15 11:55 ` Marc Kleine-Budde
2026-11-15 20:10 ` 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=20261115095746.709371-5-mkl@pengutronix.de \
--to=mkl@pengutronix.de \
--cc=davem@davemloft.net \
--cc=kernel@pengutronix.de \
--cc=kuba@kernel.org \
--cc=linux-can@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=socketcan@hartkopp.net \
--cc=stable@vger.kernel.org \
/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