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
This commit is contained in:
+36
-25
@@ -661,6 +661,39 @@ static struct shrinker nfsd_file_shrinker = {
|
||||
.seeks = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
|
||||
* @nf: nfsd_file to attempt to queue
|
||||
* @dispose: private list to queue successfully-put objects
|
||||
*
|
||||
* Unhash an nfsd_file, try to get a reference to it, and then put that
|
||||
* reference. If it's the last reference, queue it to the dispose list.
|
||||
*/
|
||||
static void
|
||||
nfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
|
||||
__must_hold(RCU)
|
||||
{
|
||||
int decrement = 1;
|
||||
|
||||
/* If we raced with someone else unhashing, ignore it */
|
||||
if (!nfsd_file_unhash(nf))
|
||||
return;
|
||||
|
||||
/* If we can't get a reference, ignore it */
|
||||
if (!nfsd_file_get(nf))
|
||||
return;
|
||||
|
||||
/* Extra decrement if we remove from the LRU */
|
||||
if (nfsd_file_lru_remove(nf))
|
||||
++decrement;
|
||||
|
||||
/* If refcount goes to 0, then put on the dispose list */
|
||||
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
|
||||
list_add(&nf->nf_lru, dispose);
|
||||
trace_nfsd_file_closing(nf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_file_queue_for_close: try to close out any open nfsd_files for an inode
|
||||
* @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)
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
int decrement = 1;
|
||||
|
||||
nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key,
|
||||
nfsd_file_rhash_params);
|
||||
if (!nf)
|
||||
break;
|
||||
|
||||
/* If we raced with someone else unhashing, ignore it */
|
||||
if (!nfsd_file_unhash(nf))
|
||||
continue;
|
||||
|
||||
/* If we can't get a reference, ignore it */
|
||||
if (!nfsd_file_get(nf))
|
||||
continue;
|
||||
|
||||
/* Extra decrement if we remove from the LRU */
|
||||
if (nfsd_file_lru_remove(nf))
|
||||
++decrement;
|
||||
|
||||
/* If refcount goes to 0, then put on the dispose list */
|
||||
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
|
||||
list_add(&nf->nf_lru, dispose);
|
||||
trace_nfsd_file_closing(nf);
|
||||
}
|
||||
nfsd_file_cond_queue(nf, dispose);
|
||||
} while (1);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@@ -928,11 +942,8 @@ __nfsd_file_cache_purge(struct net *net)
|
||||
|
||||
nf = rhashtable_walk_next(&iter);
|
||||
while (!IS_ERR_OR_NULL(nf)) {
|
||||
if (!net || nf->nf_net == net) {
|
||||
nfsd_file_unhash(nf);
|
||||
nfsd_file_lru_remove(nf);
|
||||
list_add(&nf->nf_lru, &dispose);
|
||||
}
|
||||
if (!net || nf->nf_net == net)
|
||||
nfsd_file_cond_queue(nf, &dispose);
|
||||
nf = rhashtable_walk_next(&iter);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user