Re: [PATCH net v2] ipv6: seg6: clear IPv4 control block in End.DT4
From: Andrea Mayer
Date: Sun Aug 09 2026 - 16:25:44 EST
On Wed, 5 Aug 2026 13:58:21 +0200
Nicolas Dichtel <nicolas.dichtel@xxxxxxxxx> wrote:
Thanks Kyle and David for the patch, and Nicolas for catching the End.DX4
gap. Before v3, a few comments on the commit message and on the code.
> Le 05/08/2026 à 12:17, David Lee a écrit :
> > BUG: KASAN: slab-out-of-bounds in __ip_options_echo()
> > Write of size 255
Putting the KASAN trace in the commit message would show the impact
concretely and tell a reviewer how the problem is reached.
Is the reproducer available somewhere? I'm wondering if it could easily be
turned into a test, both to check that the fix works as expected and to
help discover other related issues.
> > Therefore, End.DX4 has the same stale IP6CB-to-IPCB issue. The current
> > patch only clears IPCB in End.DT4 and is incomplete.
> >
> > Do you prefer v3 to be sent out to cover this case too?
> Yes, it would be nice to fix both in the same time.
End.DX4 and End.DT4 both reach the inner IPv4 packet through
decap_and_validate(skb, IPPROTO_IPIP). End.DT46 dispatches to End.DT4, so
it is covered either way. Handling IPPROTO_IPIP inside decap_and_validate()
covers End.DX4 too, so v3 would need a single memset there instead of one
in input_action_end_dt4() and another in input_action_end_dx4(). Something
like:
if (proto == IPPROTO_IPIP) {
int iif = IP6CB(skb)->iif;
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
IPCB(skb)->iif = iif;
}
On the interface index: I would take it from IP6CB->iif before the memset,
rather than from skb->skb_iif. IP6CB->iif is what ip6_rcv_core() recorded
for the outer packet: the interface that packet is considered to have
arrived on. That value does not come from the headers that have just been
removed, so removing them does not make it stale. skb->skb_iif is the
right source in ip_rcv_core(), which runs before l3mdev, but End.DX4 and
End.DT4 run after l3mdev.
When the packet is received on an interface enslaved to a VRF,
ip6_rcv_core() puts that interface in IP6CB->iif and l3mdev then replaces
skb->skb_iif with the L3 master. In decap_and_validate() IP6CB->iif still
holds the receiving interface and skb->skb_iif holds the VRF.
I ran some tests on net/main and on two variants of the fix that differ
only in what they write into IPCB->iif. The packet arrives on ifindex 8,
enslaved to the VRF with ifindex 9, and the receiver is a UDP socket bound
to the VRF reading IP_PKTINFO:
no fix iif from skb->skb_iif iif saved
End.DX4 8 9 8
End.DT4 8 9 8
The no fix column is net/main unchanged: seg6_local.c does not write
IPCB->iif at all, so IP_PKTINFO reports what ip6_rcv_core() wrote in
IP6CB->iif, read through IPCB because iif is at the same offset in
inet_skb_parm and inet6_skb_parm.
So the two are not interchangeable: taking the index from skb->skb_iif
makes IP_PKTINFO report the VRF instead of the receiving interface, taking
it from IP6CB->iif before the memset does not.
IP_PKTINFO is not the only reader of IPCB->iif. With skb->skb_iif that
field holds the VRF (ifindex 9), and after End.DT4 a socket listening on
the receiving interface (ifindex 8) does not see the traffic.
It is also worth reconsidering the Fixes tag: decap_and_validate() has
never cleared the control block, and its first IPPROTO_IPIP caller is
End.DX4 in 891ef8dd2a8d ("ipv6: sr: implement additional seg6local
actions"), which predates 664d6f86868b ("seg6: add support for the SRv6
End.DT4 behavior").
Two side notes, both of which the Sashiko automated review also raises. The
same stale control block is left on the IPv6 side: with an outer Hop-by-Hop
header IP6CB->nhoff points into the inner payload and I see those packets
dropped as an unknown protocol. And input_action_end_dx4() and
end_dt_vrf_core() set the transport header at sizeof(struct iphdr), so it
points inside the options when the inner IPv4 packet carries them. I have a
reproducer for each and will send the two patches separately.
Thanks,
Ciao,
Andrea