Skip to content

Commit c07dc84

Browse files
chucklevergregkh
authored andcommitted
nfsd: fix nfsd_file reference leak in nfsd4_add_rdaccess_to_wrdeleg()
commit 8072e34 upstream. 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: stable@vger.kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent a6493eb commit c07dc84

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)
@@ -6253,10 +6255,14 @@ nfsd4_add_rdaccess_to_wrdeleg(struct svc_rqst *rqstp, struct nfsd4_open *open,
62536255
fp = stp->st_stid.sc_file;
62546256
spin_lock(&fp->fi_lock);
62556257
__nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ);
6256-
fp = stp->st_stid.sc_file;
6257-
fp->fi_fds[O_RDONLY] = nf;
6258-
fp->fi_rdeleg_file = nf;
6258+
if (!fp->fi_fds[O_RDONLY]) {
6259+
fp->fi_fds[O_RDONLY] = nf;
6260+
nf = NULL;
6261+
}
6262+
fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]);
62596263
spin_unlock(&fp->fi_lock);
6264+
if (nf)
6265+
nfsd_file_put(nf);
62606266
}
62616267
return true;
62626268
}

0 commit comments

Comments
 (0)