Re: [PATCH net v3] ip: validate options before echoing them

From: Eric Dumazet

Date: Wed Sep 02 2026 - 04:58:24 EST


On Wed, Sep 2, 2026 at 7:58 AM Daehyeon Ko <4ncienth@xxxxxxxxx> wrote:
>
> IPv4 option metadata stores absolute offsets from the network header in the
> skb control block. That metadata is only safe to use while it still
> describes the header at skb_network_header().
>
> This invariant can be broken in more than one way. An IPv6 UDP packet can
> remain queued while IPV6_ADDRFORM converts its socket to IPv4, after which
> IP_RETOPTS interprets inet6_skb_parm as inet_skb_parm. Also,
> ipmr_cache_report() retains the control block when it builds a PIM
> register whole-packet report, but pushes a new 20-byte header without
> recompiling the option offsets.
>
> In the latter case, a Record-Route offset of 20 points at the original IPv4
> header after the push. __ip_options_echo() then reads the original TOS
> byte as the option length. KASAN reported a 212-byte write into the 40-byte
> stack option-data area on three fresh boots.
>
> __ip_options_echo() currently trusts both the compiled offsets and the
> length bytes found at those offsets. Its fixed-size callers reserve 40
> bytes for option data, while the TCP caller allocates only sopt->optlen
> bytes.
>
> Require the compiled option length to match the current IPv4 header, and
> validate each option's offset, kind, minimum length, source span, and
> remaining destination capacity before copying it. Use sopt->optlen as the
> destination bound so the validation also covers the smaller TCP allocation.
>
> Keep this check local to option echoing. Rejecting every SOL_IP cmsg based
> on the current network-header version drops supported metadata, including
> the physical egress IP_PKTINFO on IPv4 TX timestamps taken after IPv6
> tunnel encapsulation.
>
> With this change, the original IPV6_ADDRFORM input and the PIM register-vif
> input were KASAN-clean. The latter returned MSG_CTRUNC on three fresh
> boots, a normal IPv4 Record-Route IP_RETOPTS cmsg was preserved, and
> tunneled TX timestamp IP_PKTINFO was restored.
>
> Queued IPv6 payloads can still be returned with bounded but incorrect IPv4
> peer or error-queue address metadata after IPV6_ADDRFORM. This change only
> establishes the memory-safety invariant required by option echoing.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Closes: https://lore.kernel.org/r/20260825221922.85651-1-4ncienth@xxxxxxxxx
> Link: https://lore.kernel.org/r/20260829144847.1738294-1-4ncienth@xxxxxxxxx
> Link: https://lore.kernel.org/r/20260901141352.236286-1-pabeni@xxxxxxxxxx
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: LLM
> Signed-off-by: Daehyeon Ko <4ncienth@xxxxxxxxx>

Oh well. Ai reviews will probably raise many bugs in this patch :/

ipmr_cache_report() is a distinct bug and needs its own one liner fix.

When ipmr_cache_report() builds an IGMPMSG_WHOLEPKT or IGMPMSG_WRVIFWHOLE
report, it reallocates headroom and pushes a 20-byte iphdr with ihl = 5, but
inherits pkt->cb with stale IPCB(skb)->opt offsets.
Even if __ip_options_echo() rejects it, ip_cmsg_recv_opts() will still read
stale IPCB(skb)->opt.optlen bytes from ip_hdr(skb) + 1 and leak packet data
as IP_RECVOPTS.
ipmr_cache_report() must clear IPCB(skb)->opt (or memset IPCB(skb)) when
prepending the report header.

Something like:

diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index e5f2b1c6150d2f6b2084c2ebaef6047db869cc87..72f1d620b8760722f805935d0299bac24a9f720c
100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1102,6 +1102,7 @@ static int ipmr_cache_report(const struct mr_table *mrt,
ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
sizeof(struct iphdr));
+ memset(&IPCB(skb)->opt, 0, sizeof(IPCB(skb)->opt));
} else {
/* Copy the IP header */
skb_set_network_header(skb, skb->len);

Then the IPV6_ADDRFORM problem must be dealt without adding defensive
programming in net/ipv4/ip_options.c