Skip to content

Commit 12ce799

Browse files
hartkoppmarckleinebudde
authored andcommitted
can: bcm: extend bcm_tx_lock usage for data and timer updates
Stage new CAN frame content for an existing tx op into a kmalloc()'d buffer and validate it there, mirroring the approach already used in bcm_rx_setup(). Only copy the validated data into op->frames while holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler() can no longer observe a partially updated or unvalidated frame. Add a missing error path for memcpy_from_msg() when copying CAN frame data from userspace. Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup() under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the torn 64-bit ktime_t read on 32-bit platforms. Fixes: c2aba69 ("can: bcm: add locking for bcm_op runtime updates") Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> Link: https://patch.msgid.link/20260714-bcm_fixes-v15-6-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
1 parent 7b2c3ea commit 12ce799

1 file changed

Lines changed: 75 additions & 29 deletions

File tree

net/can/bcm.c

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ struct bcm_op {
128128
struct canfd_frame last_sframe;
129129
struct sock *sk;
130130
struct net_device *rx_reg_dev;
131-
spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */
131+
spinlock_t bcm_tx_lock; /* protect tx data and timer updates */
132132
spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */
133133
};
134134

@@ -472,12 +472,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
472472
{
473473
ktime_t ival;
474474

475+
spin_lock_bh(&op->bcm_tx_lock);
476+
475477
if (op->kt_ival1 && op->count)
476478
ival = op->kt_ival1;
477-
else if (op->kt_ival2)
479+
else if (op->kt_ival2) {
478480
ival = op->kt_ival2;
479-
else
481+
} else {
482+
spin_unlock_bh(&op->bcm_tx_lock);
480483
return false;
484+
}
485+
486+
spin_unlock_bh(&op->bcm_tx_lock);
481487

482488
hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
483489
return true;
@@ -494,25 +500,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
494500
{
495501
struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
496502
struct bcm_msg_head msg_head;
503+
bool tx_ival1, tx_ival2;
504+
505+
/* snapshot kt_ival1/kt_ival2/count under lock to avoid torn
506+
* ktime_t reads racing with concurrent bcm_tx_setup() updates
507+
*/
508+
spin_lock_bh(&op->bcm_tx_lock);
509+
tx_ival1 = op->kt_ival1 && (op->count > 0);
510+
tx_ival2 = !!op->kt_ival2;
511+
spin_unlock_bh(&op->bcm_tx_lock);
512+
513+
if (tx_ival1) {
514+
u32 flags, count;
515+
struct bcm_timeval ival1, ival2;
497516

498-
if (op->kt_ival1 && (op->count > 0)) {
499517
bcm_can_tx(op, NULL);
500-
if (!op->count && (op->flags & TX_COUNTEVT)) {
501518

519+
/* snapshot variables under lock to avoid torn reads racing
520+
* with concurrent bcm_tx_setup() updates
521+
*/
522+
spin_lock_bh(&op->bcm_tx_lock);
523+
flags = op->flags;
524+
count = op->count;
525+
ival1 = op->ival1;
526+
ival2 = op->ival2;
527+
spin_unlock_bh(&op->bcm_tx_lock);
528+
529+
if (!count && (flags & TX_COUNTEVT)) {
502530
/* create notification to user */
503531
memset(&msg_head, 0, sizeof(msg_head));
504532
msg_head.opcode = TX_EXPIRED;
505-
msg_head.flags = op->flags;
506-
msg_head.count = op->count;
507-
msg_head.ival1 = op->ival1;
508-
msg_head.ival2 = op->ival2;
533+
msg_head.flags = flags;
534+
msg_head.count = count;
535+
msg_head.ival1 = ival1;
536+
msg_head.ival2 = ival2;
509537
msg_head.can_id = op->can_id;
510538
msg_head.nframes = 0;
511539

512540
bcm_send_to_user(op, &msg_head, NULL, 0);
513541
}
514542

515-
} else if (op->kt_ival2) {
543+
} else if (tx_ival2) {
516544
bcm_can_tx(op, NULL);
517545
}
518546

@@ -1036,6 +1064,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10361064
/* check the given can_id */
10371065
op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
10381066
if (op) {
1067+
void *new_frames;
1068+
10391069
/* update existing BCM operation */
10401070

10411071
/*
@@ -1046,11 +1076,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10461076
if (msg_head->nframes > op->nframes)
10471077
return -E2BIG;
10481078

1049-
/* update CAN frames content */
1079+
/* get new CAN frames content into a staging buffer before
1080+
* locking: validate and normalize the frames there so that
1081+
* bcm_can_tx() / bcm_tx_timeout_handler() never observe a
1082+
* partially updated or unvalidated frame in op->frames
1083+
*/
1084+
new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL);
1085+
if (!new_frames)
1086+
return -ENOMEM;
1087+
10501088
for (i = 0; i < msg_head->nframes; i++) {
10511089

1052-
cf = op->frames + op->cfsiz * i;
1090+
cf = new_frames + op->cfsiz * i;
10531091
err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
1092+
if (err < 0) {
1093+
kfree(new_frames);
1094+
return err;
1095+
}
10541096

10551097
if (op->flags & CAN_FD_FRAME) {
10561098
if (cf->len > 64)
@@ -1060,36 +1102,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10601102
err = -EINVAL;
10611103
}
10621104

1063-
if (err < 0)
1105+
if (err < 0) {
1106+
kfree(new_frames);
10641107
return err;
1108+
}
10651109

10661110
if (msg_head->flags & TX_CP_CAN_ID) {
10671111
/* copy can_id into frame */
10681112
cf->can_id = msg_head->can_id;
10691113
}
10701114
}
1115+
1116+
spin_lock_bh(&op->bcm_tx_lock);
1117+
1118+
/* update CAN frames content */
1119+
memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz);
1120+
10711121
op->flags = msg_head->flags;
10721122

1073-
/* only lock for unlikely count/nframes/currframe changes */
10741123
if (op->nframes != msg_head->nframes ||
1075-
op->flags & TX_RESET_MULTI_IDX ||
1076-
op->flags & SETTIMER) {
1077-
1078-
spin_lock_bh(&op->bcm_tx_lock);
1124+
op->flags & TX_RESET_MULTI_IDX) {
1125+
/* potentially update changed nframes */
1126+
op->nframes = msg_head->nframes;
1127+
/* restart multiple frame transmission */
1128+
op->currframe = 0;
1129+
}
10791130

1080-
if (op->nframes != msg_head->nframes ||
1081-
op->flags & TX_RESET_MULTI_IDX) {
1082-
/* potentially update changed nframes */
1083-
op->nframes = msg_head->nframes;
1084-
/* restart multiple frame transmission */
1085-
op->currframe = 0;
1086-
}
1131+
if (op->flags & SETTIMER)
1132+
op->count = msg_head->count;
10871133

1088-
if (op->flags & SETTIMER)
1089-
op->count = msg_head->count;
1134+
spin_unlock_bh(&op->bcm_tx_lock);
10901135

1091-
spin_unlock_bh(&op->bcm_tx_lock);
1092-
}
1136+
kfree(new_frames);
10931137

10941138
} else {
10951139
/* insert new BCM operation for the given can_id */
@@ -1165,10 +1209,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
11651209

11661210
if (op->flags & SETTIMER) {
11671211
/* set timer values */
1212+
spin_lock_bh(&op->bcm_tx_lock);
11681213
op->ival1 = msg_head->ival1;
11691214
op->ival2 = msg_head->ival2;
11701215
op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
11711216
op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
1217+
spin_unlock_bh(&op->bcm_tx_lock);
11721218

11731219
/* disable an active timer due to zero values? */
11741220
if (!op->kt_ival1 && !op->kt_ival2)

0 commit comments

Comments
 (0)