NETFILTER: nfnetlink_queue: fix SKB_LINEAR_ASSERT when mangling packet data
authorPatrick McHardy <kaber@trash.net>
Mon, 25 Feb 2008 14:01:01 +0000 (15:01 +0100)
committerChris Wright <chrisw@sous-sol.org>
Mon, 24 Mar 2008 18:47:22 +0000 (11:47 -0700)
Upstream commit e2b58a67:

As reported by Tomas Simonaitis <tomas.simonaitis@gmail.com>, inserting new
data in skbs queued over {ip,ip6,nfnetlink}_queue triggers a SKB_LINEAR_ASSERT
in skb_put().

Going back through the git history, it seems this bug is present since at
least 2.6.12-rc2, probably even since the removal of skb_linearize() for
netfilter.

Linearize non-linear skbs through skb_copy_expand() when enlarging them.
Tested by Thomas, fixes bugzilla #9933.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
net/ipv4/netfilter/ip_queue.c
net/ipv6/netfilter/ip6_queue.c
net/netfilter/nfnetlink_queue.c

index 14d64a383db1a7223ed321e5dedfbc1942af1095..16d0fb37c4c6acbd6f67d798ea6c6a6ce3c30c88 100644 (file)
@@ -336,8 +336,8 @@ static int
 ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
 {
        int diff;
-       int err;
        struct iphdr *user_iph = (struct iphdr *)v->payload;
+       struct sk_buff *nskb;
 
        if (v->data_len < sizeof(*user_iph))
                return 0;
@@ -349,14 +349,16 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
                if (v->data_len > 0xFFFF)
                        return -EINVAL;
                if (diff > skb_tailroom(e->skb)) {
-                       err = pskb_expand_head(e->skb, 0,
+                       nskb = skb_copy_expand(e->skb, 0,
                                               diff - skb_tailroom(e->skb),
                                               GFP_ATOMIC);
-                       if (err) {
+                       if (!nskb) {
                                printk(KERN_WARNING "ip_queue: error "
-                                     "in mangle, dropping packet: %d\n", -err);
-                               return err;
+                                     "in mangle, dropping packet\n");
+                               return -ENOMEM;
                        }
+                       kfree_skb(e->skb);
+                       e->skb = nskb;
                }
                skb_put(e->skb, diff);
        }
index e273605eef855469794849f9cdfd800d659e87e7..710a04ff7712b8d35fc03ceddbf61b2813bb5fab 100644 (file)
@@ -333,8 +333,8 @@ static int
 ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
 {
        int diff;
-       int err;
        struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
+       struct sk_buff *nskb;
 
        if (v->data_len < sizeof(*user_iph))
                return 0;
@@ -346,14 +346,16 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
                if (v->data_len > 0xFFFF)
                        return -EINVAL;
                if (diff > skb_tailroom(e->skb)) {
-                       err = pskb_expand_head(e->skb, 0,
+                       nskb = skb_copy_expand(e->skb, 0,
                                               diff - skb_tailroom(e->skb),
                                               GFP_ATOMIC);
-                       if (err) {
+                       if (!nskb) {
                                printk(KERN_WARNING "ip6_queue: OOM "
                                      "in mangle, dropping packet\n");
-                               return err;
+                               return -ENOMEM;
                        }
+                       kfree_skb(e->skb);
+                       e->skb = nskb;
                }
                skb_put(e->skb, diff);
        }
index 3ceeffcf6f9de65cd5386d0a7e9d73d11cdacdc6..561c974cf63d8aa00ad799c814b5d55ddd8d63ac 100644 (file)
@@ -616,8 +616,8 @@ err_out_put:
 static int
 nfqnl_mangle(void *data, int data_len, struct nfqnl_queue_entry *e)
 {
+       struct sk_buff *nskb;
        int diff;
-       int err;
 
        diff = data_len - e->skb->len;
        if (diff < 0) {
@@ -627,14 +627,16 @@ nfqnl_mangle(void *data, int data_len, struct nfqnl_queue_entry *e)
                if (data_len > 0xFFFF)
                        return -EINVAL;
                if (diff > skb_tailroom(e->skb)) {
-                       err = pskb_expand_head(e->skb, 0,
+                       nskb = skb_copy_expand(e->skb, 0,
                                               diff - skb_tailroom(e->skb),
                                               GFP_ATOMIC);
-                       if (err) {
+                       if (!nskb) {
                                printk(KERN_WARNING "nf_queue: OOM "
                                      "in mangle, dropping packet\n");
-                               return err;
+                               return -ENOMEM;
                        }
+                       kfree_skb(e->skb);
+                       e->skb = nskb;
                }
                skb_put(e->skb, diff);
        }