[BUG] Linux Kernel NFS Server refcount_t Underflow in nfs3svc_release_getacl (S→C)

From: ven0mfuzzer

Date: Thu Apr 02 2026 - 07:11:58 EST


Linux Kernel NFS Server refcount_t Underflow in nfs3svc_release_getacl (S→C)

1. Vulnerability Title

Linux Kernel NFS Server (nfsd) refcount_t Underflow via Malicious GETACL Request with Corrupted File Handle

2. High-Level Overview

This report describes a refcount_t underflow vulnerability in the Linux kernel NFS server (nfsd) triggered when a MITM attacker corrupts the file handle in an NFSv3 GETACL request. When `fh_verify()` or `fh_copy()` fails due to the corrupted handle, `resp->fh` is left in an inconsistent refcount state. The subsequent `fh_put()` call in `nfs3svc_release_getacl()` decrements a refcount that was never properly incremented, causing an underflow that constitutes a potential use-after-free condition.

The kernel's `refcount_warn_saturate()` protection clamps the value at zero, preventing actual exploitation. However, the underlying logic error remains — on kernels compiled without `CONFIG_REFCOUNT_FULL` (or older kernels without saturating refcount), this could lead to a real use-after-free.

This vulnerability was discovered using ven0mfuzzer, our custom-designed MITM-based network filesystem fuzzer developed by our team.

Attack Model

The attack direction is Client → Server (C→S) via MITM:

1. MITM injection: An attacker with network access between an NFS client and server intercepts NFSv3 traffic and corrupts the file handle field in GETACL requests. The nfsd server processes the corrupted request, triggering the refcount underflow in the release path.

2. Malicious NFS client: A rogue NFS client sends crafted GETACL requests with invalid file handles directly to the NFS server, triggering the same code path.

3. Affected Product and Version Information

Product: Linux Kernel (upstream mainline)
Affected Component: `fs/nfsd/nfs3acl.c` — `nfs3svc_release_getacl()`
Supporting Components:
- `fs/nfsd/nfsfh.c` — `fh_verify()`, `fh_put()`
- `fs/nfsd/nfs3acl.c` — `nfsd3_proc_getacl()`

Tested Versions (confirmed vulnerable)
- Linux kernel 6.19.0 (mainline, commit `44331bd6a610`, gcc 11.4.0, built with KASAN + LOCKDEP + KFENCE)

Affected Version Range
All kernels with `CONFIG_NFSD_V3_ACL` enabled (NFSv3 ACL extension support) are believed affected.

Affected Distributions and Products

| Vendor / Product | Notes |
| --- | --- |
| Red Hat Enterprise Linux (RHEL 8.x, 9.x) | Ships nfsd with NFSv3 ACL support by default |
| Ubuntu Server (20.04 LTS, 22.04 LTS, 24.04 LTS) | NFS server packages include ACL support |
| SUSE Linux Enterprise Server (SLES 15 SP5+) | NFS server with ACL enabled |
| Debian (Bookworm, Trixie) | nfs-kernel-server includes ACL support |
| Amazon Linux 2023 | Kernel 6.1 LTS with nfsd |
| Google ChromeOS / COS | NOT AFFECTED — CONFIG_NFSD not enabled |

4. Root Cause Analysis

4.a. Detailed Description

In `fs/nfsd/nfs3acl.c`, the GETACL procedure (`nfsd3_proc_getacl`) handles two code paths:

Success path: `fh_verify()` succeeds, `fh_copy()` properly initializes `resp->fh` with correct refcount. `nfs3svc_release_getacl()` later calls `fh_put(&resp->fh)` which decrements the refcount normally.

Failure path (triggered by corrupted file handle): `fh_verify()` fails because the file handle is invalid. At this point `resp->fh` may be partially initialized — the `fh_copy()` or `fh_compose()` call either did not execute or left the refcount in an inconsistent state. However, `nfs3svc_release_getacl()` is always called by `svc_process()` as the release function, regardless of whether the procedure succeeded. When it calls `fh_put(&resp->fh)`, it decrements a refcount that was never incremented, causing underflow.

The `refcount_warn_saturate()` call in the kernel's refcount infrastructure detects this underflow and clamps the value, printing a warning. On kernels without saturating refcount protection, this would be a genuine use-after-free condition.

The bug was triggered independently twice across different fuzzing sessions, confirming it is reliably reproducible.

4.b. Code Flow

---
svc_process() [net/sunrpc/svc.c]
nfsd3_proc_getacl() [fs/nfsd/nfs3acl.c]
fh_verify(&argp->fh, ...) ← FAILS (corrupted file handle from MITM)
resp->fh is left in inconsistent state ← refcount never properly incremented
→ returns nfserr_*
nfs3svc_release_getacl() [fs/nfsd/nfs3acl.c] (always called by svc_process)
fh_put(&resp->fh) ← decrements refcount that was never incremented
→ refcount underflow!
→ refcount_warn_saturate() triggered
---

4.c. Crash Trace

This vulnerability was discovered by ven0mfuzzer. The following kernel trace is submitted following syzkaller's common practice of providing the raw crash trace as the primary reproduction evidence:

---
refcount_t: underflow; use-after-free.

Call Trace:
<TASK>
refcount_warn_saturate+0xe0/0x120
nfs3svc_release_getacl+0x149/0x1c0
svc_process+0x6c1/0xb20
svc_recv+0x2191/0x2e60
nfsd+0x284/0xd60
kthread+0x378/0x490
ret_from_fork+0x676/0xac0
</TASK>
---

Key observations:
- Trigger: nfsd kernel thread processing GETACL request with corrupted file handle
- Underflow detected at: `nfs3svc_release_getacl+0x149` — the `fh_put()` call
- Protection active: `refcount_warn_saturate()` clamps the value, preventing actual UAF
- Reproduced independently twice (crash-0-1773578276 at uptime 2568s, crash-0-1773606962 at uptime 15073s)

4.d. Suggested Fix

Ensure `resp->fh` is properly initialized before the release function is called. Add a check in `nfs3svc_release_getacl()` to verify the file handle was actually set up:

---
static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
{
struct nfsd3_getaclres *resp = rqstp->rq_resp;
+ / Only put the file handle if it was properly initialized /
+ if (resp->fh.fh_dentry)
fh_put(&resp->fh);
posix_acl_release(resp->acl_access);
posix_acl_release(resp->acl_default);
}
---

Alternatively, ensure `nfsd3_proc_getacl()` explicitly calls `fh_init()` on `resp->fh` at the start of the procedure to guarantee a clean initial state, so that `fh_put()` on an unused handle is a safe no-op.

5. Discovery Method and Reproduction

5.a. Discovery

This vulnerability was discovered using ven0mfuzzer, a custom-designed MITM-based network filesystem fuzzer developed by our team. The fuzzer operates by positioning an AF_PACKET/TCP transparent proxy between a Linux kernel filesystem client (VM-A) and its server (VM-B), then mutating network protocol messages in-flight. We designed ven0mfuzzer specifically for this class of network filesystem vulnerabilities.

Following the common syzkaller practice, we submit the kernel crash trace as the primary reproduction artifact.

5.b. Reproduction Setup

---
VM-A (NFS client) ──NFS──► Host (MITM proxy) ──TCP──► VM-B (nfsd server)
---

Trigger condition: MITM corrupts the file handle field in an NFSv3 GETACL request, causing `fh_verify()` to fail while `nfs3svc_release_getacl()` still calls `fh_put()` on the improperly initialized handle.

Reproduction steps:
1. Build kernel 6.19.0 (commit `44331bd6a610`) with KASAN, LOCKDEP, and KFENCE enabled
2. Start VM-B running nfsd server with NFSv3 ACL support (`CONFIG_NFSD_V3_ACL=y`)
3. Start MITM proxy on host, configured to corrupt file handle bytes in GETACL requests
4. Mount NFS share in VM-A through the MITM proxy
5. Execute filesystem operations that trigger GETACL (e.g., `getfacl` on mounted files)

---
Reported-by: ven0mfuzzer <ven0mkernelfuzzer@xxxxxxxxx>
Link: https://github.com/KernelStackFuzz/KernelStackFuzz