aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-02-19 20:17:52 -0500
committerDavid S. Miller <davem@davemloft.net>2008-02-19 20:17:52 -0500
commite2b58a67b91dec07dfb40ca2056c64011ce8489d (patch)
tree46e258092c2401b9cf6bc232ecfe96248a51982e
parent94cb1503c799c0197e7ef5bad606fee5c84b99d8 (diff)
[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: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv4/netfilter/ip_queue.c12
-rw-r--r--net/ipv6/netfilter/ip6_queue.c10
-rw-r--r--net/netfilter/nfnetlink_queue.c10
3 files changed, 19 insertions, 13 deletions
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
index 6bda1102851b..fe05da41d6ba 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -283,8 +283,8 @@ static int
283ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e) 283ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
284{ 284{
285 int diff; 285 int diff;
286 int err;
287 struct iphdr *user_iph = (struct iphdr *)v->payload; 286 struct iphdr *user_iph = (struct iphdr *)v->payload;
287 struct sk_buff *nskb;
288 288
289 if (v->data_len < sizeof(*user_iph)) 289 if (v->data_len < sizeof(*user_iph))
290 return 0; 290 return 0;
@@ -296,14 +296,16 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
296 if (v->data_len > 0xFFFF) 296 if (v->data_len > 0xFFFF)
297 return -EINVAL; 297 return -EINVAL;
298 if (diff > skb_tailroom(e->skb)) { 298 if (diff > skb_tailroom(e->skb)) {
299 err = pskb_expand_head(e->skb, 0, 299 nskb = skb_copy_expand(e->skb, 0,
300 diff - skb_tailroom(e->skb), 300 diff - skb_tailroom(e->skb),
301 GFP_ATOMIC); 301 GFP_ATOMIC);
302 if (err) { 302 if (!nskb) {
303 printk(KERN_WARNING "ip_queue: error " 303 printk(KERN_WARNING "ip_queue: error "
304 "in mangle, dropping packet: %d\n", -err); 304 "in mangle, dropping packet\n");
305 return err; 305 return -ENOMEM;
306 } 306 }
307 kfree_skb(e->skb);
308 e->skb = nskb;
307 } 309 }
308 skb_put(e->skb, diff); 310 skb_put(e->skb, diff);
309 } 311 }
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
index e869916b05f1..cc2f9afcf808 100644
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -285,8 +285,8 @@ static int
285ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e) 285ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
286{ 286{
287 int diff; 287 int diff;
288 int err;
289 struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload; 288 struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
289 struct sk_buff *nskb;
290 290
291 if (v->data_len < sizeof(*user_iph)) 291 if (v->data_len < sizeof(*user_iph))
292 return 0; 292 return 0;
@@ -298,14 +298,16 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
298 if (v->data_len > 0xFFFF) 298 if (v->data_len > 0xFFFF)
299 return -EINVAL; 299 return -EINVAL;
300 if (diff > skb_tailroom(e->skb)) { 300 if (diff > skb_tailroom(e->skb)) {
301 err = pskb_expand_head(e->skb, 0, 301 nskb = skb_copy_expand(e->skb, 0,
302 diff - skb_tailroom(e->skb), 302 diff - skb_tailroom(e->skb),
303 GFP_ATOMIC); 303 GFP_ATOMIC);
304 if (err) { 304 if (!nskb) {
305 printk(KERN_WARNING "ip6_queue: OOM " 305 printk(KERN_WARNING "ip6_queue: OOM "
306 "in mangle, dropping packet\n"); 306 "in mangle, dropping packet\n");
307 return err; 307 return -ENOMEM;
308 } 308 }
309 kfree_skb(e->skb);
310 e->skb = nskb;
309 } 311 }
310 skb_put(e->skb, diff); 312 skb_put(e->skb, diff);
311 } 313 }
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index a48b20fe9cd6..0043d3a9f87e 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -443,8 +443,8 @@ err_out:
443static int 443static int
444nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) 444nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
445{ 445{
446 struct sk_buff *nskb;
446 int diff; 447 int diff;
447 int err;
448 448
449 diff = data_len - e->skb->len; 449 diff = data_len - e->skb->len;
450 if (diff < 0) { 450 if (diff < 0) {
@@ -454,14 +454,16 @@ nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
454 if (data_len > 0xFFFF) 454 if (data_len > 0xFFFF)
455 return -EINVAL; 455 return -EINVAL;
456 if (diff > skb_tailroom(e->skb)) { 456 if (diff > skb_tailroom(e->skb)) {
457 err = pskb_expand_head(e->skb, 0, 457 nskb = skb_copy_expand(e->skb, 0,
458 diff - skb_tailroom(e->skb), 458 diff - skb_tailroom(e->skb),
459 GFP_ATOMIC); 459 GFP_ATOMIC);
460 if (err) { 460 if (!nskb) {
461 printk(KERN_WARNING "nf_queue: OOM " 461 printk(KERN_WARNING "nf_queue: OOM "
462 "in mangle, dropping packet\n"); 462 "in mangle, dropping packet\n");
463 return err; 463 return -ENOMEM;
464 } 464 }
465 kfree_skb(e->skb);
466 e->skb = nskb;
465 } 467 }
466 skb_put(e->skb, diff); 468 skb_put(e->skb, diff);
467 } 469 }