CVE-2026-46172
Description
In the Linux kernel, the following vulnerability has been resolved:
ipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
xfrm6_rcv_encap() performs an IPv6 route lookup when the skb does not already have a dst attached. ip6_route_input_lookup() returns a referenced dst entry even when the lookup resolves to an error route.
If dst->error is set, xfrm6_rcv_encap() drops the skb without attaching the dst to the skb and without releasing the reference returned by the lookup. Repeated packets hitting this path therefore leak dst entries.
Release the dst before jumping to the drop path.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A route lookup reference leak in Linux kernel's xfrm6_rcv_encap() can exhaust system memory; fixed by releasing the dst on error.
Vulnerability
In the Linux kernel, xfrm6_rcv_encap() (part of the IPv6 XFRM stack) performs a route lookup via ip6_route_input_lookup() when the socket buffer (skb) does not already have a dst attached. This lookup returns a referenced dst entry even if the route has an error condition (i.e., dst->error is non-zero). The function then drops the skb without releasing that reference, causing a memory leak of dst entries on each error path triggered by such packets. The issue affects Linux kernel versions prior to the inclusion of commit c2efc4956981066df2fef1cc77391b523db6d8e4. [1]
Exploitation
An attacker must be able to send crafted IPv6 packets to a system with XFRM processing enabled (e.g., using IPsec) such that the skb arrives at xfrm6_rcv_encap() without a pre-attached dst. No authentication or special privileges are needed; the attacker only needs network access to the target. The repeated triggering of the error path—sending many packets that cause a route lookup failure—will gradually exhaust kernel memory allocated for dst entries. [1]
Impact
A remote attacker can cause a denial-of-service (DoS) by exhausting system memory through cumulative dst entry leaks. This can lead to system instability or service disruption. There is no confidentiality or integrity impact. The privilege level required is none (network-only access). [1]
Mitigation
The fix is contained in the Linux kernel commit c2efc4956981066df2fef1cc77391b523db6d8e4, which releases the dst reference before jumping to the drop path when dst->error is set. This commit was included in kernel releases starting from 2025-10-29 (per stable tag). Users should update their kernel to the fixed version. No workaround is documented. [1]
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
10bc0fcb9823cdipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
c2efc4956981ipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
554c9b090c8aipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
9d5047782f9bipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
6a5eec0a2a0eipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
bc0fcb9823cdipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
554c9b090c8aipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
6a5eec0a2a0eipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
9d5047782f9bipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
c2efc4956981ipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
2 files changed · +6 −4
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
net/ipv6/xfrm6_protocol.c+3 −2 modifieddiff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b014c..9b586fcec4850b 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing dst_release() call before dropping the skb when ip6_route_input_lookup() returns an error route, causing a reference-count leak."
Attack vector
An attacker sends UDP-encapsulated ESP packets (IPv6) to a host that performs xfrm6_rcv_encap() processing. When the skb has no dst attached, the function calls ip6_route_input_lookup() which returns a referenced dst entry even for error routes. If dst->error is set, the skb is dropped without releasing the dst reference. Repeated packets hitting this code path cause a gradual leak of dst entries, eventually exhausting kernel memory or triggering resource-starvation conditions.
Affected code
The vulnerable function is xfrm6_rcv_encap() in net/ipv6/xfrm6_protocol.c. The fault lies in the code path where ip6_route_input_lookup() returns a dst entry with dst->error set, and the function jumps to the drop label without calling dst_release() on that entry.
What the fix does
The patch adds a dst_release(dst) call inside the error-check block before jumping to the drop label [patch_id=2898077]. This ensures the reference returned by ip6_route_input_lookup() is properly decremented when the route lookup yields an error, preventing the reference-count leak. The fix is minimal: it wraps the existing `if (dst->error)` check in braces and inserts the release call.
Preconditions
- configThe system must be processing UDP-encapsulated ESP (IPsec) packets over IPv6 via xfrm6_rcv_encap().
- inputThe received skb must not already have a dst attached, triggering the route lookup path.
- inputThe route lookup must resolve to an error route (dst->error != 0).
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/554c9b090c8ac5b1c5c507f4badf8d5d0c9c6e13nvd
- git.kernel.org/stable/c/6a5eec0a2a0e99ec9743cf8f1c4082178811d90anvd
- git.kernel.org/stable/c/9d5047782f9bd2829e529df69209bf3232eb561fnvd
- git.kernel.org/stable/c/bc0fcb9823cd0894934cf968b525c575833d7078nvd
- git.kernel.org/stable/c/c2efc4956981066df2fef1cc77391b523db6d8e4nvd
News mentions
0No linked articles in our index yet.