diff options
author | Michal Kubecek <mkubecek@suse.cz> | 2014-06-03 04:26:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-03 19:07:44 -0400 |
commit | 21ee543edc0dea36ab58d24523fcd42b8a270df8 (patch) | |
tree | 75ebd1123fd7f54e1712e0e3232c859548781e95 | |
parent | 1299b3c49b7119ae534284e4129032441f40a422 (diff) |
xfrm: fix race between netns cleanup and state expire notification
The xfrm_user module registers its pernet init/exit after xfrm
itself so that its net exit function xfrm_user_net_exit() is
executed before xfrm_net_exit() which calls xfrm_state_fini() to
cleanup the SA's (xfrm states). This opens a window between
zeroing net->xfrm.nlsk pointer and deleting all xfrm_state
instances which may access it (via the timer). If an xfrm state
expires in this window, xfrm_exp_state_notify() will pass null
pointer as socket to nlmsg_multicast().
As the notifications are called inside rcu_read_lock() block, it
is sufficient to retrieve the nlsk socket with rcu_dereference()
and check the it for null.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/xfrm/xfrm_user.c | 36 |
1 files changed, 25 insertions, 11 deletions
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 51398ae6cda8..09336b268001 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c | |||
@@ -955,6 +955,20 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb, | |||
955 | return skb; | 955 | return skb; |
956 | } | 956 | } |
957 | 957 | ||
958 | /* A wrapper for nlmsg_multicast() checking that nlsk is still available. | ||
959 | * Must be called with RCU read lock. | ||
960 | */ | ||
961 | static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb, | ||
962 | u32 pid, unsigned int group) | ||
963 | { | ||
964 | struct sock *nlsk = rcu_dereference(net->xfrm.nlsk); | ||
965 | |||
966 | if (nlsk) | ||
967 | return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC); | ||
968 | else | ||
969 | return -1; | ||
970 | } | ||
971 | |||
958 | static inline size_t xfrm_spdinfo_msgsize(void) | 972 | static inline size_t xfrm_spdinfo_msgsize(void) |
959 | { | 973 | { |
960 | return NLMSG_ALIGN(4) | 974 | return NLMSG_ALIGN(4) |
@@ -2265,7 +2279,7 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, | |||
2265 | if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) | 2279 | if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0) |
2266 | BUG(); | 2280 | BUG(); |
2267 | 2281 | ||
2268 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); | 2282 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE); |
2269 | } | 2283 | } |
2270 | #else | 2284 | #else |
2271 | static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, | 2285 | static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, |
@@ -2456,7 +2470,7 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) | |||
2456 | return -EMSGSIZE; | 2470 | return -EMSGSIZE; |
2457 | } | 2471 | } |
2458 | 2472 | ||
2459 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); | 2473 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE); |
2460 | } | 2474 | } |
2461 | 2475 | ||
2462 | static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) | 2476 | static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) |
@@ -2471,7 +2485,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event | |||
2471 | if (build_aevent(skb, x, c) < 0) | 2485 | if (build_aevent(skb, x, c) < 0) |
2472 | BUG(); | 2486 | BUG(); |
2473 | 2487 | ||
2474 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); | 2488 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS); |
2475 | } | 2489 | } |
2476 | 2490 | ||
2477 | static int xfrm_notify_sa_flush(const struct km_event *c) | 2491 | static int xfrm_notify_sa_flush(const struct km_event *c) |
@@ -2497,7 +2511,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c) | |||
2497 | 2511 | ||
2498 | nlmsg_end(skb, nlh); | 2512 | nlmsg_end(skb, nlh); |
2499 | 2513 | ||
2500 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); | 2514 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA); |
2501 | } | 2515 | } |
2502 | 2516 | ||
2503 | static inline size_t xfrm_sa_len(struct xfrm_state *x) | 2517 | static inline size_t xfrm_sa_len(struct xfrm_state *x) |
@@ -2584,7 +2598,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) | |||
2584 | 2598 | ||
2585 | nlmsg_end(skb, nlh); | 2599 | nlmsg_end(skb, nlh); |
2586 | 2600 | ||
2587 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); | 2601 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA); |
2588 | 2602 | ||
2589 | out_free_skb: | 2603 | out_free_skb: |
2590 | kfree_skb(skb); | 2604 | kfree_skb(skb); |
@@ -2675,7 +2689,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, | |||
2675 | if (build_acquire(skb, x, xt, xp) < 0) | 2689 | if (build_acquire(skb, x, xt, xp) < 0) |
2676 | BUG(); | 2690 | BUG(); |
2677 | 2691 | ||
2678 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC); | 2692 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE); |
2679 | } | 2693 | } |
2680 | 2694 | ||
2681 | /* User gives us xfrm_user_policy_info followed by an array of 0 | 2695 | /* User gives us xfrm_user_policy_info followed by an array of 0 |
@@ -2789,7 +2803,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct | |||
2789 | if (build_polexpire(skb, xp, dir, c) < 0) | 2803 | if (build_polexpire(skb, xp, dir, c) < 0) |
2790 | BUG(); | 2804 | BUG(); |
2791 | 2805 | ||
2792 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); | 2806 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE); |
2793 | } | 2807 | } |
2794 | 2808 | ||
2795 | static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) | 2809 | static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) |
@@ -2851,7 +2865,7 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e | |||
2851 | 2865 | ||
2852 | nlmsg_end(skb, nlh); | 2866 | nlmsg_end(skb, nlh); |
2853 | 2867 | ||
2854 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); | 2868 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY); |
2855 | 2869 | ||
2856 | out_free_skb: | 2870 | out_free_skb: |
2857 | kfree_skb(skb); | 2871 | kfree_skb(skb); |
@@ -2879,7 +2893,7 @@ static int xfrm_notify_policy_flush(const struct km_event *c) | |||
2879 | 2893 | ||
2880 | nlmsg_end(skb, nlh); | 2894 | nlmsg_end(skb, nlh); |
2881 | 2895 | ||
2882 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); | 2896 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY); |
2883 | 2897 | ||
2884 | out_free_skb: | 2898 | out_free_skb: |
2885 | kfree_skb(skb); | 2899 | kfree_skb(skb); |
@@ -2948,7 +2962,7 @@ static int xfrm_send_report(struct net *net, u8 proto, | |||
2948 | if (build_report(skb, proto, sel, addr) < 0) | 2962 | if (build_report(skb, proto, sel, addr) < 0) |
2949 | BUG(); | 2963 | BUG(); |
2950 | 2964 | ||
2951 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC); | 2965 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT); |
2952 | } | 2966 | } |
2953 | 2967 | ||
2954 | static inline size_t xfrm_mapping_msgsize(void) | 2968 | static inline size_t xfrm_mapping_msgsize(void) |
@@ -3000,7 +3014,7 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, | |||
3000 | if (build_mapping(skb, x, ipaddr, sport) < 0) | 3014 | if (build_mapping(skb, x, ipaddr, sport) < 0) |
3001 | BUG(); | 3015 | BUG(); |
3002 | 3016 | ||
3003 | return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); | 3017 | return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING); |
3004 | } | 3018 | } |
3005 | 3019 | ||
3006 | static bool xfrm_is_alive(const struct km_event *c) | 3020 | static bool xfrm_is_alive(const struct km_event *c) |