Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_ct_ftp: prefer skb_linearize

This uses a pseudo-linearization scheme with a 64k global buffer,
but BIG TCP arrival means IPv6 TCP stack can generate skbs
that exceed this size.

Use skb_linearize. It should be possible to rewrite this to properly
deal with segmented skbs (i.e., only do small chunk-wise accesses),
but this is going to be a lot more intrusive than this because every
helper function needs to get the sk_buff instead of a pointer to a raw
data buffer.

In practice, provided we're really looking at FTP control channel packets,
there should never be a case where we deal with huge packets.

Fixes: 7c4e983c4f3c ("net: allow gso_max_size to exceed 65536")
Fixes: 0fe79f28bfaf ("net: allow gro_max_size to exceed 65536")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
c783a29c f3e124c3

+6 -18
+6 -18
net/netfilter/nf_conntrack_ftp.c
··· 34 34 MODULE_ALIAS("ip_conntrack_ftp"); 35 35 MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); 36 36 37 - /* This is slow, but it's simple. --RR */ 38 - static char *ftp_buffer; 39 - 40 - static DEFINE_SPINLOCK(nf_ftp_lock); 41 - 42 37 #define MAX_PORTS 8 43 38 static u_int16_t ports[MAX_PORTS]; 44 39 static unsigned int ports_c; ··· 393 398 return NF_ACCEPT; 394 399 } 395 400 401 + if (unlikely(skb_linearize(skb))) 402 + return NF_DROP; 403 + 396 404 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); 397 405 if (th == NULL) 398 406 return NF_ACCEPT; ··· 409 411 } 410 412 datalen = skb->len - dataoff; 411 413 412 - spin_lock_bh(&nf_ftp_lock); 413 - fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer); 414 - if (!fb_ptr) { 415 - spin_unlock_bh(&nf_ftp_lock); 416 - return NF_ACCEPT; 417 - } 414 + spin_lock_bh(&ct->lock); 415 + fb_ptr = skb->data + dataoff; 418 416 419 417 ends_in_nl = (fb_ptr[datalen - 1] == '\n'); 420 418 seq = ntohl(th->seq) + datalen; ··· 538 544 if (ends_in_nl) 539 545 update_nl_seq(ct, seq, ct_ftp_info, dir, skb); 540 546 out: 541 - spin_unlock_bh(&nf_ftp_lock); 547 + spin_unlock_bh(&ct->lock); 542 548 return ret; 543 549 } 544 550 ··· 565 571 static void __exit nf_conntrack_ftp_fini(void) 566 572 { 567 573 nf_conntrack_helpers_unregister(ftp, ports_c * 2); 568 - kfree(ftp_buffer); 569 574 } 570 575 571 576 static int __init nf_conntrack_ftp_init(void) ··· 572 579 int i, ret = 0; 573 580 574 581 NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master)); 575 - 576 - ftp_buffer = kmalloc(65536, GFP_KERNEL); 577 - if (!ftp_buffer) 578 - return -ENOMEM; 579 582 580 583 if (ports_c == 0) 581 584 ports[ports_c++] = FTP_PORT; ··· 592 603 ret = nf_conntrack_helpers_register(ftp, ports_c * 2); 593 604 if (ret < 0) { 594 605 pr_err("failed to register helpers\n"); 595 - kfree(ftp_buffer); 596 606 return ret; 597 607 } 598 608