syzbot
WARNING in nf_flow_offload_gc_step
Status:
upstream: reported C repro on 2026/10/31 16:51
Subsystems:
netfilter
Labels:
prio:high
[Documentation on labels]
Reported-by: syzbot+e1a88d8b7e4cf6b51d85@syzkaller.appspotmail.com
First crash: 4d02h, last: 4d02h
✨ AI-generated root cause summary:
In the Netfilter flowtable subsystem, nf_flow_table_extend_ct_timeout() only accounts for IPPROTO_UDP and IPPROTO_TCP when refreshing conntrack timeouts during garbage collection. However, both tc act_ct and nftables flow offload support offloading IPPROTO_GRE connections to the flowtable. When the garbage collector checks an offloaded GRE connection whose timeout has dropped below min_timeout, the missing case in nf_flow_table_extend_ct_timeout() triggers the default: WARN_ON_ONCE(1) branch. In a production kernel without panic_on_warn, the warning does not crash the system and no memory corruption occurs.
▶
▼
✨ Full AI root cause analysis
Root Cause Analysis
1. Functions, Structs, and Files Involved
- File:
net/netfilter/nf_flow_table_core.c
- Function:
nf_flow_table_extend_ct_timeout() (called via nf_flow_offload_gc_step())
- Other related files/functions:
- Key Structs & Fields:
struct nf_conn: ct->status (specifically IPS_OFFLOAD_BIT), ct->timeout
struct flow_offload: flow->ct, flow->flags
2. Triggering Mechanism and Violated Invariants
- Both
tc's act_ct module (net/sched/act_ct.c) and nftables' flow offload expression (net/netfilter/nft_flow_offload.c) support offloading IPPROTO_GRE (protocol number 47 / 0x2f) flows into the Netfilter flowtable when CONFIG_NF_CT_PROTO_GRE is enabled, setting the IPS_OFFLOAD_BIT on the connection struct nf_conn.
- When GRE traffic flows, connection tracking sets standard timeouts for GRE (e.g.,
ct->proto.gre.timeout = 30*HZ or ct->proto.gre.stream_timeout = 180*HZ in nf_conntrack_gre_packet()). In particular, datapath packet processing or initial creation leaves ct->timeout - nfct_time_stamp well below min_timeout = NF_CT_DAY / 2 (12 hours).
- Periodically, the flowtable garbage collection workqueue thread (
nf_flow_offload_work_gc -> nf_flow_table_gc_run -> nf_flow_table_iterate -> nf_flow_offload_gc_step) inspects active offload entries and calls nf_flow_table_extend_ct_timeout(flow->ct) to keep the underlying conntrack entry from expiring while it is offloaded.
- In
nf_flow_table_extend_ct_timeout(), the logic checks the L4 protocol number of the connection:
u8 l4proto = nf_ct_protonum(ct);
u32 new_timeout = 1;
switch (l4proto) {
case IPPROTO_UDP:
new_timeout = NF_CT_DAY;
break;
case IPPROTO_TCP:
new_timeout = nf_flow_table_tcp_timeout(ct);
break;
default:
WARN_ON_ONCE(1);
break;
}
- Because
l4proto is IPPROTO_GRE (47 / 0x2f, recorded in R14 in the crash dump), the switch statement has no case matching IPPROTO_GRE. It falls into the default: branch, firing WARN_ON_ONCE(1) at line 553.
3. Runtime Consequences in Production Kernel
- Memory Corruption (
false): No out-of-bounds access, invalid free, use-after-free, or corrupted pointers occur. Refcounts on ct (ct->ct_general.use) are balanced with nf_ct_put(ct).
- Denial of Service (
false): In a production kernel without panic_on_warn, WARN_ON_ONCE prints a kernel warning once and allows execution to continue. After hitting the warning, new_timeout (initialized to 1) is applied via cmpxchg(&ct->timeout, ct_timeout, new_timeout + nfct_time_stamp). This causes the conntrack entry's timeout to expire in 1 jiffy, causing conntrack cleanup routines to reclaim the entry earlier than desired. However, this is handled gracefully through standard conntrack and flowtable expiration logic without crashing, hanging, or leaking kernel resources.
- Information Leak (
none): No kernel memory or pointer values are leaked to userspace or network interfaces.
- Subsystem Classification:
IsNetCandidate = true: The issue is entirely within networking (net/netfilter flow table and conntrack subsystems).
IsKVMCandidate = false: Virtualization/KVM is not involved.
Sample crash report:
* Struck through repros no longer work on HEAD.