diff options
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 47 |
1 files changed, 32 insertions, 15 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 64b70ad162e3..ab0c9efd1efa 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -131,7 +131,7 @@ static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) | |||
131 | static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) | 131 | static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) |
132 | { | 132 | { |
133 | struct sockaddr_in sin; | 133 | struct sockaddr_in sin; |
134 | struct iphdr *iph = ip_hdr(skb); | 134 | const struct iphdr *iph = ip_hdr(skb); |
135 | __be16 *ports = (__be16 *)skb_transport_header(skb); | 135 | __be16 *ports = (__be16 *)skb_transport_header(skb); |
136 | 136 | ||
137 | if (skb_transport_offset(skb) + 4 > skb->len) | 137 | if (skb_transport_offset(skb) + 4 > skb->len) |
@@ -238,7 +238,7 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) | |||
238 | but receiver should be enough clever f.e. to forward mtrace requests, | 238 | but receiver should be enough clever f.e. to forward mtrace requests, |
239 | sent to multicast group to reach destination designated router. | 239 | sent to multicast group to reach destination designated router. |
240 | */ | 240 | */ |
241 | struct ip_ra_chain *ip_ra_chain; | 241 | struct ip_ra_chain __rcu *ip_ra_chain; |
242 | static DEFINE_SPINLOCK(ip_ra_lock); | 242 | static DEFINE_SPINLOCK(ip_ra_lock); |
243 | 243 | ||
244 | 244 | ||
@@ -253,7 +253,8 @@ static void ip_ra_destroy_rcu(struct rcu_head *head) | |||
253 | int ip_ra_control(struct sock *sk, unsigned char on, | 253 | int ip_ra_control(struct sock *sk, unsigned char on, |
254 | void (*destructor)(struct sock *)) | 254 | void (*destructor)(struct sock *)) |
255 | { | 255 | { |
256 | struct ip_ra_chain *ra, *new_ra, **rap; | 256 | struct ip_ra_chain *ra, *new_ra; |
257 | struct ip_ra_chain __rcu **rap; | ||
257 | 258 | ||
258 | if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW) | 259 | if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num == IPPROTO_RAW) |
259 | return -EINVAL; | 260 | return -EINVAL; |
@@ -261,7 +262,10 @@ int ip_ra_control(struct sock *sk, unsigned char on, | |||
261 | new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; | 262 | new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; |
262 | 263 | ||
263 | spin_lock_bh(&ip_ra_lock); | 264 | spin_lock_bh(&ip_ra_lock); |
264 | for (rap = &ip_ra_chain; (ra = *rap) != NULL; rap = &ra->next) { | 265 | for (rap = &ip_ra_chain; |
266 | (ra = rcu_dereference_protected(*rap, | ||
267 | lockdep_is_held(&ip_ra_lock))) != NULL; | ||
268 | rap = &ra->next) { | ||
265 | if (ra->sk == sk) { | 269 | if (ra->sk == sk) { |
266 | if (on) { | 270 | if (on) { |
267 | spin_unlock_bh(&ip_ra_lock); | 271 | spin_unlock_bh(&ip_ra_lock); |
@@ -447,6 +451,11 @@ out: | |||
447 | } | 451 | } |
448 | 452 | ||
449 | 453 | ||
454 | static void opt_kfree_rcu(struct rcu_head *head) | ||
455 | { | ||
456 | kfree(container_of(head, struct ip_options_rcu, rcu)); | ||
457 | } | ||
458 | |||
450 | /* | 459 | /* |
451 | * Socket option code for IP. This is the end of the line after any | 460 | * Socket option code for IP. This is the end of the line after any |
452 | * TCP,UDP etc options on an IP socket. | 461 | * TCP,UDP etc options on an IP socket. |
@@ -493,13 +502,16 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
493 | switch (optname) { | 502 | switch (optname) { |
494 | case IP_OPTIONS: | 503 | case IP_OPTIONS: |
495 | { | 504 | { |
496 | struct ip_options *opt = NULL; | 505 | struct ip_options_rcu *old, *opt = NULL; |
506 | |||
497 | if (optlen > 40) | 507 | if (optlen > 40) |
498 | goto e_inval; | 508 | goto e_inval; |
499 | err = ip_options_get_from_user(sock_net(sk), &opt, | 509 | err = ip_options_get_from_user(sock_net(sk), &opt, |
500 | optval, optlen); | 510 | optval, optlen); |
501 | if (err) | 511 | if (err) |
502 | break; | 512 | break; |
513 | old = rcu_dereference_protected(inet->inet_opt, | ||
514 | sock_owned_by_user(sk)); | ||
503 | if (inet->is_icsk) { | 515 | if (inet->is_icsk) { |
504 | struct inet_connection_sock *icsk = inet_csk(sk); | 516 | struct inet_connection_sock *icsk = inet_csk(sk); |
505 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 517 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
@@ -508,17 +520,18 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
508 | (TCPF_LISTEN | TCPF_CLOSE)) && | 520 | (TCPF_LISTEN | TCPF_CLOSE)) && |
509 | inet->inet_daddr != LOOPBACK4_IPV6)) { | 521 | inet->inet_daddr != LOOPBACK4_IPV6)) { |
510 | #endif | 522 | #endif |
511 | if (inet->opt) | 523 | if (old) |
512 | icsk->icsk_ext_hdr_len -= inet->opt->optlen; | 524 | icsk->icsk_ext_hdr_len -= old->opt.optlen; |
513 | if (opt) | 525 | if (opt) |
514 | icsk->icsk_ext_hdr_len += opt->optlen; | 526 | icsk->icsk_ext_hdr_len += opt->opt.optlen; |
515 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); | 527 | icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); |
516 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 528 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
517 | } | 529 | } |
518 | #endif | 530 | #endif |
519 | } | 531 | } |
520 | opt = xchg(&inet->opt, opt); | 532 | rcu_assign_pointer(inet->inet_opt, opt); |
521 | kfree(opt); | 533 | if (old) |
534 | call_rcu(&old->rcu, opt_kfree_rcu); | ||
522 | break; | 535 | break; |
523 | } | 536 | } |
524 | case IP_PKTINFO: | 537 | case IP_PKTINFO: |
@@ -1077,12 +1090,16 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
1077 | case IP_OPTIONS: | 1090 | case IP_OPTIONS: |
1078 | { | 1091 | { |
1079 | unsigned char optbuf[sizeof(struct ip_options)+40]; | 1092 | unsigned char optbuf[sizeof(struct ip_options)+40]; |
1080 | struct ip_options * opt = (struct ip_options *)optbuf; | 1093 | struct ip_options *opt = (struct ip_options *)optbuf; |
1094 | struct ip_options_rcu *inet_opt; | ||
1095 | |||
1096 | inet_opt = rcu_dereference_protected(inet->inet_opt, | ||
1097 | sock_owned_by_user(sk)); | ||
1081 | opt->optlen = 0; | 1098 | opt->optlen = 0; |
1082 | if (inet->opt) | 1099 | if (inet_opt) |
1083 | memcpy(optbuf, inet->opt, | 1100 | memcpy(optbuf, &inet_opt->opt, |
1084 | sizeof(struct ip_options)+ | 1101 | sizeof(struct ip_options) + |
1085 | inet->opt->optlen); | 1102 | inet_opt->opt.optlen); |
1086 | release_sock(sk); | 1103 | release_sock(sk); |
1087 | 1104 | ||
1088 | if (opt->optlen == 0) | 1105 | if (opt->optlen == 0) |