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:
parent
10a973fc4f
commit
c1b967d03c
@ -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);
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user