1

nfs: fix UAF on pathwalk running into umount

NFS ->d_revalidate(), ->permission() and ->get_link() need to access
some parts of nfs_server when called in RCU mode:
	server->flags
	server->caps
	*(server->io_stats)
and, worst of all, call
	server->nfs_client->rpc_ops->have_delegation
(the last one - as NFS_PROTO(inode)->have_delegation()).  We really
don't want to RCU-delay the entire nfs_free_server() (it would have
to be done with schedule_work() from RCU callback, since it can't
be made to run from interrupt context), but actual freeing of
nfs_server and ->io_stats can be done via call_rcu() just fine.
nfs_client part is handled simply by making nfs_free_client() use
kfree_rcu().

Acked-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2023-09-27 22:11:26 -04:00
parent 10a973fc4f
commit c1b967d03c
2 changed files with 12 additions and 3 deletions

View File

@ -246,7 +246,7 @@ void nfs_free_client(struct nfs_client *clp)
put_nfs_version(clp->cl_nfs_mod); put_nfs_version(clp->cl_nfs_mod);
kfree(clp->cl_hostname); kfree(clp->cl_hostname);
kfree(clp->cl_acceptor); kfree(clp->cl_acceptor);
kfree(clp); kfree_rcu(clp, rcu);
} }
EXPORT_SYMBOL_GPL(nfs_free_client); EXPORT_SYMBOL_GPL(nfs_free_client);
@ -1006,6 +1006,14 @@ struct nfs_server *nfs_alloc_server(void)
} }
EXPORT_SYMBOL_GPL(nfs_alloc_server); EXPORT_SYMBOL_GPL(nfs_alloc_server);
static void delayed_free(struct rcu_head *p)
{
struct nfs_server *server = container_of(p, struct nfs_server, rcu);
nfs_free_iostats(server->io_stats);
kfree(server);
}
/* /*
* Free up a server record * Free up a server record
*/ */
@ -1031,10 +1039,9 @@ void nfs_free_server(struct nfs_server *server)
ida_destroy(&server->lockowner_id); ida_destroy(&server->lockowner_id);
ida_destroy(&server->openowner_id); ida_destroy(&server->openowner_id);
nfs_free_iostats(server->io_stats);
put_cred(server->cred); put_cred(server->cred);
kfree(server);
nfs_release_automount_timer(); nfs_release_automount_timer();
call_rcu(&server->rcu, delayed_free);
} }
EXPORT_SYMBOL_GPL(nfs_free_server); EXPORT_SYMBOL_GPL(nfs_free_server);

View File

@ -124,6 +124,7 @@ struct nfs_client {
char cl_ipaddr[48]; char cl_ipaddr[48];
struct net *cl_net; struct net *cl_net;
struct list_head pending_cb_stateids; struct list_head pending_cb_stateids;
struct rcu_head rcu;
}; };
/* /*
@ -265,6 +266,7 @@ struct nfs_server {
const struct cred *cred; const struct cred *cred;
bool has_sec_mnt_opts; bool has_sec_mnt_opts;
struct kobject kobj; struct kobject kobj;
struct rcu_head rcu;
}; };
/* Server capabilities */ /* Server capabilities */