summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2015-11-29 19:37:57 -0800
committerBen Hutchings <ben@decadent.org.uk>2015-12-30 02:26:02 +0000
commit5bf369b4470d3618af67b572a82d76b92ce1abd1 (patch)
tree2ebafe230e8da33e591ef32b9749963ef09cee87 /include
parent3f276bbbe575e06eafcde349f9d92332690e4d2d (diff)
ipv6: add complete rcu protection around np->opt
[ Upstream commit 45f6fad84cc305103b28d73482b344d7f5b76f39 ] This patch addresses multiple problems : UDP/RAW sendmsg() need to get a stable struct ipv6_txoptions while socket is not locked : Other threads can change np->opt concurrently. Dmitry posted a syzkaller (http://github.com/google/syzkaller) program desmonstrating use-after-free. Starting with TCP/DCCP lockless listeners, tcp_v6_syn_recv_sock() and dccp_v6_request_recv_sock() also need to use RCU protection to dereference np->opt once (before calling ipv6_dup_options()) This patch adds full RCU protection to np->opt Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net> [bwh: Backported to 3.2: - Drop changes to l2tp - Fix an additional use of np->opt in tcp_v6_send_synack() - Fold in commit 43264e0bd963 ("ipv6: remove unnecessary codes in tcp_ipv6.c") - Adjust context] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'include')
-rw-r--r--include/linux/ipv6.h2
-rw-r--r--include/net/ipv6.h21
2 files changed, 21 insertions, 2 deletions
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 84b1447481b5..00ef00d0f315 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -369,7 +369,7 @@ struct ipv6_pinfo {
struct ipv6_ac_socklist *ipv6_ac_list;
struct ipv6_fl_socklist *ipv6_fl_list;
- struct ipv6_txoptions *opt;
+ struct ipv6_txoptions __rcu *opt;
struct sk_buff *pktoptions;
struct sk_buff *rxpmtu;
struct {
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 77763c69e0a6..25f04918640e 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -191,6 +191,7 @@ extern rwlock_t ip6_ra_lock;
*/
struct ipv6_txoptions {
+ atomic_t refcnt;
/* Length of this structure */
int tot_len;
@@ -203,7 +204,7 @@ struct ipv6_txoptions {
struct ipv6_opt_hdr *dst0opt;
struct ipv6_rt_hdr *srcrt; /* Routing Header */
struct ipv6_opt_hdr *dst1opt;
-
+ struct rcu_head rcu;
/* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */
};
@@ -229,6 +230,24 @@ struct ipv6_fl_socklist {
struct ip6_flowlabel *fl;
};
+static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np)
+{
+ struct ipv6_txoptions *opt;
+
+ rcu_read_lock();
+ opt = rcu_dereference(np->opt);
+ if (opt && !atomic_inc_not_zero(&opt->refcnt))
+ opt = NULL;
+ rcu_read_unlock();
+ return opt;
+}
+
+static inline void txopt_put(struct ipv6_txoptions *opt)
+{
+ if (opt && atomic_dec_and_test(&opt->refcnt))
+ kfree_rcu(opt, rcu);
+}
+
extern struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label);
extern struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
struct ip6_flowlabel * fl,