Skip to content

Commit fb6e71d

Browse files
committed
Merge tag 'nfsd-6.2-5' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
Pull nfsd fix from Chuck Lever: - Nail another UAF in NFSD's filecache * tag 'nfsd-6.2-5' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: nfsd: don't free files unconditionally in __nfsd_file_cache_purge
2 parents 50306df + 4bdbba5 commit fb6e71d

File tree

1 file changed

+36
-25
lines changed

1 file changed

+36
-25
lines changed

fs/nfsd/filecache.c

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,39 @@ static struct shrinker nfsd_file_shrinker = {
661661
.seeks = 1,
662662
};
663663

664+
/**
665+
* nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
666+
* @nf: nfsd_file to attempt to queue
667+
* @dispose: private list to queue successfully-put objects
668+
*
669+
* Unhash an nfsd_file, try to get a reference to it, and then put that
670+
* reference. If it's the last reference, queue it to the dispose list.
671+
*/
672+
static void
673+
nfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
674+
__must_hold(RCU)
675+
{
676+
int decrement = 1;
677+
678+
/* If we raced with someone else unhashing, ignore it */
679+
if (!nfsd_file_unhash(nf))
680+
return;
681+
682+
/* If we can't get a reference, ignore it */
683+
if (!nfsd_file_get(nf))
684+
return;
685+
686+
/* Extra decrement if we remove from the LRU */
687+
if (nfsd_file_lru_remove(nf))
688+
++decrement;
689+
690+
/* If refcount goes to 0, then put on the dispose list */
691+
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
692+
list_add(&nf->nf_lru, dispose);
693+
trace_nfsd_file_closing(nf);
694+
}
695+
}
696+
664697
/**
665698
* nfsd_file_queue_for_close: try to close out any open nfsd_files for an inode
666699
* @inode: inode on which to close out nfsd_files
@@ -688,30 +721,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
688721

689722
rcu_read_lock();
690723
do {
691-
int decrement = 1;
692-
693724
nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key,
694725
nfsd_file_rhash_params);
695726
if (!nf)
696727
break;
697-
698-
/* If we raced with someone else unhashing, ignore it */
699-
if (!nfsd_file_unhash(nf))
700-
continue;
701-
702-
/* If we can't get a reference, ignore it */
703-
if (!nfsd_file_get(nf))
704-
continue;
705-
706-
/* Extra decrement if we remove from the LRU */
707-
if (nfsd_file_lru_remove(nf))
708-
++decrement;
709-
710-
/* If refcount goes to 0, then put on the dispose list */
711-
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
712-
list_add(&nf->nf_lru, dispose);
713-
trace_nfsd_file_closing(nf);
714-
}
728+
nfsd_file_cond_queue(nf, dispose);
715729
} while (1);
716730
rcu_read_unlock();
717731
}
@@ -928,11 +942,8 @@ __nfsd_file_cache_purge(struct net *net)
928942

929943
nf = rhashtable_walk_next(&iter);
930944
while (!IS_ERR_OR_NULL(nf)) {
931-
if (!net || nf->nf_net == net) {
932-
nfsd_file_unhash(nf);
933-
nfsd_file_lru_remove(nf);
934-
list_add(&nf->nf_lru, &dispose);
935-
}
945+
if (!net || nf->nf_net == net)
946+
nfsd_file_cond_queue(nf, &dispose);
936947
nf = rhashtable_walk_next(&iter);
937948
}
938949

0 commit comments

Comments
 (0)