aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/reassembly.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2013-04-16 12:55:41 +0000
committerBen Hutchings <ben@decadent.org.uk>2013-05-13 15:02:42 +0100
commit09cd035ccddd63f8e96c549d7bd80a19ecb822ef (patch)
tree346c9b31582667b96497a1f804741b3a2ed2db2d /net/ipv6/reassembly.c
parentaab74a81920693e6852b2adfdcec92411c7e10d7 (diff)
downloadkernel_samsung_smdk4412-09cd035ccddd63f8e96c549d7bd80a19ecb822ef.zip
kernel_samsung_smdk4412-09cd035ccddd63f8e96c549d7bd80a19ecb822ef.tar.gz
kernel_samsung_smdk4412-09cd035ccddd63f8e96c549d7bd80a19ecb822ef.tar.bz2
net: drop dst before queueing fragments
[ Upstream commit 97599dc792b45b1669c3cdb9a4b365aad0232f65 ] Commit 4a94445c9a5c (net: Use ip_route_input_noref() in input path) added a bug in IP defragmentation handling, as non refcounted dst could escape an RCU protected section. Commit 64f3b9e203bd068 (net: ip_expire() must revalidate route) fixed the case of timeouts, but not the general problem. Tom Parkin noticed crashes in UDP stack and provided a patch, but further analysis permitted us to pinpoint the root cause. Before queueing a packet into a frag list, we must drop its dst, as this dst has limited lifetime (RCU protected) When/if a packet is finally reassembled, we use the dst of the very last skb, still protected by RCU and valid, as the dst of the reassembled packet. Use same logic in IPv6, as there is no need to hold dst references. Reported-by: Tom Parkin <tparkin@katalix.com> Tested-by: Tom Parkin <tparkin@katalix.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r--net/ipv6/reassembly.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 2b0a4ca..411fe2c 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -386,8 +386,17 @@ found:
}
if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
- fq->q.meat == fq->q.len)
- return ip6_frag_reasm(fq, prev, dev);
+ fq->q.meat == fq->q.len) {
+ int res;
+ unsigned long orefdst = skb->_skb_refdst;
+
+ skb->_skb_refdst = 0UL;
+ res = ip6_frag_reasm(fq, prev, dev);
+ skb->_skb_refdst = orefdst;
+ return res;
+ }
+
+ skb_dst_drop(skb);
write_lock(&ip6_frags.lock);
list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list);