aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/udp.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-04-21 05:45:37 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-28 16:16:35 -0400
commitf6d8bd051c391c1c0458a30b2a7abcd939329259 (patch)
tree1dc4daecdeb0b42c2c6b59d7d6b41e091c11db5f /net/ipv4/udp.c
parent0a14842f5a3c0e88a1e59fac5c3025db39721f74 (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/udp.c')
-rw-r--r--net/ipv4/udp.c21
1 files changed, 16 insertions, 5 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index bc0dab2593e0..544f435d1aff 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -804,6 +804,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
804 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; 804 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
805 int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); 805 int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
806 struct sk_buff *skb; 806 struct sk_buff *skb;
807 struct ip_options_data opt_copy;
807 808
808 if (len > 0xFFFF) 809 if (len > 0xFFFF)
809 return -EMSGSIZE; 810 return -EMSGSIZE;
@@ -877,22 +878,32 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
877 free = 1; 878 free = 1;
878 connected = 0; 879 connected = 0;
879 } 880 }
880 if (!ipc.opt) 881 if (!ipc.opt) {
881 ipc.opt = inet->opt; 882 struct ip_options_rcu *inet_opt;
883
884 rcu_read_lock();
885 inet_opt = rcu_dereference(inet->inet_opt);
886 if (inet_opt) {
887 memcpy(&opt_copy, inet_opt,
888 sizeof(*inet_opt) + inet_opt->opt.optlen);
889 ipc.opt = &opt_copy.opt;
890 }
891 rcu_read_unlock();
892 }
882 893
883 saddr = ipc.addr; 894 saddr = ipc.addr;
884 ipc.addr = faddr = daddr; 895 ipc.addr = faddr = daddr;
885 896
886 if (ipc.opt && ipc.opt->srr) { 897 if (ipc.opt && ipc.opt->opt.srr) {
887 if (!daddr) 898 if (!daddr)
888 return -EINVAL; 899 return -EINVAL;
889 faddr = ipc.opt->faddr; 900 faddr = ipc.opt->opt.faddr;
890 connected = 0; 901 connected = 0;
891 } 902 }
892 tos = RT_TOS(inet->tos); 903 tos = RT_TOS(inet->tos);
893 if (sock_flag(sk, SOCK_LOCALROUTE) || 904 if (sock_flag(sk, SOCK_LOCALROUTE) ||
894 (msg->msg_flags & MSG_DONTROUTE) || 905 (msg->msg_flags & MSG_DONTROUTE) ||
895 (ipc.opt && ipc.opt->is_strictroute)) { 906 (ipc.opt && ipc.opt->opt.is_strictroute)) {
896 tos |= RTO_ONLINK; 907 tos |= RTO_ONLINK;
897 connected = 0; 908 connected = 0;
898 } 909 }