mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] netfilter: xt_dccp: reject short option lengths
       [not found] <179251068646.3135.16276902372052104830@kernel.org>
@ 2026-11-05  8:17 ` shaos
  2026-11-05 11:01   ` Pablo Neira Ayuso
  0 siblings, 1 reply; 2+ messages in thread
From: shaos @ 2026-11-05  8:17 UTC (permalink / raw)
  To: pablo
  Cc: Sung Byeongchan, Florian Westphal, Phil Sutter, David S . Miller,
	Jakub Kicinski, Paolo Abeni, Eric Dumazet, Simon Horman,
	coreteam, netfilter-devel, netdev, linux-kernel

Hello,

The 20 October review of the reserved-option fix pointed out that
`dccp_find_option()` can mistake a short TLV length for the next option
type. The merged fix did not address that case. I reproduced it on
top of commit 2edda78aa4eb (the reserved-option fix).

With INPUT policy DROP and an ACCEPT rule for `--dccp-option 1`, an IPv4
DCCP packet containing options `{32, 1}` is accepted. Option 32 is a
multi-byte option, and length 1 is invalid. The walker advances by one
byte and then treats that length byte as Mandatory option 1. The rule
therefore accepts a malformed packet that does not contain option 1.
This affects firewalls that use DCCP option matches as an allow-list.
The match requires `CONFIG_NETFILTER_XT_MATCH_DCCP`; a remote sender
needs reachability to a firewall that uses an option-based DCCP rule.

The reproducer uses raw sockets and a local IPv4 DCCP packet. The QEMU
runs show the false match on the original kernel and after the merged
reserved-option fix. With this patch, the malformed packet is dropped.
A packet with a real Mandatory option 1 is accepted; without option 1,
it is dropped.

The earlier review is here:
https://lists.openwall.net/netdev/2026/10/20/477

Tested source revisions:
- Vulnerable: c03c1a14507dfaf8b856c2608f9ac216021fc222 (Linux 7.3)
- Vulnerable tree: 30fd2ef82912c26cf3a58ad0ba6afc375fa851ac
- Prior fix control: 2edda78aa4eb04cd8f112a81897510ee0cdd7099
- Patch control: 2edda78aa4eb04cd8f112a81897510ee0cdd7099 plus this
  patch

Git tree at patch base: 8644625317c9d6e32244509e29387fb0c80af0f4
Compiler: gcc 11.4.0, GNU ld 2.38
QEMU: 6.2.0, x86-64 TCG, 1024 MiB, 2 CPUs

Console output:
https://gist.githubusercontent.com/shaos26/a39f036057ddd1c1b78ce2da114b8f3a/raw/6a95ab42fd7f47c2e0113c55bc80a7e86002f222/console-output.txt
Kernel config:
https://gist.githubusercontent.com/shaos26/a39f036057ddd1c1b78ce2da114b8f3a/raw/f60c4bca22a42434cac67478e74d6f7c5b28442c/kernel-config.txt
PoC source:
https://gist.githubusercontent.com/shaos26/a39f036057ddd1c1b78ce2da114b8f3a/raw/3c45d0d340d804c236503cecf5ffe33f700245d0/poc.c

Check out commit 2edda78aa4eb. From that tree root, copy
`kernel-config.txt` to `.config` and run:

    make ARCH=x86_64 olddefconfig
    make ARCH=x86_64 -j"$(nproc)" bzImage

Compile the PoC with:

    gcc -static -O2 -Wall -Wextra -Werror -o poc poc.c

Boot the kernel in a disposable QEMU guest with BusyBox,
iptables-legacy, and `poc` available. Install this rule:

    iptables-legacy -F INPUT
    iptables-legacy -P INPUT DROP
    iptables-legacy -A INPUT -p 33 -m dccp --dccp-option 1 -j ACCEPT

Run `poc badlen1`, `poc present1`, and `poc absent1`. On the unpatched
kernel, the malformed packet is delivered, the packet with a real
Mandatory option is delivered, and the packet without option 1 is
dropped. With this patch, the malformed packet is dropped and the two
controls keep their expected results. The test needs CAP_NET_RAW to
synthesize and receive the local packet.

The option walker should reject a TLV length below two before advancing:

Fixes: 1d3de414eb20 ("[NETFILTER]: New iptables DCCP protocol header match")
Link: https://lists.openwall.net/netdev/2026/10/20/477
Signed-off-by: shaos <shawnq1@163.com>
---
 net/netfilter/xt_dccp.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c
index 23a4e3e8df7d..e0d7f4374958 100644
--- a/net/netfilter/xt_dccp.c
+++ b/net/netfilter/xt_dccp.c
@@ -62,10 +62,15 @@
 			return true;
 		}
 
-		if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1)
+		if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1) {
 			i++;
-		else
-			i += op[i + 1] ? : 1;
+		} else {
+			if (op[i + 1] < 2) {
+				spin_unlock_bh(&dccp_buflock);
+				goto invalid;
+			}
+			i += op[i + 1];
+		}
 	}
 
 	spin_unlock_bh(&dccp_buflock);
 	return false;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH net] netfilter: xt_dccp: reject short option lengths
  2026-11-05  8:17 ` [PATCH net] netfilter: xt_dccp: reject short option lengths shaos
@ 2026-11-05 11:01   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 2+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-05 11:01 UTC (permalink / raw)
  To: shaos
  Cc: Sung Byeongchan, Florian Westphal, Phil Sutter, David S . Miller,
	Jakub Kicinski, Paolo Abeni, Eric Dumazet, Simon Horman,
	coreteam, netfilter-devel, netdev, linux-kernel

On Thu, Nov 05, 2026 at 08:17:35AM +0000, shaos wrote:
> Hello,
> 
> The 20 October review of the reserved-option fix pointed out that
> `dccp_find_option()` can mistake a short TLV length for the next option
> type. The merged fix did not address that case. I reproduced it on
> top of commit 2edda78aa4eb (the reserved-option fix).
> 
> With INPUT policy DROP and an ACCEPT rule for `--dccp-option 1`, an IPv4
> DCCP packet containing options `{32, 1}` is accepted. Option 32 is a
> multi-byte option, and length 1 is invalid. The walker advances by one
> byte and then treats that length byte as Mandatory option 1. The rule
> therefore accepts a malformed packet that does not contain option 1.
> This affects firewalls that use DCCP option matches as an allow-list.
> The match requires `CONFIG_NETFILTER_XT_MATCH_DCCP`; a remote sender
> needs reachability to a firewall that uses an option-based DCCP rule.
> 
> The reproducer uses raw sockets and a local IPv4 DCCP packet. The QEMU
> runs show the false match on the original kernel and after the merged
> reserved-option fix. With this patch, the malformed packet is dropped.
> A packet with a real Mandatory option 1 is accepted; without option 1,
> it is dropped.
> 
> The earlier review is here:
> https://lists.openwall.net/netdev/2026/10/20/477
> 
> Tested source revisions:
> - Vulnerable: c03c1a14507dfaf8b856c2608f9ac216021fc222 (Linux 7.3)
> - Vulnerable tree: 30fd2ef82912c26cf3a58ad0ba6afc375fa851ac
> - Prior fix control: 2edda78aa4eb04cd8f112a81897510ee0cdd7099
> - Patch control: 2edda78aa4eb04cd8f112a81897510ee0cdd7099 plus this
>   patch
> 
> Git tree at patch base: 8644625317c9d6e32244509e29387fb0c80af0f4
> Compiler: gcc 11.4.0, GNU ld 2.38
> QEMU: 6.2.0, x86-64 TCG, 1024 MiB, 2 CPUs
> 
> Console output:
> https://gist.githubusercontent.com/shaos26/a39f036057ddd1c1b78ce2da114b8f3a/raw/6a95ab42fd7f47c2e0113c55bc80a7e86002f222/console-output.txt
> Kernel config:
> https://gist.githubusercontent.com/shaos26/a39f036057ddd1c1b78ce2da114b8f3a/raw/f60c4bca22a42434cac67478e74d6f7c5b28442c/kernel-config.txt
> PoC source:
> https://gist.githubusercontent.com/shaos26/a39f036057ddd1c1b78ce2da114b8f3a/raw/3c45d0d340d804c236503cecf5ffe33f700245d0/poc.c
> 
> Check out commit 2edda78aa4eb. From that tree root, copy
> `kernel-config.txt` to `.config` and run:
> 
>     make ARCH=x86_64 olddefconfig
>     make ARCH=x86_64 -j"$(nproc)" bzImage
> 
> Compile the PoC with:
> 
>     gcc -static -O2 -Wall -Wextra -Werror -o poc poc.c
> 
> Boot the kernel in a disposable QEMU guest with BusyBox,
> iptables-legacy, and `poc` available. Install this rule:
> 
>     iptables-legacy -F INPUT
>     iptables-legacy -P INPUT DROP
>     iptables-legacy -A INPUT -p 33 -m dccp --dccp-option 1 -j ACCEPT
> 
> Run `poc badlen1`, `poc present1`, and `poc absent1`. On the unpatched
> kernel, the malformed packet is delivered, the packet with a real
> Mandatory option is delivered, and the packet without option 1 is
> dropped. With this patch, the malformed packet is dropped and the two
> controls keep their expected results. The test needs CAP_NET_RAW to
> synthesize and receive the local packet.
> 
> The option walker should reject a TLV length below two before advancing:
> 
> Fixes: 1d3de414eb20 ("[NETFILTER]: New iptables DCCP protocol header match")
> Link: https://lists.openwall.net/netdev/2026/10/20/477
> Signed-off-by: shaos <shawnq1@163.com>

This needs a real name.

> ---
>  net/netfilter/xt_dccp.c | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)
> 
> diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c
> index 23a4e3e8df7d..e0d7f4374958 100644
> --- a/net/netfilter/xt_dccp.c
> +++ b/net/netfilter/xt_dccp.c
> @@ -62,10 +62,15 @@
>  			return true;
>  		}
>  
> -		if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1)
> +		if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1) {
>  			i++;
> -		else
> -			i += op[i + 1] ? : 1;
> +		} else {
> +			if (op[i + 1] < 2) {
> +				spin_unlock_bh(&dccp_buflock);
> +				goto invalid;
> +			}
> +			i += op[i + 1];
> +		}
>  	}
>  
>  	spin_unlock_bh(&dccp_buflock);
>  	return false;
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-05 11:02 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <179251068646.3135.16276902372052104830@kernel.org>
2026-11-05  8:17 ` [PATCH net] netfilter: xt_dccp: reject short option lengths shaos
2026-11-05 11:01   ` Pablo Neira Ayuso

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®