Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f4a87e7b authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso
Browse files

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: default avatarMartin Topholm <mph@one.com>
Tested-by: default avatarMartin Topholm <mph@one.com>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent d1ee4fea
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ struct synproxy_options {

struct tcphdr;
struct xt_synproxy_info;
extern void synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
extern bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
				   const struct tcphdr *th,
				   struct synproxy_options *opts);
extern unsigned int synproxy_options_size(const struct synproxy_options *opts);
+7 −3
Original line number Diff line number Diff line
@@ -267,7 +267,8 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
	if (th == NULL)
		return NF_DROP;

	synproxy_parse_options(skb, par->thoff, th, &opts);
	if (!synproxy_parse_options(skb, par->thoff, th, &opts))
		return NF_DROP;

	if (th->syn && !(th->ack || th->fin || th->rst)) {
		/* Initial SYN from client */
@@ -350,7 +351,8 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum,

		/* fall through */
	case TCP_CONNTRACK_SYN_SENT:
		synproxy_parse_options(skb, thoff, th, &opts);
		if (!synproxy_parse_options(skb, thoff, th, &opts))
			return NF_DROP;

		if (!th->syn && th->ack &&
		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
@@ -373,7 +375,9 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum,
		if (!th->syn || !th->ack)
			break;

		synproxy_parse_options(skb, thoff, th, &opts);
		if (!synproxy_parse_options(skb, thoff, th, &opts))
			return NF_DROP;

		if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
			synproxy->tsoff = opts.tsval - synproxy->its;

+7 −3
Original line number Diff line number Diff line
@@ -282,7 +282,8 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
	if (th == NULL)
		return NF_DROP;

	synproxy_parse_options(skb, par->thoff, th, &opts);
	if (!synproxy_parse_options(skb, par->thoff, th, &opts))
		return NF_DROP;

	if (th->syn && !(th->ack || th->fin || th->rst)) {
		/* Initial SYN from client */
@@ -372,7 +373,8 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum,

		/* fall through */
	case TCP_CONNTRACK_SYN_SENT:
		synproxy_parse_options(skb, thoff, th, &opts);
		if (!synproxy_parse_options(skb, thoff, th, &opts))
			return NF_DROP;

		if (!th->syn && th->ack &&
		    CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
@@ -395,7 +397,9 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum,
		if (!th->syn || !th->ack)
			break;

		synproxy_parse_options(skb, thoff, th, &opts);
		if (!synproxy_parse_options(skb, thoff, th, &opts))
			return NF_DROP;

		if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
			synproxy->tsoff = opts.tsval - synproxy->its;

+7 −5
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@
int synproxy_net_id;
EXPORT_SYMBOL_GPL(synproxy_net_id);

void
bool
synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
		       const struct tcphdr *th, struct synproxy_options *opts)
{
@@ -32,7 +32,8 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
	u8 buf[40], *ptr;

	ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
	BUG_ON(ptr == NULL);
	if (ptr == NULL)
		return false;

	opts->options = 0;
	while (length > 0) {
@@ -41,16 +42,16 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,

		switch (opcode) {
		case TCPOPT_EOL:
			return;
			return true;
		case TCPOPT_NOP:
			length--;
			continue;
		default:
			opsize = *ptr++;
			if (opsize < 2)
				return;
				return true;
			if (opsize > length)
				return;
				return true;

			switch (opcode) {
			case TCPOPT_MSS:
@@ -84,6 +85,7 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
			length -= opsize;
		}
	}
	return true;
}
EXPORT_SYMBOL_GPL(synproxy_parse_options);