aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2012-08-19 06:31:48 -0400
committerDavid S. Miller <davem@davemloft.net>2012-08-23 01:39:46 -0400
commitef8531b64c3e2443da52e9f05d74a988230eedc5 (patch)
tree685df5cd6a04c761d3f7d3927185e4c0678c4dbb /net/xfrm
parent0115e8e30d6fcdd4b8faa30d3ffd90859a591f51 (diff)
xfrm: fix RCU bugs
This patch reverts commit 56892261ed1a (xfrm: Use rcu_dereference_bh to deference pointer protected by rcu_read_lock_bh), and fixes bugs introduced in commit 418a99ac6ad ( Replace rwlock on xfrm_policy_afinfo with rcu ) 1) We properly use RCU variant in this file, not a mix of RCU/RCU_BH 2) We must defer some writes after the synchronize_rcu() call or a reader can crash dereferencing NULL pointer. 3) Now we use the xfrm_policy_afinfo_lock spinlock only from process context, we no longer need to block BH in xfrm_policy_register_afinfo() and xfrm_policy_unregister_afinfo() 4) Can use RCU_INIT_POINTER() instead of rcu_assign_pointer() in xfrm_policy_unregister_afinfo() 5) Remove a forward inline declaration (xfrm_policy_put_afinfo()), and also move xfrm_policy_get_afinfo() declaration. Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Fan Du <fan.du@windriver.com> Cc: Priyanka Jain <Priyanka.Jain@freescale.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_policy.c76
1 files changed, 39 insertions, 37 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 2ed698c190b5..741a32aa512e 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -48,8 +48,6 @@ static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO]
48 48
49static struct kmem_cache *xfrm_dst_cache __read_mostly; 49static struct kmem_cache *xfrm_dst_cache __read_mostly;
50 50
51static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
52static inline void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
53static void xfrm_init_pmtu(struct dst_entry *dst); 51static void xfrm_init_pmtu(struct dst_entry *dst);
54static int stale_bundle(struct dst_entry *dst); 52static int stale_bundle(struct dst_entry *dst);
55static int xfrm_bundle_ok(struct xfrm_dst *xdst); 53static int xfrm_bundle_ok(struct xfrm_dst *xdst);
@@ -96,6 +94,24 @@ bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl
96 return false; 94 return false;
97} 95}
98 96
97static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
98{
99 struct xfrm_policy_afinfo *afinfo;
100
101 if (unlikely(family >= NPROTO))
102 return NULL;
103 rcu_read_lock();
104 afinfo = rcu_dereference(xfrm_policy_afinfo[family]);
105 if (unlikely(!afinfo))
106 rcu_read_unlock();
107 return afinfo;
108}
109
110static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
111{
112 rcu_read_unlock();
113}
114
99static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, 115static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
100 const xfrm_address_t *saddr, 116 const xfrm_address_t *saddr,
101 const xfrm_address_t *daddr, 117 const xfrm_address_t *daddr,
@@ -2421,7 +2437,7 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
2421 return -EINVAL; 2437 return -EINVAL;
2422 if (unlikely(afinfo->family >= NPROTO)) 2438 if (unlikely(afinfo->family >= NPROTO))
2423 return -EAFNOSUPPORT; 2439 return -EAFNOSUPPORT;
2424 spin_lock_bh(&xfrm_policy_afinfo_lock); 2440 spin_lock(&xfrm_policy_afinfo_lock);
2425 if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL)) 2441 if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
2426 err = -ENOBUFS; 2442 err = -ENOBUFS;
2427 else { 2443 else {
@@ -2444,7 +2460,7 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
2444 afinfo->garbage_collect = xfrm_garbage_collect_deferred; 2460 afinfo->garbage_collect = xfrm_garbage_collect_deferred;
2445 rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], afinfo); 2461 rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], afinfo);
2446 } 2462 }
2447 spin_unlock_bh(&xfrm_policy_afinfo_lock); 2463 spin_unlock(&xfrm_policy_afinfo_lock);
2448 2464
2449 rtnl_lock(); 2465 rtnl_lock();
2450 for_each_net(net) { 2466 for_each_net(net) {
@@ -2477,23 +2493,26 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
2477 return -EINVAL; 2493 return -EINVAL;
2478 if (unlikely(afinfo->family >= NPROTO)) 2494 if (unlikely(afinfo->family >= NPROTO))
2479 return -EAFNOSUPPORT; 2495 return -EAFNOSUPPORT;
2480 spin_lock_bh(&xfrm_policy_afinfo_lock); 2496 spin_lock(&xfrm_policy_afinfo_lock);
2481 if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) { 2497 if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
2482 if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo)) 2498 if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
2483 err = -EINVAL; 2499 err = -EINVAL;
2484 else { 2500 else
2485 struct dst_ops *dst_ops = afinfo->dst_ops; 2501 RCU_INIT_POINTER(xfrm_policy_afinfo[afinfo->family],
2486 rcu_assign_pointer(xfrm_policy_afinfo[afinfo->family], 2502 NULL);
2487 NULL); 2503 }
2488 dst_ops->kmem_cachep = NULL; 2504 spin_unlock(&xfrm_policy_afinfo_lock);
2489 dst_ops->check = NULL; 2505 if (!err) {
2490 dst_ops->negative_advice = NULL; 2506 struct dst_ops *dst_ops = afinfo->dst_ops;
2491 dst_ops->link_failure = NULL; 2507
2492 afinfo->garbage_collect = NULL; 2508 synchronize_rcu();
2493 } 2509
2510 dst_ops->kmem_cachep = NULL;
2511 dst_ops->check = NULL;
2512 dst_ops->negative_advice = NULL;
2513 dst_ops->link_failure = NULL;
2514 afinfo->garbage_collect = NULL;
2494 } 2515 }
2495 spin_unlock_bh(&xfrm_policy_afinfo_lock);
2496 synchronize_rcu();
2497 return err; 2516 return err;
2498} 2517}
2499EXPORT_SYMBOL(xfrm_policy_unregister_afinfo); 2518EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
@@ -2502,32 +2521,15 @@ static void __net_init xfrm_dst_ops_init(struct net *net)
2502{ 2521{
2503 struct xfrm_policy_afinfo *afinfo; 2522 struct xfrm_policy_afinfo *afinfo;
2504 2523
2505 rcu_read_lock_bh(); 2524 rcu_read_lock();
2506 afinfo = rcu_dereference_bh(xfrm_policy_afinfo[AF_INET]); 2525 afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
2507 if (afinfo) 2526 if (afinfo)
2508 net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops; 2527 net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
2509#if IS_ENABLED(CONFIG_IPV6) 2528#if IS_ENABLED(CONFIG_IPV6)
2510 afinfo = rcu_dereference_bh(xfrm_policy_afinfo[AF_INET6]); 2529 afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
2511 if (afinfo) 2530 if (afinfo)
2512 net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops; 2531 net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
2513#endif 2532#endif
2514 rcu_read_unlock_bh();
2515}
2516
2517static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
2518{
2519 struct xfrm_policy_afinfo *afinfo;
2520 if (unlikely(family >= NPROTO))
2521 return NULL;
2522 rcu_read_lock();
2523 afinfo = rcu_dereference(xfrm_policy_afinfo[family]);
2524 if (unlikely(!afinfo))
2525 rcu_read_unlock();
2526 return afinfo;
2527}
2528
2529static inline void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
2530{
2531 rcu_read_unlock(); 2533 rcu_read_unlock();
2532} 2534}
2533 2535