Skip to content

Commit 8072e34

Browse files
committed
nfsd: fix nfsd_file reference leak in nfsd4_add_rdaccess_to_wrdeleg()
nfsd4_add_rdaccess_to_wrdeleg() unconditionally overwrites fp->fi_fds[O_RDONLY] with a newly acquired nfsd_file. However, if the client already has a SHARE_ACCESS_READ open from a previous OPEN operation, this action overwrites the existing pointer without releasing its reference, orphaning the previous reference. Additionally, the function originally stored the same nfsd_file pointer in both fp->fi_fds[O_RDONLY] and fp->fi_rdeleg_file with only a single reference. When put_deleg_file() runs, it clears fi_rdeleg_file and calls nfs4_file_put_access() to release the file. However, nfs4_file_put_access() only releases fi_fds[O_RDONLY] when the fi_access[O_RDONLY] counter drops to zero. If another READ open exists on the file, the counter remains elevated and the nfsd_file reference from the delegation is never released. This potentially causes open conflicts on that file. Then, on server shutdown, these leaks cause __nfsd_file_cache_purge() to encounter files with an elevated reference count that cannot be cleaned up, ultimately triggering a BUG() in kmem_cache_destroy() because there are still nfsd_file objects allocated in that cache. Fixes: e7a8ebc ("NFSD: Offer write delegation for OPEN with OPEN4_SHARE_ACCESS_WRITE") Cc: [email protected] Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent a49a2a1 commit 8072e34

1 file changed

Lines changed: 10 additions & 4 deletions

File tree

fs/nfsd/nfs4state.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,8 +1218,10 @@ static void put_deleg_file(struct nfs4_file *fp)
12181218

12191219
if (nf)
12201220
nfsd_file_put(nf);
1221-
if (rnf)
1221+
if (rnf) {
1222+
nfsd_file_put(rnf);
12221223
nfs4_file_put_access(fp, NFS4_SHARE_ACCESS_READ);
1224+
}
12231225
}
12241226

12251227
static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct file *f)
@@ -6231,10 +6233,14 @@ nfsd4_add_rdaccess_to_wrdeleg(struct svc_rqst *rqstp, struct nfsd4_open *open,
62316233
fp = stp->st_stid.sc_file;
62326234
spin_lock(&fp->fi_lock);
62336235
__nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ);
6234-
fp = stp->st_stid.sc_file;
6235-
fp->fi_fds[O_RDONLY] = nf;
6236-
fp->fi_rdeleg_file = nf;
6236+
if (!fp->fi_fds[O_RDONLY]) {
6237+
fp->fi_fds[O_RDONLY] = nf;
6238+
nf = NULL;
6239+
}
6240+
fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]);
62376241
spin_unlock(&fp->fi_lock);
6242+
if (nf)
6243+
nfsd_file_put(nf);
62386244
}
62396245
return true;
62406246
}

0 commit comments

Comments
 (0)