Commit e2b58a67 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[NETFILTER]: {ip,ip6,nfnetlink}_queue: fix SKB_LINEAR_ASSERT when mangling packet data

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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 94cb1503
...@@ -283,8 +283,8 @@ static int ...@@ -283,8 +283,8 @@ static int
ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e) ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
{ {
int diff; int diff;
int err;
struct iphdr *user_iph = (struct iphdr *)v->payload; struct iphdr *user_iph = (struct iphdr *)v->payload;
struct sk_buff *nskb;
if (v->data_len < sizeof(*user_iph)) if (v->data_len < sizeof(*user_iph))
return 0; return 0;
...@@ -296,14 +296,16 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e) ...@@ -296,14 +296,16 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
if (v->data_len > 0xFFFF) if (v->data_len > 0xFFFF)
return -EINVAL; return -EINVAL;
if (diff > skb_tailroom(e->skb)) { 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), diff - skb_tailroom(e->skb),
GFP_ATOMIC); GFP_ATOMIC);
if (err) { if (!nskb) {
printk(KERN_WARNING "ip_queue: error " printk(KERN_WARNING "ip_queue: error "
"in mangle, dropping packet: %d\n", -err); "in mangle, dropping packet\n");
return err; return -ENOMEM;
} }
kfree_skb(e->skb);
e->skb = nskb;
} }
skb_put(e->skb, diff); skb_put(e->skb, diff);
} }
......
...@@ -285,8 +285,8 @@ static int ...@@ -285,8 +285,8 @@ static int
ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e) ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
{ {
int diff; int diff;
int err;
struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload; struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
struct sk_buff *nskb;
if (v->data_len < sizeof(*user_iph)) if (v->data_len < sizeof(*user_iph))
return 0; return 0;
...@@ -298,14 +298,16 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e) ...@@ -298,14 +298,16 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
if (v->data_len > 0xFFFF) if (v->data_len > 0xFFFF)
return -EINVAL; return -EINVAL;
if (diff > skb_tailroom(e->skb)) { 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), diff - skb_tailroom(e->skb),
GFP_ATOMIC); GFP_ATOMIC);
if (err) { if (!nskb) {
printk(KERN_WARNING "ip6_queue: OOM " printk(KERN_WARNING "ip6_queue: OOM "
"in mangle, dropping packet\n"); "in mangle, dropping packet\n");
return err; return -ENOMEM;
} }
kfree_skb(e->skb);
e->skb = nskb;
} }
skb_put(e->skb, diff); skb_put(e->skb, diff);
} }
......
...@@ -443,8 +443,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) ...@@ -443,8 +443,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
static int static int
nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
{ {
struct sk_buff *nskb;
int diff; int diff;
int err;
diff = data_len - e->skb->len; diff = data_len - e->skb->len;
if (diff < 0) { if (diff < 0) {
...@@ -454,14 +454,16 @@ nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) ...@@ -454,14 +454,16 @@ nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
if (data_len > 0xFFFF) if (data_len > 0xFFFF)
return -EINVAL; return -EINVAL;
if (diff > skb_tailroom(e->skb)) { 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), diff - skb_tailroom(e->skb),
GFP_ATOMIC); GFP_ATOMIC);
if (err) { if (!nskb) {
printk(KERN_WARNING "nf_queue: OOM " printk(KERN_WARNING "nf_queue: OOM "
"in mangle, dropping packet\n"); "in mangle, dropping packet\n");
return err; return -ENOMEM;
} }
kfree_skb(e->skb);
e->skb = nskb;
} }
skb_put(e->skb, diff); skb_put(e->skb, diff);
} }
......
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