CVE-2026-89533
Critical · 9.8CVSS
9.8
EPSS
0.5%
Published
Sep 11, 2026
CISA due date
—
Cloudanix analysis
A bug in how some Linux file servers handle a high-speed network file-sharing path can let anyone who can reach that service crash the machine or potentially take it over without logging in.
- Exploitation
- Remote Unauthenticated
- Attacker needs
- None
- Complexity
- Moderate
- Impact
- Full Compromise
- Component
- OS kernel
- Class
- Memory Corruption
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
Integer underflow and incorrect DMA-length arithmetic in the Linux kernel NFS-over-RDMA server (svcrdma) when svc_rdma_read_chunk_range() rebuilds a Position-Zero or Call Read-chunk sub-range. A fully consumed segment is not skipped, so dummy.rs_length underflows a u32 toward UINT_MAX; that huge length becomes nr_bvec and drives an oversized kmalloc_array_node() in svc_rdma_get_rw_ctxt() plus wrong DMA mapping in svc_rdma_build_read_segment(), yielding kernel memory corruption, unbounded allocation, and host crash, with a plausible further path to kernel-memory safety violations.
Why it matters to the business
Any host exposing NFS over RDMA can be crashed remotely by an unauthenticated network peer, taking file services and the whole machine offline; memory corruption also implies a risk of kernel-level compromise of the file server. The issue is scored critical (network-reachable, no privileges or user interaction) and has been public for about 34 days, so unpatched NFS/RDMA servers remain a high operational risk even though it is not listed in CISA KEV, is not associated with ransomware, and has no reported in-the-wild exploit maturity.
How an attack unfolds
An unauthenticated attacker who can reach an NFS server with the RDMA transport enabled sends an RPC Call whose Read-chunk segment list and requested offset/length hit the defective skip predicate and residual-length formula. The kernel treats a fully consumed segment as still in-range, underflows the per-segment length, and attempts a multi-mebibyte RDMA work-context allocation and DMA setup. That typically panics or OOM-kills the host; if allocation and mapping succeed with corrupted sizes, kernel heap and DMA state can be corrupted. No named threat actors or public exploit activity are reported.
How to fix it
Install a Linux kernel that includes the svcrdma fix for this issue (corrected >= skip of fully consumed segments, residual length capped with offset inside min_t, immediate success on zero-length ranges, and loop exit once the requested length is consumed). Apply the vendor or distro kernel update that cites this CVE; no per-package fixed version list was supplied with this record, so use the supplier advisory for the exact kernel package. If NFS over RDMA is not required, unload or blacklist svcrdma/xprtrdma/rpcrdma, disable nfsrdma, and stop advertising RDMA NFS. Restrict who can reach NFS/RDMA endpoints with network ACLs or fabric zoning until kernels are patched.
How to confirm the fix worked
Confirm the running kernel changelog, distro errata, or upstream commit contains the svcrdma read_chunk_range arithmetic fix, then reboot into that kernel. Verify svcrdma/rpcrdma module versions and that nfs-utils/rdma configuration matches the patched kernel. After patching, exercise legitimate NFS-over-RDMA I/O (including Call/Read chunks that end on segment boundaries and zero-length trailing ranges) and confirm there are no new oopses, unexpected -EINVAL on valid Calls, or oversized kmalloc warnings. On unpatched hosts, treat loaded svcrdma plus an enabled nfsrdma listener as exposed.
Typical attack path
- Discover a Linux NFS server with RDMA (svcrdma) enabled and reachable on the RDMA fabric or associated IP/port path.
- Establish a normal NFS/RDMA session as an unauthenticated or unprivileged network peer.
- Submit an RPC Call that includes a Position-Zero or Call Read chunk whose segment lengths and byte offset/length combination exercise the off-by-one skip and residual-length bugs.
- Trigger u32 underflow so the server computes an enormous DMA/bvec count and performs an oversized allocation and mapping.
- Cause kernel oops, OOM, or memory corruption on the NFS server, resulting in denial of service and, if corruption is controllable, potential kernel-level impact.
How to detect it
There are no public exploit signatures or named actor TTPs. Hunt retrospectively on NFS/RDMA servers for kernel oops, BUG, panic, or OOM around svcrdma allocation and DMA setup, especially large or failed kmalloc_array_node allocations while handling Read chunks. Review dmesg/journal/kern.log and crash dumps for stacks through svc_rdma_read_chunk_range, svc_rdma_read_call_chunk, svc_rdma_build_read_segment, and svc_rdma_get_rw_ctxt. Correlate timestamps with unusual inbound RDMA or NFS/RDMA sessions, connection storms, or client disconnects. A single crash without a matching patch window is more consistent with accidental trigger or probing than confirmed targeted exploitation, given exploit maturity of None.
- svc_rdma_read_chunk_range
- svc_rdma_read_call_chunk
- svc_rdma_build_read_segment
- svc_rdma_get_rw_ctxt
- svcrdma
- kmalloc_array_node
- Call Trace:.*svcrdma
- Oops:.*svcrdma
- Out of memory:.*svcrdma
What your security team should do
- Prioritize kernel updates on all NFS servers with RDMA enabled; treat as urgent given unauthenticated remote reachability even though the CVE is not in KEV.
- Inventory hosts with svcrdma, rpcrdma, or nfsrdma loaded and disable NFS over RDMA where it is not a business requirement.
- Network-restrict RDMA and NFS listener endpoints to known clients until patches are confirmed.
- Watch kernel logs on NFS/RDMA servers for oops, OOM, and oversized allocation in the RDMA server path as a stopgap detection control.
- Track distro kernel advisories for this CVE and verify rollout with configuration management rather than assuming a generic kernel bump is sufficient.
The record
In the Linux kernel, the following vulnerability has been resolved: svcrdma: Fix offset arithmetic in read_chunk_range svc_rdma_read_chunk_range() walks a Read chunk's segment list to build a sub-range starting at byte offset and spanning length bytes for a Position-Zero or Call chunk. Two arithmetic defects in the per-segment loop produce wrong DMA lengths and a u32 underflow: pcl_for_each_segment(segment, chunk) { if (offset > segment->rs_length) { offset -= segment->rs_length; continue; } dummy.rs_handle = segment->rs_handle; dummy.rs_length = min_t(u32, length, segment->rs_length) - offset; dummy.rs_offset = segment->rs_offset + offset; First, the skip predicate uses '>' instead of '>='. When offset equals the segment's full rs_length, the segment is fully consumed and should be skipped, but the loop falls through into the body. The resulting dummy.rs_length is min_t(u32, length, rs_length) - rs_length, which underflows to a near-UINT_MAX u32 when length is smaller than rs_length, or is zero otherwise. Second, the length formula subtracts offset from the min_t() result rather than from segment->rs_length before the cap. For offset > 0 the segment's residual is rs_length - offset, not rs_length, so the cap must be applied to the residual. With the current bracketing, whenever length is smaller than rs_length - offset the per-segment length becomes length - offset instead of length, silently dropping offset bytes from the rebuilt chunk. Combined with the boundary case above it also enables the u32 underflow path, which propagates a huge nr_bvec into svc_rdma_build_read_segment() and a multi-MiB kmalloc_array_node() in svc_rdma_get_rw_ctxt(). Additionally, svc_rdma_read_call_chunk() can invoke this function with length == 0 when the last Read chunk ends exactly at the end of the Call chunk. With the corrected >= predicate, every segment is skipped and the function returns the initial -EINVAL, rejecting a valid request. Return success immediately when length is zero. Also break out of the loop once length is fully consumed to avoid passing zero-length segments to svc_rdma_build_read_segment(). Fix by using '>=' so a fully-consumed segment is skipped, by moving '- offset' inside min_t() so the cap is applied to the segment's residual length, by returning success for zero-length requests, and by stopping iteration when the requested range has been consumed.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|