@@ -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