From: Arnaud Ebalard Date: Tue, 29 Apr 2008 10:16:34 +0000 (-0700) Subject: netfilter: {nfnetlink,ip,ip6}_queue: fix skb_over_panic when enlarging packets X-Git-Tag: v3.12-rc1~20970^2~15 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9a732ed6d0e126d4c8a818f42a13f3df11755bee;p=kernel%2Fkernel-generic.git netfilter: {nfnetlink,ip,ip6}_queue: fix skb_over_panic when enlarging packets While reinjecting *bigger* modified versions of IPv6 packets using libnetfilter_queue, things work fine on a 2.6.24 kernel (2.6.22 too) but I get the following on recents kernels (2.6.25, trace below is against today's net-2.6 git tree): skb_over_panic: text:c04fddb0 len:696 put:632 head:f7592c00 data:f7592c00 tail:0xf7592eb8 end:0xf7592e80 dev:eth0 ------------[ cut here ]------------ invalid opcode: 0000 [#1] PREEMPT Process sendd (pid: 3657, ti=f6014000 task=f77c31d0 task.ti=f6014000) Stack: c071e638 c04fddb0 000002b8 00000278 f7592c00 f7592c00 f7592eb8 f7592e80 f763c000 f6bc5200 f7592c40 f6015c34 c04cdbfc f6bc5200 00000278 f6015c60 c04fddb0 00000020 f72a10c0 f751b420 00000001 0000000a 000002b8 c065582c Call Trace: [] ? nfqnl_recv_verdict+0x1c0/0x2e0 [] ? skb_put+0x3c/0x40 [] ? nfqnl_recv_verdict+0x1c0/0x2e0 [] ? nfnetlink_rcv_msg+0xf5/0x160 [] ? nfnetlink_rcv_msg+0x1e/0x160 [] ? nfnetlink_rcv_msg+0x0/0x160 [] ? netlink_rcv_skb+0x77/0xa0 [] ? nfnetlink_rcv+0x1c/0x30 [] ? netlink_unicast+0x243/0x2b0 [] ? memcpy_fromiovec+0x4a/0x70 [] ? netlink_sendmsg+0x1c6/0x270 [] ? sock_sendmsg+0xc4/0xf0 [] ? set_next_entity+0x1d/0x50 [] ? autoremove_wake_function+0x0/0x40 [] ? __wake_up_common+0x3e/0x70 [] ? n_tty_receive_buf+0x34f/0x1280 [] ? __wake_up+0x68/0x70 [] ? copy_from_user+0x37/0x70 [] ? verify_iovec+0x2c/0x90 [] ? sys_sendmsg+0x10a/0x230 [] ? __dequeue_entity+0x2a/0xa0 [] ? set_next_entity+0x1d/0x50 [] ? pty_write+0x47/0x60 [] ? tty_default_put_char+0x1b/0x20 [] ? __wake_up+0x49/0x70 [] ? tty_ldisc_deref+0x39/0x90 [] ? tty_write+0x1a0/0x1b0 [] ? sys_socketcall+0x7f/0x260 [] ? sysenter_past_esp+0x6a/0x91 [] ? snd_intel8x0m_probe+0x270/0x6e0 ======================= Code: 00 00 89 5c 24 14 8b 98 9c 00 00 00 89 54 24 0c 89 5c 24 10 8b 40 50 89 4c 24 04 c7 04 24 38 e6 71 c0 89 44 24 08 e8 c4 46 c5 ff <0f> 0b eb fe 55 89 e5 56 89 d6 53 89 c3 83 ec 0c 8b 40 50 39 d0 EIP: [] skb_over_panic+0x5c/0x60 SS:ESP 0068:f6015bf8 Looking at the code, I ended up in nfq_mangle() function (called by nfqnl_recv_verdict()) which performs a call to skb_copy_expand() due to the increased size of data passed to the function. AFAICT, it should ask for 'diff' instead of 'diff - skb_tailroom(e->skb)'. Because the resulting sk_buff has not enough space to support the skb_put(skb, diff) call a few lines later, this results in the call to skb_over_panic(). The patch below asks for allocation of a copy with enough space for mangled packet and the same amount of headroom as old sk_buff. While looking at how the regression appeared (e2b58a67), I noticed the same pattern in ipq_mangle_ipv6() and ipq_mangle_ipv4(). The patch corrects those locations too. Tested with bigger reinjected IPv6 packets (nfqnl_mangle() path), things are ok (2.6.25 and today's net-2.6 git tree). Signed-off-by: Arnaud Ebalard Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index 719be29..26a37ce 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c @@ -296,9 +296,8 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e) if (v->data_len > 0xFFFF) return -EINVAL; if (diff > skb_tailroom(e->skb)) { - nskb = skb_copy_expand(e->skb, 0, - diff - skb_tailroom(e->skb), - GFP_ATOMIC); + nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), + diff, GFP_ATOMIC); if (!nskb) { printk(KERN_WARNING "ip_queue: error " "in mangle, dropping packet\n"); diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index 92a36c9..2eff3ae 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c @@ -298,9 +298,8 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e) if (v->data_len > 0xFFFF) return -EINVAL; if (diff > skb_tailroom(e->skb)) { - nskb = skb_copy_expand(e->skb, 0, - diff - skb_tailroom(e->skb), - GFP_ATOMIC); + nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), + diff, GFP_ATOMIC); if (!nskb) { printk(KERN_WARNING "ip6_queue: OOM " "in mangle, dropping packet\n"); diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 2c9fe5c..3447025 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -454,9 +454,8 @@ nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) if (data_len > 0xFFFF) return -EINVAL; if (diff > skb_tailroom(e->skb)) { - nskb = skb_copy_expand(e->skb, 0, - diff - skb_tailroom(e->skb), - GFP_ATOMIC); + nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), + diff, GFP_ATOMIC); if (!nskb) { printk(KERN_WARNING "nf_queue: OOM " "in mangle, dropping packet\n");