can: raw: add locking for raw flags bitfield

With commit 890e5198a6 ("can: raw: use bitfields to store flags in
struct raw_sock") the formerly separate integer values have been integrated
into a single bitfield. This led to a read-modify-write operation when
changing a flag in raw_setsockopt() which now needs a locking to prevent
concurrent access.

Instead of adding a lock/unlock hell in each of the flag manipulations this
patch introduces a wrapper for a new raw_setsockopt_locked() function
analogue to the isotp_setsockopt[_locked]() approach in net/can/isotp.c

Fixes: 890e5198a6 ("can: raw: use bitfields to store flags in struct raw_sock")
Reported-by: Eulgyu Kim <eulgyukim@snu.ac.kr>
Closes: https://lore.kernel.org/linux-can/20260503112200.22727-1-eulgyukim@snu.ac.kr/
Tested-by: Eulgyu Kim <eulgyukim@snu.ac.kr>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Reviewed-by: Vincent Mailhol <mailhol@kernel.org>
Tested-by: Vincent Mailhol <mailhol@kernel.org>
Link: https://patch.msgid.link/20260504111928.41856-1-socketcan@hartkopp.net
[mkl: use Closes tag instead of Link]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Oliver Hartkopp 2026-05-07 10:22:23 +02:00 committed by Marc Kleine-Budde
parent c43122fef3
commit 1e5185c090

View File

@ -562,8 +562,8 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
return RAW_MIN_NAMELEN; return RAW_MIN_NAMELEN;
} }
static int raw_setsockopt(struct socket *sock, int level, int optname, static int raw_setsockopt_locked(struct socket *sock, int optname,
sockptr_t optval, unsigned int optlen) sockptr_t optval, unsigned int optlen)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk); struct raw_sock *ro = raw_sk(sk);
@ -575,9 +575,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
int flag; int flag;
int err = 0; int err = 0;
if (level != SOL_CAN_RAW)
return -EINVAL;
switch (optname) { switch (optname) {
case CAN_RAW_FILTER: case CAN_RAW_FILTER:
if (optlen % sizeof(struct can_filter) != 0) if (optlen % sizeof(struct can_filter) != 0)
@ -598,17 +595,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return -EFAULT; return -EFAULT;
} }
rtnl_lock();
lock_sock(sk);
dev = ro->dev; dev = ro->dev;
if (ro->bound && dev) { if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) {
if (dev->reg_state != NETREG_REGISTERED) { if (count > 1)
if (count > 1) kfree(filter);
kfree(filter); return -ENODEV;
err = -ENODEV;
goto out_fil;
}
} }
if (ro->bound) { if (ro->bound) {
@ -622,7 +613,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (err) { if (err) {
if (count > 1) if (count > 1)
kfree(filter); kfree(filter);
goto out_fil; return err;
} }
/* remove old filter registrations */ /* remove old filter registrations */
@ -642,11 +633,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
} }
ro->filter = filter; ro->filter = filter;
ro->count = count; ro->count = count;
out_fil:
release_sock(sk);
rtnl_unlock();
break; break;
case CAN_RAW_ERR_FILTER: case CAN_RAW_ERR_FILTER:
@ -658,16 +644,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
err_mask &= CAN_ERR_MASK; err_mask &= CAN_ERR_MASK;
rtnl_lock();
lock_sock(sk);
dev = ro->dev; dev = ro->dev;
if (ro->bound && dev) { if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED)
if (dev->reg_state != NETREG_REGISTERED) { return -ENODEV;
err = -ENODEV;
goto out_err;
}
}
/* remove current error mask */ /* remove current error mask */
if (ro->bound) { if (ro->bound) {
@ -676,7 +655,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
err_mask); err_mask);
if (err) if (err)
goto out_err; return err;
/* remove old err_mask registration */ /* remove old err_mask registration */
raw_disable_errfilter(sock_net(sk), dev, sk, raw_disable_errfilter(sock_net(sk), dev, sk,
@ -685,11 +664,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
/* link new err_mask to the socket */ /* link new err_mask to the socket */
ro->err_mask = err_mask; ro->err_mask = err_mask;
out_err:
release_sock(sk);
rtnl_unlock();
break; break;
case CAN_RAW_LOOPBACK: case CAN_RAW_LOOPBACK:
@ -769,6 +743,26 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return err; return err;
} }
static int raw_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err;
if (level != SOL_CAN_RAW)
return -EINVAL;
rtnl_lock();
lock_sock(sk);
err = raw_setsockopt_locked(sock, optname, optval, optlen);
release_sock(sk);
rtnl_unlock();
return err;
}
static int raw_getsockopt(struct socket *sock, int level, int optname, static int raw_getsockopt(struct socket *sock, int level, int optname,
sockopt_t *opt) sockopt_t *opt)
{ {