HIGH Introduced in 6.12
tcp ReqskTimer UAF
CVE-2026-99270
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
KernelScan AI8.1HIGH
01Description
In the Linux kernel, the following vulnerability has been resolved: tcp: Fix potential UAF in reqsk_timer_handler(). When TCP socket migration fails at inet_ehash_insert() in reqsk_timer_handler(), we jump to the no_ownership: label and free the new reqsk immediately with __reqsk_free(). Thus, we must stop the new reqsk's timer before jumping to the label, but the timer might be missed since the cited commit, resulting in UAF. As we are in the original reqsk's timer context, we can safely call timer_delete_sync() for the new reqsk. Let's pass false to __inet_csk_reqsk_queue_drop() to stop the new reqsk's timer.
02KernelScan AI Analysis
Risk summary
Remote-reachable only on servers that opt into TCP listener migration (`net.ipv4.tcp_migrate_req=1` or a `BPF_SK_REUSEPORT_SELECT_OR_MIGRATE` program on an SO_REUSEPORT group) **and** close a listener while handshakes are pending; default systems never execute the vulnerable branch, and the realistic remote outcome is a kernel panic, not RCE. The more practical risk is **local**: an unprivileged user with user namespaces can enable the per-netns sysctl, own both listeners and drive the race over loopback, making this a plausible privilege escalation on hosts that allow unprivileged userns. No long-term kernel has the fix as of 2026-11-04 (fixed in 7.1 and later); set `tcp_migrate_req=0` and restrict unprivileged user namespaces.
On the KernelScan blog
Vulnerability analysis
Type: use-after-free of a cloned `request_sock` (`nreq`) whose `rsk_timer` is still armed.
Root cause: in `reqsk_timer_handler()` the migration branch (listener no longer `TCP_LISTEN`, `reuseport_migrate_sock()` picks a new listener) clones the req and re-arms the clone's timer with `mod_timer()`. If `inet_ehash_insert(nreq, oreq)` then fails because `oreq` was unhashed concurrently (final ACK / RST won the `own_req` race on another CPU), the code calls `__inet_csk_reqsk_queue_drop(sk_listener, nreq, true)` and jumps to `no_ownership:` which `__reqsk_free(nreq)`. `from_timer=true` skips `timer_delete_sync()`, so the freed object stays linked in the timer wheel and its handler later runs on freed memory.
Real introducer: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink()", v6.12-rc4, backported to 5.15.170 / 6.1.115 / 6.6.59 / 6.11.6). Before it, the drop used `timer_pending() && del_timer_sync()`, and the just-armed, CPU-pinned `nreq` timer was always pending, so it was deleted. The `Fixes:` tag (83fccfc3940c, 4.2) and the 5.14 lock (c905dee62232, migration added) are both broader than real exposure.
Fix (97c8a3c1f73d, v7.1-rc4): pass `from_timer=false` for `nreq`; safe because we run in `oreq`'s timer, not `nreq`'s.
Preconditions: SO_REUSEPORT group + migration enabled (sysctl default 0, per-netns, writable by userns root) + listener close during pending handshakes + a microsecond race between the timer and ACK processing on another CPU. The `request_sock_TCP` slab is `SLAB_TYPESAFE_BY_RCU`, so attacker SYNs readily reclaim the slot as another reqsk (refcount/timer-list corruption), but control-flow hijack needs cross-cache reclaim plus a KASLR bypass, which is unrealistic remotely.
Lifecycle
03Fix Versions
| Branch | Introduced | Fixed in | Patch commit |
|---|---|---|---|
| 5.15 | 5.15.170 | — | |
| 6.1 | 6.1.115 | — | |
| 6.11 | 6.11.6 | — | |
| 6.18 | 6.12 | 6.18.61 | 97c8a3c1f73d |
| 6.6 | 6.6.59 | — | |
| mainline | 6.12 | 7.1 | 97c8a3c1f73d |
04CIP Kernels
State of the newest release of each Civil Infrastructure Platform SLTS tree, from CIP's own fix records.
| CIP tree | State |
|---|---|
| cip/6.12 | Affected — no CIP fix yet (latest 6.12.114-cip34) |
| cip/6.1 | Affected — no CIP fix yet (latest 6.1.190-cip62) |
| cip/6.1-rt | Affected — no CIP fix yet (latest 6.1.189-cip61-rt32) |