@@ -130,7 +130,7 @@ struct cgw_job {
130130 u32 handled_frames ;
131131 u32 dropped_frames ;
132132 u32 deleted_frames ;
133- struct cf_mod mod ;
133+ struct cf_mod __rcu * cf_mod ;
134134 union {
135135 /* CAN frame data source */
136136 struct net_device * dev ;
@@ -459,6 +459,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
459459 struct cgw_job * gwj = (struct cgw_job * )data ;
460460 struct canfd_frame * cf ;
461461 struct sk_buff * nskb ;
462+ struct cf_mod * mod ;
462463 int modidx = 0 ;
463464
464465 /* process strictly Classic CAN or CAN FD frames */
@@ -506,7 +507,8 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
506507 * When there is at least one modification function activated,
507508 * we need to copy the skb as we want to modify skb->data.
508509 */
509- if (gwj -> mod .modfunc [0 ])
510+ mod = rcu_dereference (gwj -> cf_mod );
511+ if (mod -> modfunc [0 ])
510512 nskb = skb_copy (skb , GFP_ATOMIC );
511513 else
512514 nskb = skb_clone (skb , GFP_ATOMIC );
@@ -529,8 +531,8 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
529531 cf = (struct canfd_frame * )nskb -> data ;
530532
531533 /* perform preprocessed modification functions if there are any */
532- while (modidx < MAX_MODFUNCTIONS && gwj -> mod . modfunc [modidx ])
533- (* gwj -> mod . modfunc [modidx ++ ])(cf , & gwj -> mod );
534+ while (modidx < MAX_MODFUNCTIONS && mod -> modfunc [modidx ])
535+ (* mod -> modfunc [modidx ++ ])(cf , mod );
534536
535537 /* Has the CAN frame been modified? */
536538 if (modidx ) {
@@ -546,11 +548,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
546548 }
547549
548550 /* check for checksum updates */
549- if (gwj -> mod . csumfunc .crc8 )
550- (* gwj -> mod . csumfunc .crc8 )(cf , & gwj -> mod . csum .crc8 );
551+ if (mod -> csumfunc .crc8 )
552+ (* mod -> csumfunc .crc8 )(cf , & mod -> csum .crc8 );
551553
552- if (gwj -> mod . csumfunc .xor )
553- (* gwj -> mod . csumfunc .xor )(cf , & gwj -> mod . csum .xor );
554+ if (mod -> csumfunc .xor )
555+ (* mod -> csumfunc .xor )(cf , & mod -> csum .xor );
554556 }
555557
556558 /* clear the skb timestamp if not configured the other way */
@@ -581,9 +583,20 @@ static void cgw_job_free_rcu(struct rcu_head *rcu_head)
581583{
582584 struct cgw_job * gwj = container_of (rcu_head , struct cgw_job , rcu );
583585
586+ /* cgw_job::cf_mod is always accessed from the same cgw_job object within
587+ * the same RCU read section. Once cgw_job is scheduled for removal,
588+ * cf_mod can also be removed without mandating an additional grace period.
589+ */
590+ kfree (rcu_access_pointer (gwj -> cf_mod ));
584591 kmem_cache_free (cgw_cache , gwj );
585592}
586593
594+ /* Return cgw_job::cf_mod with RTNL protected section */
595+ static struct cf_mod * cgw_job_cf_mod (struct cgw_job * gwj )
596+ {
597+ return rcu_dereference_protected (gwj -> cf_mod , rtnl_is_locked ());
598+ }
599+
587600static int cgw_notifier (struct notifier_block * nb ,
588601 unsigned long msg , void * ptr )
589602{
@@ -616,6 +629,7 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
616629{
617630 struct rtcanmsg * rtcan ;
618631 struct nlmsghdr * nlh ;
632+ struct cf_mod * mod ;
619633
620634 nlh = nlmsg_put (skb , pid , seq , type , sizeof (* rtcan ), flags );
621635 if (!nlh )
@@ -650,82 +664,83 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
650664 goto cancel ;
651665 }
652666
667+ mod = cgw_job_cf_mod (gwj );
653668 if (gwj -> flags & CGW_FLAGS_CAN_FD ) {
654669 struct cgw_fdframe_mod mb ;
655670
656- if (gwj -> mod . modtype .and ) {
657- memcpy (& mb .cf , & gwj -> mod . modframe .and , sizeof (mb .cf ));
658- mb .modtype = gwj -> mod . modtype .and ;
671+ if (mod -> modtype .and ) {
672+ memcpy (& mb .cf , & mod -> modframe .and , sizeof (mb .cf ));
673+ mb .modtype = mod -> modtype .and ;
659674 if (nla_put (skb , CGW_FDMOD_AND , sizeof (mb ), & mb ) < 0 )
660675 goto cancel ;
661676 }
662677
663- if (gwj -> mod . modtype .or ) {
664- memcpy (& mb .cf , & gwj -> mod . modframe .or , sizeof (mb .cf ));
665- mb .modtype = gwj -> mod . modtype .or ;
678+ if (mod -> modtype .or ) {
679+ memcpy (& mb .cf , & mod -> modframe .or , sizeof (mb .cf ));
680+ mb .modtype = mod -> modtype .or ;
666681 if (nla_put (skb , CGW_FDMOD_OR , sizeof (mb ), & mb ) < 0 )
667682 goto cancel ;
668683 }
669684
670- if (gwj -> mod . modtype .xor ) {
671- memcpy (& mb .cf , & gwj -> mod . modframe .xor , sizeof (mb .cf ));
672- mb .modtype = gwj -> mod . modtype .xor ;
685+ if (mod -> modtype .xor ) {
686+ memcpy (& mb .cf , & mod -> modframe .xor , sizeof (mb .cf ));
687+ mb .modtype = mod -> modtype .xor ;
673688 if (nla_put (skb , CGW_FDMOD_XOR , sizeof (mb ), & mb ) < 0 )
674689 goto cancel ;
675690 }
676691
677- if (gwj -> mod . modtype .set ) {
678- memcpy (& mb .cf , & gwj -> mod . modframe .set , sizeof (mb .cf ));
679- mb .modtype = gwj -> mod . modtype .set ;
692+ if (mod -> modtype .set ) {
693+ memcpy (& mb .cf , & mod -> modframe .set , sizeof (mb .cf ));
694+ mb .modtype = mod -> modtype .set ;
680695 if (nla_put (skb , CGW_FDMOD_SET , sizeof (mb ), & mb ) < 0 )
681696 goto cancel ;
682697 }
683698 } else {
684699 struct cgw_frame_mod mb ;
685700
686- if (gwj -> mod . modtype .and ) {
687- memcpy (& mb .cf , & gwj -> mod . modframe .and , sizeof (mb .cf ));
688- mb .modtype = gwj -> mod . modtype .and ;
701+ if (mod -> modtype .and ) {
702+ memcpy (& mb .cf , & mod -> modframe .and , sizeof (mb .cf ));
703+ mb .modtype = mod -> modtype .and ;
689704 if (nla_put (skb , CGW_MOD_AND , sizeof (mb ), & mb ) < 0 )
690705 goto cancel ;
691706 }
692707
693- if (gwj -> mod . modtype .or ) {
694- memcpy (& mb .cf , & gwj -> mod . modframe .or , sizeof (mb .cf ));
695- mb .modtype = gwj -> mod . modtype .or ;
708+ if (mod -> modtype .or ) {
709+ memcpy (& mb .cf , & mod -> modframe .or , sizeof (mb .cf ));
710+ mb .modtype = mod -> modtype .or ;
696711 if (nla_put (skb , CGW_MOD_OR , sizeof (mb ), & mb ) < 0 )
697712 goto cancel ;
698713 }
699714
700- if (gwj -> mod . modtype .xor ) {
701- memcpy (& mb .cf , & gwj -> mod . modframe .xor , sizeof (mb .cf ));
702- mb .modtype = gwj -> mod . modtype .xor ;
715+ if (mod -> modtype .xor ) {
716+ memcpy (& mb .cf , & mod -> modframe .xor , sizeof (mb .cf ));
717+ mb .modtype = mod -> modtype .xor ;
703718 if (nla_put (skb , CGW_MOD_XOR , sizeof (mb ), & mb ) < 0 )
704719 goto cancel ;
705720 }
706721
707- if (gwj -> mod . modtype .set ) {
708- memcpy (& mb .cf , & gwj -> mod . modframe .set , sizeof (mb .cf ));
709- mb .modtype = gwj -> mod . modtype .set ;
722+ if (mod -> modtype .set ) {
723+ memcpy (& mb .cf , & mod -> modframe .set , sizeof (mb .cf ));
724+ mb .modtype = mod -> modtype .set ;
710725 if (nla_put (skb , CGW_MOD_SET , sizeof (mb ), & mb ) < 0 )
711726 goto cancel ;
712727 }
713728 }
714729
715- if (gwj -> mod . uid ) {
716- if (nla_put_u32 (skb , CGW_MOD_UID , gwj -> mod . uid ) < 0 )
730+ if (mod -> uid ) {
731+ if (nla_put_u32 (skb , CGW_MOD_UID , mod -> uid ) < 0 )
717732 goto cancel ;
718733 }
719734
720- if (gwj -> mod . csumfunc .crc8 ) {
735+ if (mod -> csumfunc .crc8 ) {
721736 if (nla_put (skb , CGW_CS_CRC8 , CGW_CS_CRC8_LEN ,
722- & gwj -> mod . csum .crc8 ) < 0 )
737+ & mod -> csum .crc8 ) < 0 )
723738 goto cancel ;
724739 }
725740
726- if (gwj -> mod . csumfunc .xor ) {
741+ if (mod -> csumfunc .xor ) {
727742 if (nla_put (skb , CGW_CS_XOR , CGW_CS_XOR_LEN ,
728- & gwj -> mod . csum .xor ) < 0 )
743+ & mod -> csum .xor ) < 0 )
729744 goto cancel ;
730745 }
731746
@@ -1059,7 +1074,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
10591074 struct net * net = sock_net (skb -> sk );
10601075 struct rtcanmsg * r ;
10611076 struct cgw_job * gwj ;
1062- struct cf_mod mod ;
1077+ struct cf_mod * mod ;
10631078 struct can_can_gw ccgw ;
10641079 u8 limhops = 0 ;
10651080 int err = 0 ;
@@ -1078,37 +1093,48 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
10781093 if (r -> gwtype != CGW_TYPE_CAN_CAN )
10791094 return - EINVAL ;
10801095
1081- err = cgw_parse_attr (nlh , & mod , CGW_TYPE_CAN_CAN , & ccgw , & limhops );
1096+ mod = kmalloc (sizeof (* mod ), GFP_KERNEL );
1097+ if (!mod )
1098+ return - ENOMEM ;
1099+
1100+ err = cgw_parse_attr (nlh , mod , CGW_TYPE_CAN_CAN , & ccgw , & limhops );
10821101 if (err < 0 )
1083- return err ;
1102+ goto out_free_cf ;
10841103
1085- if (mod . uid ) {
1104+ if (mod -> uid ) {
10861105 ASSERT_RTNL ();
10871106
10881107 /* check for updating an existing job with identical uid */
10891108 hlist_for_each_entry (gwj , & net -> can .cgw_list , list ) {
1090- if (gwj -> mod .uid != mod .uid )
1109+ struct cf_mod * old_cf ;
1110+
1111+ old_cf = cgw_job_cf_mod (gwj );
1112+ if (old_cf -> uid != mod -> uid )
10911113 continue ;
10921114
10931115 /* interfaces & filters must be identical */
1094- if (memcmp (& gwj -> ccgw , & ccgw , sizeof (ccgw )))
1095- return - EINVAL ;
1116+ if (memcmp (& gwj -> ccgw , & ccgw , sizeof (ccgw ))) {
1117+ err = - EINVAL ;
1118+ goto out_free_cf ;
1119+ }
10961120
1097- /* update modifications with disabled softirq & quit */
1098- local_bh_disable ();
1099- memcpy (& gwj -> mod , & mod , sizeof (mod ));
1100- local_bh_enable ();
1121+ rcu_assign_pointer (gwj -> cf_mod , mod );
1122+ kfree_rcu_mightsleep (old_cf );
11011123 return 0 ;
11021124 }
11031125 }
11041126
11051127 /* ifindex == 0 is not allowed for job creation */
1106- if (!ccgw .src_idx || !ccgw .dst_idx )
1107- return - ENODEV ;
1128+ if (!ccgw .src_idx || !ccgw .dst_idx ) {
1129+ err = - ENODEV ;
1130+ goto out_free_cf ;
1131+ }
11081132
11091133 gwj = kmem_cache_alloc (cgw_cache , GFP_KERNEL );
1110- if (!gwj )
1111- return - ENOMEM ;
1134+ if (!gwj ) {
1135+ err = - ENOMEM ;
1136+ goto out_free_cf ;
1137+ }
11121138
11131139 gwj -> handled_frames = 0 ;
11141140 gwj -> dropped_frames = 0 ;
@@ -1118,7 +1144,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
11181144 gwj -> limit_hops = limhops ;
11191145
11201146 /* insert already parsed information */
1121- memcpy ( & gwj -> mod , & mod , sizeof ( mod ) );
1147+ RCU_INIT_POINTER ( gwj -> cf_mod , mod );
11221148 memcpy (& gwj -> ccgw , & ccgw , sizeof (ccgw ));
11231149
11241150 err = - ENODEV ;
@@ -1152,9 +1178,11 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
11521178 if (!err )
11531179 hlist_add_head_rcu (& gwj -> list , & net -> can .cgw_list );
11541180out :
1155- if (err )
1181+ if (err ) {
11561182 kmem_cache_free (cgw_cache , gwj );
1157-
1183+ out_free_cf :
1184+ kfree (mod );
1185+ }
11581186 return err ;
11591187}
11601188
@@ -1214,19 +1242,22 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
12141242
12151243 /* remove only the first matching entry */
12161244 hlist_for_each_entry_safe (gwj , nx , & net -> can .cgw_list , list ) {
1245+ struct cf_mod * cf_mod ;
1246+
12171247 if (gwj -> flags != r -> flags )
12181248 continue ;
12191249
12201250 if (gwj -> limit_hops != limhops )
12211251 continue ;
12221252
1253+ cf_mod = cgw_job_cf_mod (gwj );
12231254 /* we have a match when uid is enabled and identical */
1224- if (gwj -> mod . uid || mod .uid ) {
1225- if (gwj -> mod . uid != mod .uid )
1255+ if (cf_mod -> uid || mod .uid ) {
1256+ if (cf_mod -> uid != mod .uid )
12261257 continue ;
12271258 } else {
12281259 /* no uid => check for identical modifications */
1229- if (memcmp (& gwj -> mod , & mod , sizeof (mod )))
1260+ if (memcmp (cf_mod , & mod , sizeof (mod )))
12301261 continue ;
12311262 }
12321263
0 commit comments