In the Linux kernel, the following vulnerability has been resolved:
SUNRPC: xdr_buf_trim: clamp buf->len to avoid underflow
xdr_buf_trim() trims len bytes from the tail of an xdr_buf by
walking the tail, pages, and head iovecs. Each per-section step
uses min_t() so it never removes more bytes than that section
holds, but the final accounting at the fix_len label subtracts the
total bytes actually consumed from buf->len without any clamp:
<pre>
fix_len:
buf->len -= (len - trim);
</pre>
When the caller has set buf->len to a value smaller than the sum
of the iov_lens, (len - trim) can exceed buf->len and the unsigned
subtraction wraps to near UINT_MAX. gss_krb5_unwrap_v2() reaches
xdr_buf_trim() in exactly that state:
<pre>
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
</pre>
buf->len is a small wire-derived value while the iov_lens are at
page scale, so the per-section loops legitimately consume far more
bytes than buf->len records. The wrapped buf->len then propagates
as the authoritative stream bound into every downstream XDR
decoder.
Fix by clamping the decrement so buf->len bottoms out at zero:
<pre>
buf->len -= min_t(unsigned int, buf->len, len - trim);
</pre>
On the normal path where the iov_lens sum to buf->len, (len - trim)
is always <= buf->len and the result is identical to before. No
callers change behavior outside the underflow case.
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS Score: 9.8
AV:N - xdr_buf_trim() is reached from nfsd RPCSEC_GSS privacy unwrap on TCP/2049 (svcauth_gss_unwrap_priv → gss_unwrap → gss_krb5_unwrap_v2) and from the NFS client's gss_unwrap_resp_priv() on every krb5p reply, so a remote NFS peer delivers the wrap token over the network.
AC:L - A peer holding the GSS session keys can encrypt a RFC 4121 wrap token so that after decrypt, buf->len is a small wire-derived value while iov_lens remain page-scale; xdr_buf_trim() then subtracts more than buf->len and wraps, with no race or layout outside attacker control.
PR:N - A malicious or compromised NFS server that already shares a krb5p GSS context needs no account or capability on the victim client; ordinary krb5p replies and NFSv4.0 GSS callbacks are processed without local privileges on that host.
UI:N - Once a krb5p NFS mount exists, the client unwraps every privacy-protected reply and NFSv4.0 GSS callback with no further mount, click, or open; idle revalidations and server-driven callbacks are sufficient.
S:U - The wrapped buf->len and subsequent XDR decode run in the host kernel that processed the RPC and do not cross a VM, IOMMU, or sandbox boundary.
C:H - The underflowed buf->len (~UINT_MAX) becomes the authoritative stream bound, and on nfsd xdr->nwords also underflows via saved_len - buf->len, so later xdr_read_pages/xdr_shrink_bufhead and NFS XDR decoders read far past the receive buffer into adjacent kernel memory.
I:H - xdr_shrink_bufhead() calls xdr_buf_head_shift_right() with a near-UINT_MAX length derived from the wrapped buf->len, and garbage xdr_stream_pos values can index the svc pages[] array out of bounds, an out-of-bounds write exploitable for control-flow hijacking.
A:H - Those unbounded XDR copies and the inconsistent xdr_buf (wrapped length versus iov_lens) oops or panic nfsd or the NFS client, and the peer can repeat the request at will.
| Attack Vector |
Network |
Scope |
Unchanged |
| Attack Complexity |
Low |
Confidentiality Impact |
High |
| Privileges Required |
None |
Integrity Impact |
High |
| User Interaction |
None |
Availability Impact |
High |
AV:N - xdr_buf_trim() is reached from nfsd RPCSEC_GSS privacy unwrap on TCP/2049 (svcauth_gss_unwrap_priv → gss_unwrap → gss_krb5_unwrap_v2) and from the NFS client's gss_unwrap_resp_priv() on every krb5p reply, so a remote NFS peer delivers the wrap token over the network.
AC:L - A peer holding the GSS session keys can encrypt a RFC 4121 wrap token so that after decrypt, buf->len is a small wire-derived value while iov_lens remain page-scale; xdr_buf_trim() then subtracts more than buf->len and wraps, with no race or layout outside attacker control.
PR:N - A malicious or compromised NFS server that already shares a krb5p GSS context needs no account or capability on the victim client; ordinary krb5p replies and NFSv4.0 GSS callbacks are processed without local privileges on that host.
UI:N - Once a krb5p NFS mount exists, the client unwraps every privacy-protected reply and NFSv4.0 GSS callback with no further mount, click, or open; idle revalidations and server-driven callbacks are sufficient.
S:U - The wrapped buf->len and subsequent XDR decode run in the host kernel that processed the RPC and do not cross a VM, IOMMU, or sandbox boundary.
C:H - The underflowed buf->len (~UINT_MAX) becomes the authoritative stream bound, and on nfsd xdr->nwords also underflows via saved_len - buf->len, so later xdr_read_pages/xdr_shrink_bufhead and NFS XDR decoders read far past the receive buffer into adjacent kernel memory.
I:H - xdr_shrink_bufhead() calls xdr_buf_head_shift_right() with a near-UINT_MAX length derived from the wrapped buf->len, and garbage xdr_stream_pos values can index the svc pages[] array out of bounds, an out-of-bounds write exploitable for control-flow hijacking.
A:H - Those unbounded XDR copies and the inconsistent xdr_buf (wrapped length versus iov_lens) oops or panic nfsd or the NFS client, and the peer can repeat the request at will.
CVSS 3.1