Skip to content

Commit c2aba69

Browse files
hartkoppmarckleinebudde
authored andcommitted
can: bcm: add locking for bcm_op runtime updates
The CAN broadcast manager (CAN BCM) can send a sequence of CAN frames via hrtimer. The content and also the length of the sequence can be changed resp reduced at runtime where the 'currframe' counter is then set to zero. Although this appeared to be a safe operation the updates of 'currframe' can be triggered from user space and hrtimer context in bcm_can_tx(). Anderson Nascimento created a proof of concept that triggered a KASAN slab-out-of-bounds read access which can be prevented with a spin_lock_bh. At the rework of bcm_can_tx() the 'count' variable has been moved into the protected section as this variable can be modified from both contexts too. Fixes: ffd980f ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: Anderson Nascimento <[email protected]> Tested-by: Anderson Nascimento <[email protected]> Reviewed-by: Marc Kleine-Budde <[email protected]> Signed-off-by: Oliver Hartkopp <[email protected]> Link: https://patch.msgid.link/[email protected] Cc: [email protected] Signed-off-by: Marc Kleine-Budde <[email protected]>
1 parent 69c6d83 commit c2aba69

File tree

1 file changed

+45
-21
lines changed

1 file changed

+45
-21
lines changed

net/can/bcm.c

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include <linux/can/skb.h>
5959
#include <linux/can/bcm.h>
6060
#include <linux/slab.h>
61+
#include <linux/spinlock.h>
6162
#include <net/sock.h>
6263
#include <net/net_namespace.h>
6364

@@ -122,6 +123,7 @@ struct bcm_op {
122123
struct canfd_frame last_sframe;
123124
struct sock *sk;
124125
struct net_device *rx_reg_dev;
126+
spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
125127
};
126128

127129
struct bcm_sock {
@@ -285,13 +287,18 @@ static void bcm_can_tx(struct bcm_op *op)
285287
{
286288
struct sk_buff *skb;
287289
struct net_device *dev;
288-
struct canfd_frame *cf = op->frames + op->cfsiz * op->currframe;
290+
struct canfd_frame *cf;
289291
int err;
290292

291293
/* no target device? => exit */
292294
if (!op->ifindex)
293295
return;
294296

297+
/* read currframe under lock protection */
298+
spin_lock_bh(&op->bcm_tx_lock);
299+
cf = op->frames + op->cfsiz * op->currframe;
300+
spin_unlock_bh(&op->bcm_tx_lock);
301+
295302
dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
296303
if (!dev) {
297304
/* RFC: should this bcm_op remove itself here? */
@@ -312,6 +319,10 @@ static void bcm_can_tx(struct bcm_op *op)
312319
skb->dev = dev;
313320
can_skb_set_owner(skb, op->sk);
314321
err = can_send(skb, 1);
322+
323+
/* update currframe and count under lock protection */
324+
spin_lock_bh(&op->bcm_tx_lock);
325+
315326
if (!err)
316327
op->frames_abs++;
317328

@@ -320,6 +331,11 @@ static void bcm_can_tx(struct bcm_op *op)
320331
/* reached last frame? */
321332
if (op->currframe >= op->nframes)
322333
op->currframe = 0;
334+
335+
if (op->count > 0)
336+
op->count--;
337+
338+
spin_unlock_bh(&op->bcm_tx_lock);
323339
out:
324340
dev_put(dev);
325341
}
@@ -430,7 +446,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
430446
struct bcm_msg_head msg_head;
431447

432448
if (op->kt_ival1 && (op->count > 0)) {
433-
op->count--;
449+
bcm_can_tx(op);
434450
if (!op->count && (op->flags & TX_COUNTEVT)) {
435451

436452
/* create notification to user */
@@ -445,7 +461,6 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
445461

446462
bcm_send_to_user(op, &msg_head, NULL, 0);
447463
}
448-
bcm_can_tx(op);
449464

450465
} else if (op->kt_ival2) {
451466
bcm_can_tx(op);
@@ -956,16 +971,42 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
956971
}
957972
op->flags = msg_head->flags;
958973

974+
/* only lock for unlikely count/nframes/currframe changes */
975+
if (op->nframes != msg_head->nframes ||
976+
op->flags & TX_RESET_MULTI_IDX ||
977+
op->flags & SETTIMER) {
978+
979+
spin_lock_bh(&op->bcm_tx_lock);
980+
981+
if (op->nframes != msg_head->nframes ||
982+
op->flags & TX_RESET_MULTI_IDX) {
983+
/* potentially update changed nframes */
984+
op->nframes = msg_head->nframes;
985+
/* restart multiple frame transmission */
986+
op->currframe = 0;
987+
}
988+
989+
if (op->flags & SETTIMER)
990+
op->count = msg_head->count;
991+
992+
spin_unlock_bh(&op->bcm_tx_lock);
993+
}
994+
959995
} else {
960996
/* insert new BCM operation for the given can_id */
961997

962998
op = kzalloc(OPSIZ, GFP_KERNEL);
963999
if (!op)
9641000
return -ENOMEM;
9651001

1002+
spin_lock_init(&op->bcm_tx_lock);
9661003
op->can_id = msg_head->can_id;
9671004
op->cfsiz = CFSIZ(msg_head->flags);
9681005
op->flags = msg_head->flags;
1006+
op->nframes = msg_head->nframes;
1007+
1008+
if (op->flags & SETTIMER)
1009+
op->count = msg_head->count;
9691010

9701011
/* create array for CAN frames and copy the data */
9711012
if (msg_head->nframes > 1) {
@@ -1023,22 +1064,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10231064

10241065
} /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
10251066

1026-
if (op->nframes != msg_head->nframes) {
1027-
op->nframes = msg_head->nframes;
1028-
/* start multiple frame transmission with index 0 */
1029-
op->currframe = 0;
1030-
}
1031-
1032-
/* check flags */
1033-
1034-
if (op->flags & TX_RESET_MULTI_IDX) {
1035-
/* start multiple frame transmission with index 0 */
1036-
op->currframe = 0;
1037-
}
1038-
10391067
if (op->flags & SETTIMER) {
10401068
/* set timer values */
1041-
op->count = msg_head->count;
10421069
op->ival1 = msg_head->ival1;
10431070
op->ival2 = msg_head->ival2;
10441071
op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
@@ -1055,11 +1082,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10551082
op->flags |= TX_ANNOUNCE;
10561083
}
10571084

1058-
if (op->flags & TX_ANNOUNCE) {
1085+
if (op->flags & TX_ANNOUNCE)
10591086
bcm_can_tx(op);
1060-
if (op->count)
1061-
op->count--;
1062-
}
10631087

10641088
if (op->flags & STARTTIMER)
10651089
bcm_tx_start_timer(op);

0 commit comments

Comments
 (0)