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

netfilter: synproxy: fix BUG_ON triggered by corrupt TCP packets

TCP packets hitting the SYN proxy through the SYNPROXY target are not
validated by TCP conntrack. When th->doff is below 5, an underflow happens
when calculating the options length, causing skb_header_pointer() to
return NULL and triggering the BUG_ON().

Handle this case gracefully by checking for NULL instead of using BUG_ON().

Reported-by: Martin Topholm <mph@one.com>
Tested-by: Martin Topholm <mph@one.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Patrick McHardy and committed by
Pablo Neira Ayuso
f4a87e7b d1ee4fea

+22 -12
+1 -1
include/net/netfilter/nf_conntrack_synproxy.h
··· 56 56 57 57 struct tcphdr; 58 58 struct xt_synproxy_info; 59 - extern void synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, 59 + extern bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, 60 60 const struct tcphdr *th, 61 61 struct synproxy_options *opts); 62 62 extern unsigned int synproxy_options_size(const struct synproxy_options *opts);
+7 -3
net/ipv4/netfilter/ipt_SYNPROXY.c
··· 267 267 if (th == NULL) 268 268 return NF_DROP; 269 269 270 - synproxy_parse_options(skb, par->thoff, th, &opts); 270 + if (!synproxy_parse_options(skb, par->thoff, th, &opts)) 271 + return NF_DROP; 271 272 272 273 if (th->syn && !(th->ack || th->fin || th->rst)) { 273 274 /* Initial SYN from client */ ··· 351 350 352 351 /* fall through */ 353 352 case TCP_CONNTRACK_SYN_SENT: 354 - synproxy_parse_options(skb, thoff, th, &opts); 353 + if (!synproxy_parse_options(skb, thoff, th, &opts)) 354 + return NF_DROP; 355 355 356 356 if (!th->syn && th->ack && 357 357 CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { ··· 375 373 if (!th->syn || !th->ack) 376 374 break; 377 375 378 - synproxy_parse_options(skb, thoff, th, &opts); 376 + if (!synproxy_parse_options(skb, thoff, th, &opts)) 377 + return NF_DROP; 378 + 379 379 if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) 380 380 synproxy->tsoff = opts.tsval - synproxy->its; 381 381
+7 -3
net/ipv6/netfilter/ip6t_SYNPROXY.c
··· 282 282 if (th == NULL) 283 283 return NF_DROP; 284 284 285 - synproxy_parse_options(skb, par->thoff, th, &opts); 285 + if (!synproxy_parse_options(skb, par->thoff, th, &opts)) 286 + return NF_DROP; 286 287 287 288 if (th->syn && !(th->ack || th->fin || th->rst)) { 288 289 /* Initial SYN from client */ ··· 373 372 374 373 /* fall through */ 375 374 case TCP_CONNTRACK_SYN_SENT: 376 - synproxy_parse_options(skb, thoff, th, &opts); 375 + if (!synproxy_parse_options(skb, thoff, th, &opts)) 376 + return NF_DROP; 377 377 378 378 if (!th->syn && th->ack && 379 379 CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { ··· 397 395 if (!th->syn || !th->ack) 398 396 break; 399 397 400 - synproxy_parse_options(skb, thoff, th, &opts); 398 + if (!synproxy_parse_options(skb, thoff, th, &opts)) 399 + return NF_DROP; 400 + 401 401 if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) 402 402 synproxy->tsoff = opts.tsval - synproxy->its; 403 403
+7 -5
net/netfilter/nf_synproxy_core.c
··· 24 24 int synproxy_net_id; 25 25 EXPORT_SYMBOL_GPL(synproxy_net_id); 26 26 27 - void 27 + bool 28 28 synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, 29 29 const struct tcphdr *th, struct synproxy_options *opts) 30 30 { ··· 32 32 u8 buf[40], *ptr; 33 33 34 34 ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); 35 - BUG_ON(ptr == NULL); 35 + if (ptr == NULL) 36 + return false; 36 37 37 38 opts->options = 0; 38 39 while (length > 0) { ··· 42 41 43 42 switch (opcode) { 44 43 case TCPOPT_EOL: 45 - return; 44 + return true; 46 45 case TCPOPT_NOP: 47 46 length--; 48 47 continue; 49 48 default: 50 49 opsize = *ptr++; 51 50 if (opsize < 2) 52 - return; 51 + return true; 53 52 if (opsize > length) 54 - return; 53 + return true; 55 54 56 55 switch (opcode) { 57 56 case TCPOPT_MSS: ··· 85 84 length -= opsize; 86 85 } 87 86 } 87 + return true; 88 88 } 89 89 EXPORT_SYMBOL_GPL(synproxy_parse_options); 90 90