From: shaos <shawnq1@163.com>
To: pablo@netfilter.org
Cc: Sung Byeongchan <tjdqudcks0424@naver.com>,
Florian Westphal <fw@strlen.de>, Phil Sutter <phil@nwl.cc>,
"David S . Miller" <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Eric Dumazet <edumazet@kernel.org>,
Simon Horman <horms@kernel.org>,
coreteam@netfilter.org, netfilter-devel@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net] netfilter: xt_dccp: reject short option lengths
Date: Thu, 5 Nov 2026 08:17:35 +0000 [thread overview]
Message-ID: <20261105081735.749379-1-shawnq1@163.com> (raw)
In-Reply-To: <179251068646.3135.16276902372052104830@kernel.org>
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
next parent reply other threads:[~2026-11-05 8:18 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <179251068646.3135.16276902372052104830@kernel.org>
2026-11-05 8:17 ` shaos [this message]
2026-11-05 11:01 ` Pablo Neira Ayuso
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261105081735.749379-1-shawnq1@163.com \
--to=shawnq1@163.com \
--cc=coreteam@netfilter.org \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=fw@strlen.de \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pablo@netfilter.org \
--cc=phil@nwl.cc \
--cc=tjdqudcks0424@naver.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®