@@ -972,10 +972,10 @@ static void ip6_rt_init_dst(struct rt6_info *rt, struct fib6_info *ort)
972
972
rt -> dst .lastuse = jiffies ;
973
973
}
974
974
975
+ /* Caller must already hold reference to @from */
975
976
static void rt6_set_from (struct rt6_info * rt , struct fib6_info * from )
976
977
{
977
978
rt -> rt6i_flags &= ~RTF_EXPIRES ;
978
- fib6_info_hold (from );
979
979
rcu_assign_pointer (rt -> from , from );
980
980
dst_init_metrics (& rt -> dst , from -> fib6_metrics -> metrics , true);
981
981
if (from -> fib6_metrics != & dst_default_metrics ) {
@@ -984,6 +984,7 @@ static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
984
984
}
985
985
}
986
986
987
+ /* Caller must already hold reference to @ort */
987
988
static void ip6_rt_copy_init (struct rt6_info * rt , struct fib6_info * ort )
988
989
{
989
990
struct net_device * dev = fib6_info_nh_dev (ort );
@@ -1044,9 +1045,14 @@ static struct rt6_info *ip6_create_rt_rcu(struct fib6_info *rt)
1044
1045
struct net_device * dev = rt -> fib6_nh .nh_dev ;
1045
1046
struct rt6_info * nrt ;
1046
1047
1048
+ if (!fib6_info_hold_safe (rt ))
1049
+ return NULL ;
1050
+
1047
1051
nrt = ip6_dst_alloc (dev_net (dev ), dev , flags );
1048
1052
if (nrt )
1049
1053
ip6_rt_copy_init (nrt , rt );
1054
+ else
1055
+ fib6_info_release (rt );
1050
1056
1051
1057
return nrt ;
1052
1058
}
@@ -1178,10 +1184,15 @@ static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort,
1178
1184
* Clone the route.
1179
1185
*/
1180
1186
1187
+ if (!fib6_info_hold_safe (ort ))
1188
+ return NULL ;
1189
+
1181
1190
dev = ip6_rt_get_dev_rcu (ort );
1182
1191
rt = ip6_dst_alloc (dev_net (dev ), dev , 0 );
1183
- if (!rt )
1192
+ if (!rt ) {
1193
+ fib6_info_release (ort );
1184
1194
return NULL ;
1195
+ }
1185
1196
1186
1197
ip6_rt_copy_init (rt , ort );
1187
1198
rt -> rt6i_flags |= RTF_CACHE ;
@@ -1210,12 +1221,17 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct fib6_info *rt)
1210
1221
struct net_device * dev ;
1211
1222
struct rt6_info * pcpu_rt ;
1212
1223
1224
+ if (!fib6_info_hold_safe (rt ))
1225
+ return NULL ;
1226
+
1213
1227
rcu_read_lock ();
1214
1228
dev = ip6_rt_get_dev_rcu (rt );
1215
1229
pcpu_rt = ip6_dst_alloc (dev_net (dev ), dev , flags );
1216
1230
rcu_read_unlock ();
1217
- if (!pcpu_rt )
1231
+ if (!pcpu_rt ) {
1232
+ fib6_info_release (rt );
1218
1233
return NULL ;
1234
+ }
1219
1235
ip6_rt_copy_init (pcpu_rt , rt );
1220
1236
pcpu_rt -> rt6i_flags |= RTF_PCPU ;
1221
1237
return pcpu_rt ;
@@ -2486,7 +2502,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
2486
2502
2487
2503
out :
2488
2504
if (ret )
2489
- dst_hold ( & ret -> dst );
2505
+ ip6_hold_safe ( net , & ret , true );
2490
2506
else
2491
2507
ret = ip6_create_rt_rcu (rt );
2492
2508
@@ -3303,7 +3319,8 @@ static int ip6_route_del(struct fib6_config *cfg,
3303
3319
continue ;
3304
3320
if (cfg -> fc_protocol && cfg -> fc_protocol != rt -> fib6_protocol )
3305
3321
continue ;
3306
- fib6_info_hold (rt );
3322
+ if (!fib6_info_hold_safe (rt ))
3323
+ continue ;
3307
3324
rcu_read_unlock ();
3308
3325
3309
3326
/* if gateway was specified only delete the one hop */
@@ -3409,6 +3426,9 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
3409
3426
3410
3427
rcu_read_lock ();
3411
3428
from = rcu_dereference (rt -> from );
3429
+ /* This fib6_info_hold() is safe here because we hold reference to rt
3430
+ * and rt already holds reference to fib6_info.
3431
+ */
3412
3432
fib6_info_hold (from );
3413
3433
rcu_read_unlock ();
3414
3434
@@ -3470,7 +3490,8 @@ static struct fib6_info *rt6_get_route_info(struct net *net,
3470
3490
continue ;
3471
3491
if (!ipv6_addr_equal (& rt -> fib6_nh .nh_gw , gwaddr ))
3472
3492
continue ;
3473
- fib6_info_hold (rt );
3493
+ if (!fib6_info_hold_safe (rt ))
3494
+ continue ;
3474
3495
break ;
3475
3496
}
3476
3497
out :
@@ -3530,8 +3551,8 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
3530
3551
ipv6_addr_equal (& rt -> fib6_nh .nh_gw , addr ))
3531
3552
break ;
3532
3553
}
3533
- if (rt )
3534
- fib6_info_hold ( rt ) ;
3554
+ if (rt && ! fib6_info_hold_safe ( rt ) )
3555
+ rt = NULL ;
3535
3556
rcu_read_unlock ();
3536
3557
return rt ;
3537
3558
}
@@ -3579,8 +3600,8 @@ static void __rt6_purge_dflt_routers(struct net *net,
3579
3600
struct inet6_dev * idev = dev ? __in6_dev_get (dev ) : NULL ;
3580
3601
3581
3602
if (rt -> fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF ) &&
3582
- (!idev || idev -> cnf .accept_ra != 2 )) {
3583
- fib6_info_hold (rt );
3603
+ (!idev || idev -> cnf .accept_ra != 2 ) &&
3604
+ fib6_info_hold_safe (rt )) {
3584
3605
rcu_read_unlock ();
3585
3606
ip6_del_rt (net , rt );
3586
3607
goto restart ;
0 commit comments