diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2008-01-28 23:45:20 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-31 22:27:07 -0500 |
commit | 29ffe1a5c52dae13b6efead97aab9b058f38fce4 (patch) | |
tree | bcca1391f02a01cf1b2d5942a06d392ac0f5be11 | |
parent | 1987e7b4855fcb6a866d3279ee9f2890491bc34d (diff) |
[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: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/ip_output.c | 4 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 4 |
2 files changed, 6 insertions, 2 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 18070ca65771..6e4d5f493bf9 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -476,6 +476,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) | |||
476 | if (skb_shinfo(skb)->frag_list) { | 476 | if (skb_shinfo(skb)->frag_list) { |
477 | struct sk_buff *frag; | 477 | struct sk_buff *frag; |
478 | int first_len = skb_pagelen(skb); | 478 | int first_len = skb_pagelen(skb); |
479 | int truesizes = 0; | ||
479 | 480 | ||
480 | if (first_len - hlen > mtu || | 481 | if (first_len - hlen > mtu || |
481 | ((first_len - hlen) & 7) || | 482 | ((first_len - hlen) & 7) || |
@@ -499,7 +500,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) | |||
499 | sock_hold(skb->sk); | 500 | sock_hold(skb->sk); |
500 | frag->sk = skb->sk; | 501 | frag->sk = skb->sk; |
501 | frag->destructor = sock_wfree; | 502 | frag->destructor = sock_wfree; |
502 | skb->truesize -= frag->truesize; | 503 | truesizes += frag->truesize; |
503 | } | 504 | } |
504 | } | 505 | } |
505 | 506 | ||
@@ -510,6 +511,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) | |||
510 | frag = skb_shinfo(skb)->frag_list; | 511 | frag = skb_shinfo(skb)->frag_list; |
511 | skb_shinfo(skb)->frag_list = NULL; | 512 | skb_shinfo(skb)->frag_list = NULL; |
512 | skb->data_len = first_len - skb_headlen(skb); | 513 | skb->data_len = first_len - skb_headlen(skb); |
514 | skb->truesize -= truesizes; | ||
513 | skb->len = first_len; | 515 | skb->len = first_len; |
514 | iph->tot_len = htons(first_len); | 516 | iph->tot_len = htons(first_len); |
515 | iph->frag_off = htons(IP_MF); | 517 | iph->frag_off = htons(IP_MF); |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 15c4f6cee3e6..cfe9e707883c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -636,6 +636,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
636 | 636 | ||
637 | if (skb_shinfo(skb)->frag_list) { | 637 | if (skb_shinfo(skb)->frag_list) { |
638 | int first_len = skb_pagelen(skb); | 638 | int first_len = skb_pagelen(skb); |
639 | int truesizes = 0; | ||
639 | 640 | ||
640 | if (first_len - hlen > mtu || | 641 | if (first_len - hlen > mtu || |
641 | ((first_len - hlen) & 7) || | 642 | ((first_len - hlen) & 7) || |
@@ -658,7 +659,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
658 | sock_hold(skb->sk); | 659 | sock_hold(skb->sk); |
659 | frag->sk = skb->sk; | 660 | frag->sk = skb->sk; |
660 | frag->destructor = sock_wfree; | 661 | frag->destructor = sock_wfree; |
661 | skb->truesize -= frag->truesize; | 662 | truesizes += frag->truesize; |
662 | } | 663 | } |
663 | } | 664 | } |
664 | 665 | ||
@@ -689,6 +690,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
689 | 690 | ||
690 | first_len = skb_pagelen(skb); | 691 | first_len = skb_pagelen(skb); |
691 | skb->data_len = first_len - skb_headlen(skb); | 692 | skb->data_len = first_len - skb_headlen(skb); |
693 | skb->truesize -= truesizes; | ||
692 | skb->len = first_len; | 694 | skb->len = first_len; |
693 | ipv6_hdr(skb)->payload_len = htons(first_len - | 695 | ipv6_hdr(skb)->payload_len = htons(first_len - |
694 | sizeof(struct ipv6hdr)); | 696 | sizeof(struct ipv6hdr)); |