diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-04-21 05:45:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-28 16:16:35 -0400 |
commit | f6d8bd051c391c1c0458a30b2a7abcd939329259 (patch) | |
tree | 1dc4daecdeb0b42c2c6b59d7d6b41e091c11db5f /net/ipv4/af_inet.c | |
parent | 0a14842f5a3c0e88a1e59fac5c3025db39721f74 (diff) |
inet: add RCU protection to inet->opt
We lack proper synchronization to manipulate inet->opt ip_options
Problem is ip_make_skb() calls ip_setup_cork() and
ip_setup_cork() possibly makes a copy of ipc->opt (struct ip_options),
without any protection against another thread manipulating inet->opt.
Another thread can change inet->opt pointer and free old one under us.
Use RCU to protect inet->opt (changed to inet->inet_opt).
Instead of handling atomic refcounts, just copy ip_options when
necessary, to avoid cache line dirtying.
We cant insert an rcu_head in struct ip_options since its included in
skb->cb[], so this patch is large because I had to introduce a new
ip_options_rcu structure.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/af_inet.c')
-rw-r--r-- | net/ipv4/af_inet.c | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 0413af3e2285..963a621e75c7 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -153,7 +153,7 @@ void inet_sock_destruct(struct sock *sk) | |||
153 | WARN_ON(sk->sk_wmem_queued); | 153 | WARN_ON(sk->sk_wmem_queued); |
154 | WARN_ON(sk->sk_forward_alloc); | 154 | WARN_ON(sk->sk_forward_alloc); |
155 | 155 | ||
156 | kfree(inet->opt); | 156 | kfree(rcu_dereference_protected(inet->inet_opt, 1)); |
157 | dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); | 157 | dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); |
158 | sk_refcnt_debug_dec(sk); | 158 | sk_refcnt_debug_dec(sk); |
159 | } | 159 | } |
@@ -1106,9 +1106,12 @@ static int inet_sk_reselect_saddr(struct sock *sk) | |||
1106 | struct flowi4 fl4; | 1106 | struct flowi4 fl4; |
1107 | struct rtable *rt; | 1107 | struct rtable *rt; |
1108 | __be32 new_saddr; | 1108 | __be32 new_saddr; |
1109 | struct ip_options_rcu *inet_opt; | ||
1109 | 1110 | ||
1110 | if (inet->opt && inet->opt->srr) | 1111 | inet_opt = rcu_dereference_protected(inet->inet_opt, |
1111 | daddr = inet->opt->faddr; | 1112 | sock_owned_by_user(sk)); |
1113 | if (inet_opt && inet_opt->opt.srr) | ||
1114 | daddr = inet_opt->opt.faddr; | ||
1112 | 1115 | ||
1113 | /* Query new route. */ | 1116 | /* Query new route. */ |
1114 | rt = ip_route_connect(&fl4, daddr, 0, RT_CONN_FLAGS(sk), | 1117 | rt = ip_route_connect(&fl4, daddr, 0, RT_CONN_FLAGS(sk), |
@@ -1148,6 +1151,7 @@ int inet_sk_rebuild_header(struct sock *sk) | |||
1148 | struct inet_sock *inet = inet_sk(sk); | 1151 | struct inet_sock *inet = inet_sk(sk); |
1149 | struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); | 1152 | struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); |
1150 | __be32 daddr; | 1153 | __be32 daddr; |
1154 | struct ip_options_rcu *inet_opt; | ||
1151 | int err; | 1155 | int err; |
1152 | 1156 | ||
1153 | /* Route is OK, nothing to do. */ | 1157 | /* Route is OK, nothing to do. */ |
@@ -1155,9 +1159,12 @@ int inet_sk_rebuild_header(struct sock *sk) | |||
1155 | return 0; | 1159 | return 0; |
1156 | 1160 | ||
1157 | /* Reroute. */ | 1161 | /* Reroute. */ |
1162 | rcu_read_lock(); | ||
1163 | inet_opt = rcu_dereference(inet->inet_opt); | ||
1158 | daddr = inet->inet_daddr; | 1164 | daddr = inet->inet_daddr; |
1159 | if (inet->opt && inet->opt->srr) | 1165 | if (inet_opt && inet_opt->opt.srr) |
1160 | daddr = inet->opt->faddr; | 1166 | daddr = inet_opt->opt.faddr; |
1167 | rcu_read_unlock(); | ||
1161 | rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr, | 1168 | rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr, |
1162 | inet->inet_dport, inet->inet_sport, | 1169 | inet->inet_dport, inet->inet_sport, |
1163 | sk->sk_protocol, RT_CONN_FLAGS(sk), | 1170 | sk->sk_protocol, RT_CONN_FLAGS(sk), |