Commit d188ba86 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

xfrm: add rcu protection to sk->sk_policy[]

XFRM can deal with SYNACK messages, sent while listener socket
is not locked. We add proper rcu protection to __xfrm_sk_clone_policy()
and xfrm_sk_policy_lookup()

This might serve as the first step to remove xfrm.xfrm_policy_lock
use in fast path.

Fixes: fa76ce73 ("inet: get rid of central tcp/dccp listener timer")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 56f04730
...@@ -388,7 +388,7 @@ struct sock { ...@@ -388,7 +388,7 @@ struct sock {
struct socket_wq *sk_wq_raw; struct socket_wq *sk_wq_raw;
}; };
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
struct xfrm_policy *sk_policy[2]; struct xfrm_policy __rcu *sk_policy[2];
#endif #endif
struct dst_entry *sk_rx_dst; struct dst_entry *sk_rx_dst;
struct dst_entry __rcu *sk_dst_cache; struct dst_entry __rcu *sk_dst_cache;
......
...@@ -1142,12 +1142,14 @@ static inline int xfrm6_route_forward(struct sk_buff *skb) ...@@ -1142,12 +1142,14 @@ static inline int xfrm6_route_forward(struct sk_buff *skb)
return xfrm_route_forward(skb, AF_INET6); return xfrm_route_forward(skb, AF_INET6);
} }
int __xfrm_sk_clone_policy(struct sock *sk); int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk);
static inline int xfrm_sk_clone_policy(struct sock *sk) static inline int xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
{ {
if (unlikely(sk->sk_policy[0] || sk->sk_policy[1])) sk->sk_policy[0] = NULL;
return __xfrm_sk_clone_policy(sk); sk->sk_policy[1] = NULL;
if (unlikely(osk->sk_policy[0] || osk->sk_policy[1]))
return __xfrm_sk_clone_policy(sk, osk);
return 0; return 0;
} }
...@@ -1155,12 +1157,16 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir); ...@@ -1155,12 +1157,16 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir);
static inline void xfrm_sk_free_policy(struct sock *sk) static inline void xfrm_sk_free_policy(struct sock *sk)
{ {
if (unlikely(sk->sk_policy[0] != NULL)) { struct xfrm_policy *pol;
xfrm_policy_delete(sk->sk_policy[0], XFRM_POLICY_MAX);
pol = rcu_dereference_protected(sk->sk_policy[0], 1);
if (unlikely(pol != NULL)) {
xfrm_policy_delete(pol, XFRM_POLICY_MAX);
sk->sk_policy[0] = NULL; sk->sk_policy[0] = NULL;
} }
if (unlikely(sk->sk_policy[1] != NULL)) { pol = rcu_dereference_protected(sk->sk_policy[1], 1);
xfrm_policy_delete(sk->sk_policy[1], XFRM_POLICY_MAX+1); if (unlikely(pol != NULL)) {
xfrm_policy_delete(pol, XFRM_POLICY_MAX+1);
sk->sk_policy[1] = NULL; sk->sk_policy[1] = NULL;
} }
} }
...@@ -1170,7 +1176,7 @@ void xfrm_garbage_collect(struct net *net); ...@@ -1170,7 +1176,7 @@ void xfrm_garbage_collect(struct net *net);
#else #else
static inline void xfrm_sk_free_policy(struct sock *sk) {} static inline void xfrm_sk_free_policy(struct sock *sk) {}
static inline int xfrm_sk_clone_policy(struct sock *sk) { return 0; } static inline int xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) { return 0; }
static inline int xfrm6_route_forward(struct sk_buff *skb) { return 1; } static inline int xfrm6_route_forward(struct sk_buff *skb) { return 1; }
static inline int xfrm4_route_forward(struct sk_buff *skb) { return 1; } static inline int xfrm4_route_forward(struct sk_buff *skb) { return 1; }
static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb) static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
......
...@@ -1550,7 +1550,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) ...@@ -1550,7 +1550,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
*/ */
is_charged = sk_filter_charge(newsk, filter); is_charged = sk_filter_charge(newsk, filter);
if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) { if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
/* It is still raw copy of parent, so invalidate /* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */ * destructor and make plain sk_free() */
newsk->sk_destruct = NULL; newsk->sk_destruct = NULL;
......
...@@ -1221,8 +1221,10 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, ...@@ -1221,8 +1221,10 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
struct xfrm_policy *pol; struct xfrm_policy *pol;
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
rcu_read_lock();
read_lock_bh(&net->xfrm.xfrm_policy_lock); read_lock_bh(&net->xfrm.xfrm_policy_lock);
if ((pol = sk->sk_policy[dir]) != NULL) { pol = rcu_dereference(sk->sk_policy[dir]);
if (pol != NULL) {
bool match = xfrm_selector_match(&pol->selector, fl, bool match = xfrm_selector_match(&pol->selector, fl,
sk->sk_family); sk->sk_family);
int err = 0; int err = 0;
...@@ -1246,6 +1248,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, ...@@ -1246,6 +1248,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
} }
out: out:
read_unlock_bh(&net->xfrm.xfrm_policy_lock); read_unlock_bh(&net->xfrm.xfrm_policy_lock);
rcu_read_unlock();
return pol; return pol;
} }
...@@ -1314,13 +1317,14 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) ...@@ -1314,13 +1317,14 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
#endif #endif
write_lock_bh(&net->xfrm.xfrm_policy_lock); write_lock_bh(&net->xfrm.xfrm_policy_lock);
old_pol = sk->sk_policy[dir]; old_pol = rcu_dereference_protected(sk->sk_policy[dir],
sk->sk_policy[dir] = pol; lockdep_is_held(&net->xfrm.xfrm_policy_lock));
if (pol) { if (pol) {
pol->curlft.add_time = get_seconds(); pol->curlft.add_time = get_seconds();
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0); pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
xfrm_sk_policy_link(pol, dir); xfrm_sk_policy_link(pol, dir);
} }
rcu_assign_pointer(sk->sk_policy[dir], pol);
if (old_pol) { if (old_pol) {
if (pol) if (pol)
xfrm_policy_requeue(old_pol, pol); xfrm_policy_requeue(old_pol, pol);
...@@ -1368,17 +1372,26 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) ...@@ -1368,17 +1372,26 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
return newp; return newp;
} }
int __xfrm_sk_clone_policy(struct sock *sk) int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
{ {
struct xfrm_policy *p0 = sk->sk_policy[0], const struct xfrm_policy *p;
*p1 = sk->sk_policy[1]; struct xfrm_policy *np;
int i, ret = 0;
sk->sk_policy[0] = sk->sk_policy[1] = NULL; rcu_read_lock();
if (p0 && (sk->sk_policy[0] = clone_policy(p0, 0)) == NULL) for (i = 0; i < 2; i++) {
return -ENOMEM; p = rcu_dereference(osk->sk_policy[i]);
if (p1 && (sk->sk_policy[1] = clone_policy(p1, 1)) == NULL) if (p) {
return -ENOMEM; np = clone_policy(p, i);
return 0; if (unlikely(!np)) {
ret = -ENOMEM;
break;
}
rcu_assign_pointer(sk->sk_policy[i], np);
}
}
rcu_read_unlock();
return ret;
} }
static int static int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment