NFS Client Bugfixes for Linux 6.12-rc

Localio Bugfixes:
   * Remove duplicated include in localio.c
   * Fix race in NFS calls to nfsd_file_put_local() and nfsd_serv_put()
   * Fix Kconfig for NFS_COMMON_LOCALIO_SUPPORT
   * Fix nfsd_file tracepoints to handle NULL rqstp pointers
 
 Other Bugfixes:
   * Fix program selection loop in svc_process_common
   * Fix integer overflow in decode_rc_list()
   * Prevent NULL-pointer dereference in nfs42_complete_copies()
   * Fix CB_RECALL performance issues when using a large number of delegations
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmcJjjQACgkQ18tUv7Cl
 QOvgJw/6A33s+pjyBVLIKT6oMCPkUJeQ4Rhg9Je0Qw/ji0eFkT4Eyd65kRz3T9M/
 qRrCfWaUd2dTYcbKQyhuGTlEfICZa9R4I0/Ztk9yvf9xcd1xFXKzTkFekGUVeHQA
 OcngDu9psFxhvyzKI8nAHs1ephX/T7TywvTKANMRbeRCYYvVkytAt9YeVMigYZa5
 dnchoUdGUdL6B6RXCU/Qhf0A1uYyA4hkk/FTBCPgv+kYx5pnjFq0y/yIIHDzCR3I
 +yE1ss3EpVTQgt2Ca/cmDyYXsa7G8G51U7cS5AeIoXfsf1EGtTujowWcBY4oqFEC
 ixx58fQe48AqwsP5XDZn8gnsuYH9snnw5rIB0IVqq55/a+XLMupHayyf/iziMV3s
 JWgT4gKDyFca2pT+bJ8iWweU+ecRYxKGnh2NydyBiqowogsHZm4uKh0vELvqqkBd
 RIjCyIiQVhYBII2jqpjRnxrqhGUT5XO99NQdQIGV0bUjCEP4YAjY4ChfEVcWXhnB
 ppyBP+r8N5O77NcVqsVQS26U0/jb9K30LyYl9VT43ank3d+VVtHA5ZqnUflWtwuc
 2XiGDvXW9mIvbVraWIZXUNVy39bzRclDf5bx4jeYLnKCMym81rkEIBOvBKQKZTrl
 v+1Nhaj+fSw+rFSUm0KPqms0UDiT0Ol7ltu84ifadYqubbSEbqU=
 =QBvR
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-6.12-2' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull NFS client fixes from Anna Schumaker:
 "Localio Bugfixes:
   - remove duplicated include in localio.c
   - fix race in NFS calls to nfsd_file_put_local() and nfsd_serv_put()
   - fix Kconfig for NFS_COMMON_LOCALIO_SUPPORT
   - fix nfsd_file tracepoints to handle NULL rqstp pointers

  Other Bugfixes:
   - fix program selection loop in svc_process_common
   - fix integer overflow in decode_rc_list()
   - prevent NULL-pointer dereference in nfs42_complete_copies()
   - fix CB_RECALL performance issues when using a large number of
     delegations"

* tag 'nfs-for-6.12-2' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  NFS: remove revoked delegation from server's delegation list
  nfsd/localio: fix nfsd_file tracepoints to handle NULL rqstp
  nfs_common: fix Kconfig for NFS_COMMON_LOCALIO_SUPPORT
  nfs_common: fix race in NFS calls to nfsd_file_put_local() and nfsd_serv_put()
  NFSv4: Prevent NULL-pointer dereference in nfs42_complete_copies()
  SUNRPC: Fix integer overflow in decode_rc_list()
  sunrpc: fix prog selection loop in svc_process_common
  nfs: Remove duplicated include in localio.c
This commit is contained in:
Linus Torvalds 2024-10-11 15:37:15 -07:00
commit 6254d53727
15 changed files with 45 additions and 22 deletions

View File

@ -388,7 +388,7 @@ config NFS_COMMON
config NFS_COMMON_LOCALIO_SUPPORT config NFS_COMMON_LOCALIO_SUPPORT
tristate tristate
default n depends on NFS_LOCALIO
default y if NFSD=y || NFS_FS=y default y if NFSD=y || NFS_FS=y
default m if NFSD=m && NFS_FS=m default m if NFSD=m && NFS_FS=m
select SUNRPC select SUNRPC

View File

@ -375,6 +375,8 @@ static __be32 decode_rc_list(struct xdr_stream *xdr,
rc_list->rcl_nrefcalls = ntohl(*p++); rc_list->rcl_nrefcalls = ntohl(*p++);
if (rc_list->rcl_nrefcalls) { if (rc_list->rcl_nrefcalls) {
if (unlikely(rc_list->rcl_nrefcalls > xdr->buf->len))
goto out;
p = xdr_inline_decode(xdr, p = xdr_inline_decode(xdr,
rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t)); rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))

View File

@ -996,6 +996,7 @@ struct nfs_server *nfs_alloc_server(void)
INIT_LIST_HEAD(&server->layouts); INIT_LIST_HEAD(&server->layouts);
INIT_LIST_HEAD(&server->state_owners_lru); INIT_LIST_HEAD(&server->state_owners_lru);
INIT_LIST_HEAD(&server->ss_copies); INIT_LIST_HEAD(&server->ss_copies);
INIT_LIST_HEAD(&server->ss_src_copies);
atomic_set(&server->active, 0); atomic_set(&server->active, 0);

View File

@ -1001,6 +1001,11 @@ void nfs_delegation_mark_returned(struct inode *inode,
} }
nfs_mark_delegation_revoked(delegation); nfs_mark_delegation_revoked(delegation);
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
spin_unlock(&delegation->lock);
if (nfs_detach_delegation(NFS_I(inode), delegation, NFS_SERVER(inode)))
nfs_put_delegation(delegation);
goto out_rcu_unlock;
out_clear_returning: out_clear_returning:
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);

View File

@ -18,7 +18,6 @@
#include <net/addrconf.h> #include <net/addrconf.h>
#include <linux/nfs_common.h> #include <linux/nfs_common.h>
#include <linux/nfslocalio.h> #include <linux/nfslocalio.h>
#include <linux/module.h>
#include <linux/bvec.h> #include <linux/bvec.h>
#include <linux/nfs.h> #include <linux/nfs.h>
@ -341,7 +340,7 @@ nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
{ {
struct nfs_pgio_header *hdr = iocb->hdr; struct nfs_pgio_header *hdr = iocb->hdr;
nfs_to->nfsd_file_put_local(iocb->localio); nfs_to_nfsd_file_put_local(iocb->localio);
nfs_local_iocb_free(iocb); nfs_local_iocb_free(iocb);
nfs_local_hdr_release(hdr, hdr->task.tk_ops); nfs_local_hdr_release(hdr, hdr->task.tk_ops);
} }
@ -622,7 +621,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
} }
out: out:
if (status != 0) { if (status != 0) {
nfs_to->nfsd_file_put_local(localio); nfs_to_nfsd_file_put_local(localio);
hdr->task.tk_status = status; hdr->task.tk_status = status;
nfs_local_hdr_release(hdr, call_ops); nfs_local_hdr_release(hdr, call_ops);
} }
@ -673,7 +672,7 @@ nfs_local_release_commit_data(struct nfsd_file *localio,
struct nfs_commit_data *data, struct nfs_commit_data *data,
const struct rpc_call_ops *call_ops) const struct rpc_call_ops *call_ops)
{ {
nfs_to->nfsd_file_put_local(localio); nfs_to_nfsd_file_put_local(localio);
call_ops->rpc_call_done(&data->task, data); call_ops->rpc_call_done(&data->task, data);
call_ops->rpc_release(data); call_ops->rpc_release(data);
} }

View File

@ -218,7 +218,7 @@ static int handle_async_copy(struct nfs42_copy_res *res,
if (dst_server != src_server) { if (dst_server != src_server) {
spin_lock(&src_server->nfs_client->cl_lock); spin_lock(&src_server->nfs_client->cl_lock);
list_add_tail(&copy->src_copies, &src_server->ss_copies); list_add_tail(&copy->src_copies, &src_server->ss_src_copies);
spin_unlock(&src_server->nfs_client->cl_lock); spin_unlock(&src_server->nfs_client->cl_lock);
} }

View File

@ -1585,7 +1585,7 @@ static void nfs42_complete_copies(struct nfs4_state_owner *sp, struct nfs4_state
complete(&copy->completion); complete(&copy->completion);
} }
} }
list_for_each_entry(copy, &sp->so_server->ss_copies, src_copies) { list_for_each_entry(copy, &sp->so_server->ss_src_copies, src_copies) {
if ((test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags) && if ((test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags) &&
!nfs4_stateid_match_other(&state->stateid, !nfs4_stateid_match_other(&state->stateid,
&copy->parent_src_state->stateid))) &copy->parent_src_state->stateid)))

View File

@ -142,8 +142,11 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
/* We have an implied reference to net thanks to nfsd_serv_try_get */ /* We have an implied reference to net thanks to nfsd_serv_try_get */
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
cred, nfs_fh, fmode); cred, nfs_fh, fmode);
if (IS_ERR(localio)) if (IS_ERR(localio)) {
rcu_read_lock();
nfs_to->nfsd_serv_put(net); nfs_to->nfsd_serv_put(net);
rcu_read_unlock();
}
return localio; return localio;
} }
EXPORT_SYMBOL_GPL(nfs_open_local_fh); EXPORT_SYMBOL_GPL(nfs_open_local_fh);

View File

@ -398,7 +398,7 @@ nfsd_file_put(struct nfsd_file *nf)
* reference to the associated nn->nfsd_serv. * reference to the associated nn->nfsd_serv.
*/ */
void void
nfsd_file_put_local(struct nfsd_file *nf) nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu)
{ {
struct net *net = nf->nf_net; struct net *net = nf->nf_net;

View File

@ -53,7 +53,7 @@ void nfsd_localio_ops_init(void)
* *
* On successful return, returned nfsd_file will have its nf_net member * On successful return, returned nfsd_file will have its nf_net member
* set. Caller (NFS client) is responsible for calling nfsd_serv_put and * set. Caller (NFS client) is responsible for calling nfsd_serv_put and
* nfsd_file_put (via nfs_to->nfsd_file_put_local). * nfsd_file_put (via nfs_to_nfsd_file_put_local).
*/ */
struct nfsd_file * struct nfsd_file *
nfsd_open_local_fh(struct net *net, struct auth_domain *dom, nfsd_open_local_fh(struct net *net, struct auth_domain *dom,

View File

@ -214,14 +214,14 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
return 0; return 0;
} }
bool nfsd_serv_try_get(struct net *net) bool nfsd_serv_try_get(struct net *net) __must_hold(rcu)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref)); return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref));
} }
void nfsd_serv_put(struct net *net) void nfsd_serv_put(struct net *net) __must_hold(rcu)
{ {
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);

View File

@ -1113,7 +1113,7 @@ TRACE_EVENT(nfsd_file_acquire,
), ),
TP_fast_assign( TP_fast_assign(
__entry->xid = be32_to_cpu(rqstp->rq_xid); __entry->xid = rqstp ? be32_to_cpu(rqstp->rq_xid) : 0;
__entry->inode = inode; __entry->inode = inode;
__entry->may_flags = may_flags; __entry->may_flags = may_flags;
__entry->nf_ref = nf ? refcount_read(&nf->nf_ref) : 0; __entry->nf_ref = nf ? refcount_read(&nf->nf_ref) : 0;
@ -1147,7 +1147,7 @@ TRACE_EVENT(nfsd_file_insert_err,
__field(long, error) __field(long, error)
), ),
TP_fast_assign( TP_fast_assign(
__entry->xid = be32_to_cpu(rqstp->rq_xid); __entry->xid = rqstp ? be32_to_cpu(rqstp->rq_xid) : 0;
__entry->inode = inode; __entry->inode = inode;
__entry->may_flags = may_flags; __entry->may_flags = may_flags;
__entry->error = error; __entry->error = error;
@ -1177,7 +1177,7 @@ TRACE_EVENT(nfsd_file_cons_err,
__field(const void *, nf_file) __field(const void *, nf_file)
), ),
TP_fast_assign( TP_fast_assign(
__entry->xid = be32_to_cpu(rqstp->rq_xid); __entry->xid = rqstp ? be32_to_cpu(rqstp->rq_xid) : 0;
__entry->inode = inode; __entry->inode = inode;
__entry->may_flags = may_flags; __entry->may_flags = may_flags;
__entry->nf_ref = refcount_read(&nf->nf_ref); __entry->nf_ref = refcount_read(&nf->nf_ref);

View File

@ -249,6 +249,7 @@ struct nfs_server {
struct list_head layouts; struct list_head layouts;
struct list_head delegations; struct list_head delegations;
struct list_head ss_copies; struct list_head ss_copies;
struct list_head ss_src_copies;
unsigned long delegation_gen; unsigned long delegation_gen;
unsigned long mig_gen; unsigned long mig_gen;

View File

@ -65,10 +65,25 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
struct rpc_clnt *, const struct cred *, struct rpc_clnt *, const struct cred *,
const struct nfs_fh *, const fmode_t); const struct nfs_fh *, const fmode_t);
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
{
/*
* Once reference to nfsd_serv is dropped, NFSD could be
* unloaded, so ensure safe return from nfsd_file_put_local()
* by always taking RCU.
*/
rcu_read_lock();
nfs_to->nfsd_file_put_local(localio);
rcu_read_unlock();
}
#else /* CONFIG_NFS_LOCALIO */ #else /* CONFIG_NFS_LOCALIO */
static inline void nfsd_localio_ops_init(void) static inline void nfsd_localio_ops_init(void)
{ {
} }
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
{
}
#endif /* CONFIG_NFS_LOCALIO */ #endif /* CONFIG_NFS_LOCALIO */
#endif /* __LINUX_NFSLOCALIO_H */ #endif /* __LINUX_NFSLOCALIO_H */

View File

@ -1321,7 +1321,7 @@ static int
svc_process_common(struct svc_rqst *rqstp) svc_process_common(struct svc_rqst *rqstp)
{ {
struct xdr_stream *xdr = &rqstp->rq_res_stream; struct xdr_stream *xdr = &rqstp->rq_res_stream;
struct svc_program *progp; struct svc_program *progp = NULL;
const struct svc_procedure *procp = NULL; const struct svc_procedure *procp = NULL;
struct svc_serv *serv = rqstp->rq_server; struct svc_serv *serv = rqstp->rq_server;
struct svc_process_info process; struct svc_process_info process;
@ -1351,12 +1351,9 @@ svc_process_common(struct svc_rqst *rqstp)
rqstp->rq_vers = be32_to_cpup(p++); rqstp->rq_vers = be32_to_cpup(p++);
rqstp->rq_proc = be32_to_cpup(p); rqstp->rq_proc = be32_to_cpup(p);
for (pr = 0; pr < serv->sv_nprogs; pr++) { for (pr = 0; pr < serv->sv_nprogs; pr++)
progp = &serv->sv_programs[pr]; if (rqstp->rq_prog == serv->sv_programs[pr].pg_prog)
progp = &serv->sv_programs[pr];
if (rqstp->rq_prog == progp->pg_prog)
break;
}
/* /*
* Decode auth data, and add verifier to reply buffer. * Decode auth data, and add verifier to reply buffer.