Skip to content

Commit 65f2a5c

Browse files
Mike SnitzerAnna Schumaker
authored andcommitted
nfs_common: fix race in NFS calls to nfsd_file_put_local() and nfsd_serv_put()
Add nfs_to_nfsd_file_put_local() interface to fix race with nfsd module unload. Similarly, use RCU around nfs_open_local_fh()'s error path call to nfs_to->nfsd_serv_put(). Holding RCU ensures that NFS will safely _call and return_ from its nfs_to calls into the NFSD functions nfsd_file_put_local() and nfsd_serv_put(). Otherwise, if RCU isn't used then there is a narrow window when NFS's reference for the nfsd_file and nfsd_serv are dropped and the NFSD module could be unloaded, which could result in a crash from the return instruction for either nfs_to->nfsd_file_put_local() or nfs_to->nfsd_serv_put(). Reported-by: NeilBrown <[email protected]> Signed-off-by: Mike Snitzer <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent a848c29 commit 65f2a5c

File tree

6 files changed

+26
-8
lines changed

6 files changed

+26
-8
lines changed

fs/nfs/localio.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
340340
{
341341
struct nfs_pgio_header *hdr = iocb->hdr;
342342

343-
nfs_to->nfsd_file_put_local(iocb->localio);
343+
nfs_to_nfsd_file_put_local(iocb->localio);
344344
nfs_local_iocb_free(iocb);
345345
nfs_local_hdr_release(hdr, hdr->task.tk_ops);
346346
}
@@ -621,7 +621,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
621621
}
622622
out:
623623
if (status != 0) {
624-
nfs_to->nfsd_file_put_local(localio);
624+
nfs_to_nfsd_file_put_local(localio);
625625
hdr->task.tk_status = status;
626626
nfs_local_hdr_release(hdr, call_ops);
627627
}
@@ -672,7 +672,7 @@ nfs_local_release_commit_data(struct nfsd_file *localio,
672672
struct nfs_commit_data *data,
673673
const struct rpc_call_ops *call_ops)
674674
{
675-
nfs_to->nfsd_file_put_local(localio);
675+
nfs_to_nfsd_file_put_local(localio);
676676
call_ops->rpc_call_done(&data->task, data);
677677
call_ops->rpc_release(data);
678678
}

fs/nfs_common/nfslocalio.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,11 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
142142
/* We have an implied reference to net thanks to nfsd_serv_try_get */
143143
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
144144
cred, nfs_fh, fmode);
145-
if (IS_ERR(localio))
145+
if (IS_ERR(localio)) {
146+
rcu_read_lock();
146147
nfs_to->nfsd_serv_put(net);
148+
rcu_read_unlock();
149+
}
147150
return localio;
148151
}
149152
EXPORT_SYMBOL_GPL(nfs_open_local_fh);

fs/nfsd/filecache.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ nfsd_file_put(struct nfsd_file *nf)
398398
* reference to the associated nn->nfsd_serv.
399399
*/
400400
void
401-
nfsd_file_put_local(struct nfsd_file *nf)
401+
nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu)
402402
{
403403
struct net *net = nf->nf_net;
404404

fs/nfsd/localio.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ void nfsd_localio_ops_init(void)
5353
*
5454
* On successful return, returned nfsd_file will have its nf_net member
5555
* set. Caller (NFS client) is responsible for calling nfsd_serv_put and
56-
* nfsd_file_put (via nfs_to->nfsd_file_put_local).
56+
* nfsd_file_put (via nfs_to_nfsd_file_put_local).
5757
*/
5858
struct nfsd_file *
5959
nfsd_open_local_fh(struct net *net, struct auth_domain *dom,

fs/nfsd/nfssvc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,14 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
214214
return 0;
215215
}
216216

217-
bool nfsd_serv_try_get(struct net *net)
217+
bool nfsd_serv_try_get(struct net *net) __must_hold(rcu)
218218
{
219219
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
220220

221221
return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref));
222222
}
223223

224-
void nfsd_serv_put(struct net *net)
224+
void nfsd_serv_put(struct net *net) __must_hold(rcu)
225225
{
226226
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
227227

include/linux/nfslocalio.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,25 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
6565
struct rpc_clnt *, const struct cred *,
6666
const struct nfs_fh *, const fmode_t);
6767

68+
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
69+
{
70+
/*
71+
* Once reference to nfsd_serv is dropped, NFSD could be
72+
* unloaded, so ensure safe return from nfsd_file_put_local()
73+
* by always taking RCU.
74+
*/
75+
rcu_read_lock();
76+
nfs_to->nfsd_file_put_local(localio);
77+
rcu_read_unlock();
78+
}
79+
6880
#else /* CONFIG_NFS_LOCALIO */
6981
static inline void nfsd_localio_ops_init(void)
7082
{
7183
}
84+
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
85+
{
86+
}
7287
#endif /* CONFIG_NFS_LOCALIO */
7388

7489
#endif /* __LINUX_NFSLOCALIO_H */

0 commit comments

Comments
 (0)