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/dccp | |
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/dccp')
-rw-r--r-- | net/dccp/ipv4.c | 16 | ||||
-rw-r--r-- | net/dccp/ipv6.c | 2 |
2 files changed, 11 insertions, 7 deletions
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index b92ab655d44e..cbbcc6c036e0 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -48,6 +48,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
48 | struct flowi4 fl4; | 48 | struct flowi4 fl4; |
49 | struct rtable *rt; | 49 | struct rtable *rt; |
50 | int err; | 50 | int err; |
51 | struct ip_options_rcu *inet_opt; | ||
51 | 52 | ||
52 | dp->dccps_role = DCCP_ROLE_CLIENT; | 53 | dp->dccps_role = DCCP_ROLE_CLIENT; |
53 | 54 | ||
@@ -58,10 +59,13 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
58 | return -EAFNOSUPPORT; | 59 | return -EAFNOSUPPORT; |
59 | 60 | ||
60 | nexthop = daddr = usin->sin_addr.s_addr; | 61 | nexthop = daddr = usin->sin_addr.s_addr; |
61 | if (inet->opt != NULL && inet->opt->srr) { | 62 | |
63 | inet_opt = rcu_dereference_protected(inet->inet_opt, | ||
64 | sock_owned_by_user(sk)); | ||
65 | if (inet_opt != NULL && inet_opt->opt.srr) { | ||
62 | if (daddr == 0) | 66 | if (daddr == 0) |
63 | return -EINVAL; | 67 | return -EINVAL; |
64 | nexthop = inet->opt->faddr; | 68 | nexthop = inet_opt->opt.faddr; |
65 | } | 69 | } |
66 | 70 | ||
67 | orig_sport = inet->inet_sport; | 71 | orig_sport = inet->inet_sport; |
@@ -78,7 +82,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
78 | return -ENETUNREACH; | 82 | return -ENETUNREACH; |
79 | } | 83 | } |
80 | 84 | ||
81 | if (inet->opt == NULL || !inet->opt->srr) | 85 | if (inet_opt == NULL || !inet_opt->opt.srr) |
82 | daddr = rt->rt_dst; | 86 | daddr = rt->rt_dst; |
83 | 87 | ||
84 | if (inet->inet_saddr == 0) | 88 | if (inet->inet_saddr == 0) |
@@ -89,8 +93,8 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
89 | inet->inet_daddr = daddr; | 93 | inet->inet_daddr = daddr; |
90 | 94 | ||
91 | inet_csk(sk)->icsk_ext_hdr_len = 0; | 95 | inet_csk(sk)->icsk_ext_hdr_len = 0; |
92 | if (inet->opt != NULL) | 96 | if (inet_opt) |
93 | inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen; | 97 | inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; |
94 | /* | 98 | /* |
95 | * Socket identity is still unknown (sport may be zero). | 99 | * Socket identity is still unknown (sport may be zero). |
96 | * However we set state to DCCP_REQUESTING and not releasing socket | 100 | * However we set state to DCCP_REQUESTING and not releasing socket |
@@ -405,7 +409,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
405 | newinet->inet_daddr = ireq->rmt_addr; | 409 | newinet->inet_daddr = ireq->rmt_addr; |
406 | newinet->inet_rcv_saddr = ireq->loc_addr; | 410 | newinet->inet_rcv_saddr = ireq->loc_addr; |
407 | newinet->inet_saddr = ireq->loc_addr; | 411 | newinet->inet_saddr = ireq->loc_addr; |
408 | newinet->opt = ireq->opt; | 412 | newinet->inet_opt = ireq->opt; |
409 | ireq->opt = NULL; | 413 | ireq->opt = NULL; |
410 | newinet->mc_index = inet_iif(skb); | 414 | newinet->mc_index = inet_iif(skb); |
411 | newinet->mc_ttl = ip_hdr(skb)->ttl; | 415 | newinet->mc_ttl = ip_hdr(skb)->ttl; |
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 73add2373247..8dc4348774a5 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -573,7 +573,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | |||
573 | 573 | ||
574 | First: no IPv4 options. | 574 | First: no IPv4 options. |
575 | */ | 575 | */ |
576 | newinet->opt = NULL; | 576 | newinet->inet_opt = NULL; |
577 | 577 | ||
578 | /* Clone RX bits */ | 578 | /* Clone RX bits */ |
579 | newnp->rxopt.all = np->rxopt.all; | 579 | newnp->rxopt.all = np->rxopt.all; |