* [PATCH net] xfrm: enforce hard byte lifetime in xfrm_input()
@ 2026-07-24 10:45 Yuxiang Yang
2026-07-27 8:18 ` Steffen Klassert
0 siblings, 1 reply; 2+ messages in thread
From: Yuxiang Yang @ 2026-07-24 10:45 UTC (permalink / raw)
To: netdev
Cc: steffen.klassert, herbert, davem, edumazet, kuba, pabeni, horms,
linux-kernel, Yuxiang Yang, stable, Yizhou Zhao, Ao Wang,
Xuewei Feng, Qi Li, Ke Xu, yyxroy22
xfrm_input() checks an inbound state's hard lifetime before processing
the current packet, but accounts the packet only after the transform
succeeds. The check therefore sees only the previous byte count.
If the current packet makes the new count exceed the hard byte limit,
that packet is still delivered. The state is not expired until the next
matching packet arrives. The effect is limited to the packet that crosses
the configured limit.
This affects only states configured with a finite hard byte limit. The
default XFRM_INF lifetime is unaffected. IPComp makes the issue especially
visible because decompression can increase skb->len before it is accounted.
Account the actual post-transform length, then enforce the finite hard byte
limit before delivering the packet. Use a strict comparison so a packet
that brings the count exactly to the limit remains accepted.
Saturate the byte counter at XFRM_INF - 1 because XFRM_INF is also the
unlimited-lifetime sentinel. Carry a separate saturation flag so a finite
lifetime still expires if the addition overflows or reaches the sentinel.
The transform and replay update have already completed when an overshooting
packet is dropped. Checking the compressed length before the transform
would not enforce the correct limit because IPComp can increase skb->len
during decompression.
With this change applied to net at 78f75d632f74, a local QEMU/TCG test
dropped a 136-byte packet at a hard limit of 135 and accepted the same
packet at limits of 136 and 100000. XfrmInStateExpired increased by 1, 0,
and 0, respectively. A characterization-only kprobe module seeded
curlft.bytes at U64_MAX - 100 for an unlimited lifetime; two packets were
delivered, the counter saturated at U64_MAX - 1, and XfrmInStateExpired
did not change.
This change is limited to hard byte lifetime handling in xfrm_input().
xfrm6_input_addr() and the output path use separate byte accounting and
are not changed here.
The faulty ordering predates the available Git development history, so
there is no accurate introducing commit to reference with a Fixes tag.
Cc: stable@vger.kernel.org
Signed-off-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
---
net/xfrm/xfrm_input.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index eecab337b..6846ccb39 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/overflow.h>
#include <linux/percpu.h>
#include <net/dst.h>
#include <net/ip.h>
@@ -582,6 +583,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
daddr = (xfrm_address_t *)(skb_network_header(skb) +
XFRM_SPI_SKB_CB(skb)->daddroff);
do {
+ bool saturated;
+ u64 bytes, len;
+
sp = skb_sec_path(skb);
if (sp->len == XFRM_MAX_DEPTH) {
@@ -689,10 +693,24 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
xfrm_replay_advance(x, seq);
- x->curlft.bytes += skb->len;
+ len = skb->len;
+ saturated = check_add_overflow(x->curlft.bytes, len, &bytes);
+ if (saturated || bytes == XFRM_INF) {
+ bytes = XFRM_INF - 1;
+ saturated = true;
+ }
+ x->curlft.bytes = bytes;
x->curlft.packets++;
x->lastused = ktime_get_real_seconds();
+ /* IPComp may expand skb->len during the input transform. */
+ if (x->lft.hard_byte_limit != XFRM_INF &&
+ (saturated || bytes > x->lft.hard_byte_limit)) {
+ xfrm_state_check_expire(x);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEEXPIRED);
+ goto drop_unlock;
+ }
+
spin_unlock(&x->lock);
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
--
2.34.1
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH net] xfrm: enforce hard byte lifetime in xfrm_input()
2026-07-24 10:45 [PATCH net] xfrm: enforce hard byte lifetime in xfrm_input() Yuxiang Yang
@ 2026-07-27 8:18 ` Steffen Klassert
0 siblings, 0 replies; 2+ messages in thread
From: Steffen Klassert @ 2026-07-27 8:18 UTC (permalink / raw)
To: Yuxiang Yang
Cc: netdev, herbert, davem, edumazet, kuba, pabeni, horms,
linux-kernel, stable, Yizhou Zhao, Ao Wang, Xuewei Feng, Qi Li,
Ke Xu, yyxroy22
On Fri, Jul 24, 2026 at 10:45:17AM +0000, Yuxiang Yang wrote:
> xfrm_input() checks an inbound state's hard lifetime before processing
> the current packet, but accounts the packet only after the transform
> succeeds. The check therefore sees only the previous byte count.
>
> If the current packet makes the new count exceed the hard byte limit,
> that packet is still delivered. The state is not expired until the next
> matching packet arrives. The effect is limited to the packet that crosses
> the configured limit.
>
> This affects only states configured with a finite hard byte limit. The
> default XFRM_INF lifetime is unaffected. IPComp makes the issue especially
> visible because decompression can increase skb->len before it is accounted.
>
> Account the actual post-transform length, then enforce the finite hard byte
> limit before delivering the packet. Use a strict comparison so a packet
> that brings the count exactly to the limit remains accepted.
>
> Saturate the byte counter at XFRM_INF - 1 because XFRM_INF is also the
> unlimited-lifetime sentinel. Carry a separate saturation flag so a finite
> lifetime still expires if the addition overflows or reaches the sentinel.
>
> The transform and replay update have already completed when an overshooting
> packet is dropped. Checking the compressed length before the transform
> would not enforce the correct limit because IPComp can increase skb->len
> during decompression.
>
> With this change applied to net at 78f75d632f74, a local QEMU/TCG test
> dropped a 136-byte packet at a hard limit of 135 and accepted the same
> packet at limits of 136 and 100000. XfrmInStateExpired increased by 1, 0,
> and 0, respectively. A characterization-only kprobe module seeded
> curlft.bytes at U64_MAX - 100 for an unlimited lifetime; two packets were
> delivered, the counter saturated at U64_MAX - 1, and XfrmInStateExpired
> did not change.
>
> This change is limited to hard byte lifetime handling in xfrm_input().
> xfrm6_input_addr() and the output path use separate byte accounting and
> are not changed here.
>
> The faulty ordering predates the available Git development history, so
> there is no accurate introducing commit to reference with a Fixes tag.
A fix needs a 'Fixes:' tag. If the bug predates the git history,
then use the first commit in the git history for the fixes tag.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
> ---
> net/xfrm/xfrm_input.c | 20 +++++++++++++++++++-
> 1 file changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
> index eecab337b..6846ccb39 100644
> --- a/net/xfrm/xfrm_input.c
> +++ b/net/xfrm/xfrm_input.c
> @@ -14,6 +14,7 @@
> #include <linux/slab.h>
> #include <linux/module.h>
> #include <linux/netdevice.h>
> +#include <linux/overflow.h>
> #include <linux/percpu.h>
> #include <net/dst.h>
> #include <net/ip.h>
> @@ -582,6 +583,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
> daddr = (xfrm_address_t *)(skb_network_header(skb) +
> XFRM_SPI_SKB_CB(skb)->daddroff);
> do {
> + bool saturated;
> + u64 bytes, len;
> +
> sp = skb_sec_path(skb);
>
> if (sp->len == XFRM_MAX_DEPTH) {
> @@ -689,10 +693,24 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
>
> xfrm_replay_advance(x, seq);
>
> - x->curlft.bytes += skb->len;
> + len = skb->len;
> + saturated = check_add_overflow(x->curlft.bytes, len, &bytes);
> + if (saturated || bytes == XFRM_INF) {
> + bytes = XFRM_INF - 1;
> + saturated = true;
> + }
> + x->curlft.bytes = bytes;
> x->curlft.packets++;
> x->lastused = ktime_get_real_seconds();
>
> + /* IPComp may expand skb->len during the input transform. */
Please don't make the xfrm input path more complicated just for
an IPcomp lifebyte check. Nobody uses this anymore and we
discussed to deprecate it at the IPsec Workshop last week.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-27 8:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 10:45 [PATCH net] xfrm: enforce hard byte lifetime in xfrm_input() Yuxiang Yang
2026-07-27 8:18 ` Steffen Klassert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox