can: raw: add locking for raw flags bitfield
Subsystems (2)
Oliver Hartkopp
maintainerPATCH 1/1 can: raw: add locking for raw flags bitfield
With commit 890e5198a6e5 ("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
--- net/can/raw.c | 66 +++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 36 deletions(-)
net/can/raw.c +30 -36
@@ -560,12 +560,12 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr, addr->can_ifindex = ro->ifindex;return RAW_MIN_NAMELEN ; } -static int raw_setsockopt (struct socket *sock,int level,int optname, -sockptr_t optval,unsigned int optlen) +static int raw_setsockopt_locked (struct socket *sock,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 */
@@ -573,13 +573,10 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,can_err_mask_t err_mask =0 ;int count =0 ;int flag;int err =0 ; -if (level !=SOL_CAN_RAW ) -return -EINVAL ; -switch (optname) {case CAN_RAW_FILTER :if (optlen %sizeof (struct can_filter ) !=0 )return -EINVAL ;
@@ -596,21 +593,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, }else if (count ==1 ) {if (copy_from_sockptr (&sfilter, optval,sizeof (sfilter)))return -EFAULT ; } -rtnl_lock (); -lock_sock (sk); - dev = ro->dev; -if (ro->bound && dev) { -if (dev->reg_state !=NETREG_REGISTERED ) { -if (count >1 ) -kfree (filter); - err = -ENODEV ; - goto out_fil; - } +if (ro->bound && dev && dev->reg_state !=NETREG_REGISTERED ) { +if (count >1 ) +kfree (filter); +return -ENODEV ; }if (ro->bound) {/* (try to) register the new filters */ if (count ==1 )
@@ -620,11 +611,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, err =raw_enable_filters (sock_net (sk), dev, sk, filter, count);if (err) {if (count >1 )kfree (filter); - goto out_fil; +return err; }/* remove old filter registrations */ raw_disable_filters (sock_net (sk), dev, sk, ro->filter, ro->count);
@@ -640,15 +631,10 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, ro->dfilter = sfilter; filter = &ro->dfilter; } ro->filter = filter; ro->count = count; - - out_fil: -release_sock (sk); -rtnl_unlock (); -break ;case CAN_RAW_ERR_FILTER :if (optlen !=sizeof (err_mask))return -EINVAL ;
@@ -656,42 +642,30 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,if (copy_from_sockptr (&err_mask, optval, optlen))return -EFAULT ; err_mask &=CAN_ERR_MASK ; -rtnl_lock (); -lock_sock (sk); - dev = ro->dev; -if (ro->bound && dev) { -if (dev->reg_state !=NETREG_REGISTERED ) { - err = -ENODEV ; - goto out_err; - } - } +if (ro->bound && dev && dev->reg_state !=NETREG_REGISTERED ) +return -ENODEV ;/* remove current error mask */ if (ro->bound) {/* (try to) register the new err_mask */ err =raw_enable_errfilter (sock_net (sk), dev, sk, err_mask);if (err) - goto out_err; +return err;/* remove old err_mask registration */ raw_disable_errfilter (sock_net (sk), dev, sk, ro->err_mask); }/* link new err_mask to the socket */ ro->err_mask = err_mask; - - out_err: -release_sock (sk); -rtnl_unlock (); -break ;case CAN_RAW_LOOPBACK :if (optlen !=sizeof (flag))return -EINVAL ;
@@ -767,10 +741,30 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,return -ENOPROTOOPT ; }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,sockopt_t *opt) {struct sock *sk = sock->sk;struct raw_sock *ro =raw_sk (sk);
Signature · 1 line
2.53.0
Quoted 11 lines · Oliver Hartkopp
With commit
890e5198a6e5("can: raw: use bitfields to store flags in structraw_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 inraw_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 innet/can/isotp.cFixes 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock")
Arg, that's my patch, sorry for that!
Quoted 1 line
Reported-by Eulgyu Kim <[email protected]>
Maybe add a link to the report?
Quoted 2 lines
Tested-by Eulgyu Kim <[email protected]>Signed-off-by Oliver Hartkopp <[email protected]>
I was able to trigger the bug locally using Eulgyu minimum reproducer and I can confirm that the issue is correctly resolved by this patch. I also think that globally holding the lock simplifies the logic. I tried to think if there were any alternatives (like READ_ONCE()/WRITE_ONCE() or atomic types) but none of these seem applicable here.
Yours sincerely,
Vincent Mailhol
Oliver Hartkopp
maintainerHello Vincent!
Quoted 7 lines · Vincent Mailhol
Quoted 6 lines · Oliver Hartkopp
With commit
890e5198a6e5("can: raw: use bitfields to store flags in structraw_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 inraw_setsockopt()which now needs a locking to prevent concurrent access.
Quoted 7 lines
Quoted 5 lines
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 innet/can/isotp.cFixes 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock")Arg, that's my patch, sorry for that!
No problem. I did realize this either o_O
Quoted 5 lines
Quoted 1 line
Reported-by Eulgyu Kim <[email protected]>Maybe add a link to the report?
Good idea. In fact checkpatch.pl said "Reported-by: should be immediately followed by Closes: with a URL to the report"
So it should be
Quoted 10 lines
Quoted 2 lines
Tested-by Eulgyu Kim <[email protected]>Signed-off-by Oliver Hartkopp <[email protected]>I was able to trigger the bug locally using Eulgyu minimum reproducer and I can confirm that the issue is correctly resolved by this patch. I also think that globally holding the lock simplifies the logic. I tried to think if there were any alternatives (like READ_ONCE()/WRITE_ONCE() or atomic types) but none of these seem applicable here.
Quoted 2 lines
Reviewed-by Vincent Mailhol <[email protected]>Tested-by Vincent Mailhol <[email protected]>
Thanks Vincent!
Best regards,
Oliver
Marc Kleine-Budde
maintainerQuoted 6 lines · Oliver Hartkopp
With commit
890e5198a6e5("can: raw: use bitfields to store flags in structraw_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 inraw_setsockopt()which now needs a locking to prevent concurrent access.
Quoted 8 lines
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 innet/can/isotp.cFixes 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock")Reported-by Eulgyu Kim <[email protected]>Tested-by Eulgyu Kim <[email protected]>Signed-off-by Oliver Hartkopp <[email protected]>
Applied with Closes tag instead of Link after Reported-by.
regards,
Marc
Signature · 4 lines
Pengutronix e.K. | Marc Kleine-Budde | Embedded Linux | https://www.pengutronix.de | Vertretung Nürnberg | Phone: +49-5121-206917-129 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |