Commit 29ffe1a5 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[INET]: Prevent out-of-sync truesize on ip_fragment slow path

When ip_fragment has to hit the slow path the value of skb->truesize
may go out of sync because we would have updated it without changing
the packet length.  This violates the constraints on truesize.

This patch postpones the update of skb->truesize to prevent this.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1987e7b4
...@@ -476,6 +476,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) ...@@ -476,6 +476,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
if (skb_shinfo(skb)->frag_list) { if (skb_shinfo(skb)->frag_list) {
struct sk_buff *frag; struct sk_buff *frag;
int first_len = skb_pagelen(skb); int first_len = skb_pagelen(skb);
int truesizes = 0;
if (first_len - hlen > mtu || if (first_len - hlen > mtu ||
((first_len - hlen) & 7) || ((first_len - hlen) & 7) ||
...@@ -499,7 +500,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) ...@@ -499,7 +500,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
sock_hold(skb->sk); sock_hold(skb->sk);
frag->sk = skb->sk; frag->sk = skb->sk;
frag->destructor = sock_wfree; frag->destructor = sock_wfree;
skb->truesize -= frag->truesize; truesizes += frag->truesize;
} }
} }
...@@ -510,6 +511,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) ...@@ -510,6 +511,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
frag = skb_shinfo(skb)->frag_list; frag = skb_shinfo(skb)->frag_list;
skb_shinfo(skb)->frag_list = NULL; skb_shinfo(skb)->frag_list = NULL;
skb->data_len = first_len - skb_headlen(skb); skb->data_len = first_len - skb_headlen(skb);
skb->truesize -= truesizes;
skb->len = first_len; skb->len = first_len;
iph->tot_len = htons(first_len); iph->tot_len = htons(first_len);
iph->frag_off = htons(IP_MF); iph->frag_off = htons(IP_MF);
......
...@@ -636,6 +636,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ...@@ -636,6 +636,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
if (skb_shinfo(skb)->frag_list) { if (skb_shinfo(skb)->frag_list) {
int first_len = skb_pagelen(skb); int first_len = skb_pagelen(skb);
int truesizes = 0;
if (first_len - hlen > mtu || if (first_len - hlen > mtu ||
((first_len - hlen) & 7) || ((first_len - hlen) & 7) ||
...@@ -658,7 +659,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ...@@ -658,7 +659,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
sock_hold(skb->sk); sock_hold(skb->sk);
frag->sk = skb->sk; frag->sk = skb->sk;
frag->destructor = sock_wfree; frag->destructor = sock_wfree;
skb->truesize -= frag->truesize; truesizes += frag->truesize;
} }
} }
...@@ -689,6 +690,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ...@@ -689,6 +690,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
first_len = skb_pagelen(skb); first_len = skb_pagelen(skb);
skb->data_len = first_len - skb_headlen(skb); skb->data_len = first_len - skb_headlen(skb);
skb->truesize -= truesizes;
skb->len = first_len; skb->len = first_len;
ipv6_hdr(skb)->payload_len = htons(first_len - ipv6_hdr(skb)->payload_len = htons(first_len -
sizeof(struct ipv6hdr)); sizeof(struct ipv6hdr));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment