net: drop dst before queueing fragments
[ Upstream commit97599dc792] Commit4a94445c9a(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. Commit64f3b9e203(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>
This commit is contained in:
committed by
Ben Hutchings
parent
aab74a8192
commit
09cd035ccd
@ -251,8 +251,7 @@ static void ip_expire(unsigned long arg)
|
||||
if (!head->dev)
|
||||
goto out_rcu_unlock;
|
||||
|
||||
/* skb dst is stale, drop it, and perform route lookup again */
|
||||
skb_dst_drop(head);
|
||||
/* skb has no dst, perform route lookup again */
|
||||
iph = ip_hdr(head);
|
||||
err = ip_route_input_noref(head, iph->daddr, iph->saddr,
|
||||
iph->tos, head->dev);
|
||||
@ -518,8 +517,16 @@ found:
|
||||
qp->q.last_in |= INET_FRAG_FIRST_IN;
|
||||
|
||||
if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
|
||||
qp->q.meat == qp->q.len)
|
||||
return ip_frag_reasm(qp, prev, dev);
|
||||
qp->q.meat == qp->q.len) {
|
||||
unsigned long orefdst = skb->_skb_refdst;
|
||||
|
||||
skb->_skb_refdst = 0UL;
|
||||
err = ip_frag_reasm(qp, prev, dev);
|
||||
skb->_skb_refdst = orefdst;
|
||||
return err;
|
||||
}
|
||||
|
||||
skb_dst_drop(skb);
|
||||
|
||||
write_lock(&ip4_frags.lock);
|
||||
list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list);
|
||||
|
||||
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user