Patch "net: fix IP early demux races" has been added to the 4.3-stable tree
Subsystems (4)
This is a note to let you know that I've just added the patch titled
net: fix IP early demux races
to the 4.3-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
net-fix-ip-early-demux-races.patch
and it can be found in the queue-4.3 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From foo@baz Wed Dec 30 19:48:47 PST 2015
From: Eric Dumazet <[email protected]>
Date: Mon, 14 Dec 2015 14:08:53 -0800
Subject: net: fix IP early demux races
From: Eric Dumazet <[email protected]>
[ Upstream commit 5037e9ef9454917b047f9f3a19b4dd179fbf7cd4 ]
David Wilder reported crashes caused by dst reuse.
<quote David>
I am seeing a crash on a distro V4.2.3 kernel caused by a double release of a dst_entry. In ipv4_dst_destroy() the call to list_empty() finds a poisoned next pointer, indicating the dst_entry has already been removed from the list and freed. The crash occurs 18 to 24 hours into a run of a network stress exerciser.
</quote>
Thanks to his detailed report and analysis, we were able to understand the core issue.
IP early demux can associate a dst to skb, after a lookup in TCP/UDP sockets.
When socket cache is not properly set, we want to store into sk->sk_dst_cache the dst for future IP early demux lookups, by acquiring a stable refcount on the dst.
Problem is this acquisition is simply using an atomic_inc(), which works well, unless the dst was queued for destruction from dst_release() noticing dst refcount went to zero, if DST_NOCACHE was set on dst.
We need to make sure current refcount is not zero before incrementing it, or risk double free as David reported.
This patch, being a stable candidate, adds two new helpers, and use them only from IP early demux problematic paths.
It might be possible to merge in net-next skb_dst_force() and skb_dst_force_safe(), but I prefer having the smallest patch for stable kernels : Maybe some skb_dst_force() callers do not expect skb->dst can suddenly be cleared.
Can probably be backported back to linux-3.6 kernels
--- include/net/dst.h | 33 +++++++++++++++++++++++++++++++++ include/net/sock.h | 2 +- net/ipv4/tcp_ipv4.c | 5 ++--- net/ipv6/tcp_ipv6.c | 3 +-- 4 files changed, 37 insertions(+), 6 deletions(-)
include/net/dst.h +33 -0
@@ -322,6 +322,39 @@ static inline void skb_dst_force(struct } } +/** +* dst_hold_safe - Take a reference on a dst if possible +* @dst: pointer to dst entry +* +* This helper returns false if it could not safely +* take a reference on a dst. +*/ +static inline bool dst_hold_safe (struct dst_entry *dst) +{ +if (dst->flags &DST_NOCACHE ) +return atomic_inc_not_zero (&dst->__refcnt); +dst_hold (dst); +return true; +} + +/** +* skb_dst_force_safe - makes sure skb dst is refcounted +* @skb: buffer +* +* If dst is not yet refcounted and not destroyed, grab a ref on it. +*/ +static inline void skb_dst_force_safe (struct sk_buff *skb) +{ +if (skb_dst_is_noref (skb)) { +struct dst_entry *dst =skb_dst (skb); + +if (!dst_hold_safe (dst)) + dst =NULL ; + + skb->_skb_refdst = (unsigned long )dst; + } +} +/** * __skb_tunnel_rx - prepare skb for rx reinsert
include/net/sock.h +1 -1
@@ -801,7 +801,7 @@ void sk_stream_write_space(struct sock *static inline void __sk_add_backlog (struct sock *sk,struct sk_buff *skb) {/* dont let skb dst not refcounted, we are going to leave rcu lock */ -skb_dst_force (skb); +skb_dst_force_safe (skb);if (!sk->sk_backlog.tail) sk->sk_backlog.head = skb;
net/ipv4/tcp_ipv4.c +2 -3
@@ -1509,7 +1509,7 @@ bool tcp_prequeue(struct sock *sk, strucif (likely (sk->sk_rx_dst))skb_dst_drop (skb);else -skb_dst_force (skb); +skb_dst_force_safe (skb);__skb_queue_tail (&tp->ucopy.prequeue, skb); tp->ucopy.memory += skb->truesize;
@@ -1710,8 +1710,7 @@ void inet_sk_rx_dst_set(struct sock *sk, {struct dst_entry *dst =skb_dst (skb); -if (dst) { -dst_hold (dst); +if (dst &&dst_hold_safe (dst)) { sk->sk_rx_dst = dst;inet_sk (sk)->rx_dst_ifindex = skb->skb_iif; }
net/ipv6/tcp_ipv6.c +1 -2
@@ -93,10 +93,9 @@ static void inet6_sk_rx_dst_set(struct s {struct dst_entry *dst =skb_dst (skb); -if (dst) { +if (dst &&dst_hold_safe (dst)) {const struct rt6_info *rt = (const struct rt6_info *)dst; -dst_hold (dst); sk->sk_rx_dst = dst;inet_sk (sk)->rx_dst_ifindex = skb->skb_iif;inet6_sk (sk)->rx_dst_cookie =rt6_get_cookie (rt);
Patches currently in stable-queue which might be from [email protected] are
queue-4.3/inet-tcp-fix-inetpeer_set_addr_v4.patch queue-4.3/ipv6-sctp-clone-options-to-avoid-use-after-free.patch queue-4.3/tcp-restore-fastopen-with-no-data-in-syn-packet.patch queue-4.3/net_sched-make-qdisc_tree_decrease_qlen-work-for-non-mq.patch queue-4.3/net-fix-ip-early-demux-races.patch