FAILED: patch "[PATCH] sunrpc: defer rq_argp and rq_resp free until after RCU grace" failed to apply to 6.6-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x c479bde671cbe2f9e152834a8b0eb7c3c295bbaf
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090350-breeze-repugnant-70de@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c479bde671cbe2f9e152834a8b0eb7c3c295bbaf Mon Sep 17 00:00:00 2001
From: Jeff Layton <[email protected]>
Date: Thu, 11 Jun 2026 16:00:47 -0400
Subject: [PATCH] sunrpc: defer rq_argp and rq_resp free until after RCU grace
period
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
net/sunrpc/svc.c +10 -3
@@ -716,6 +716,15 @@ svc_release_buffer(struct svc_rqst *rqstp) } } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +static void svc_rqst_free (struct svc_rqst *rqstp) {
@@ -724,10 +733,8 @@ svc_rqst_free(struct svc_rqst *rqstp)svc_release_buffer (rqstp);if (rqstp->rq_scratch_folio)folio_put (rqstp->rq_scratch_folio); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst *
Sasha Levin
maintainerPATCH 1/7 SUNRPC: move all of xprt handling into svc_xprt_handle()
From: NeilBrown <[email protected]>
[ Upstream commit e3274026e2ec69eec6ab51bc499e14bb548548d0 ]
svc_xprt_handle() does lots of things itself, but leaves some to the caller - svc_recv(). This isn't elegant.
Move that code out of svc_recv() into svc_xprt_handle()
Move the calls to svc_xprt_release() from svc_send() and svc_drop() (the two possible final steps in svc_process()) and from svc_recv() (in the case where svc_process() wasn't called) into svc_xprt_handle().
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- net/sunrpc/svc_xprt.c | 53 ++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 33 deletions(-)
net/sunrpc/svc_xprt.c +20 -33
@@ -784,7 +784,7 @@ static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxptsvc_xprt_received (newxpt); } -static int svc_handle_xprt (struct svc_rqst *rqstp,struct svc_xprt *xprt) +static void svc_handle_xprt (struct svc_rqst *rqstp,struct svc_xprt *xprt) {struct svc_serv *serv = rqstp->rq_server;int len =0 ;
@@ -825,11 +825,26 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt) len = xprt->xpt_ops->xpo_recvfrom (rqstp); rqstp->rq_reserved = serv->sv_max_mesg;atomic_add (rqstp->rq_reserved, &xprt->xpt_reserved); +if (len <=0 ) + goto out; + +trace_svc_xdr_recvfrom (&rqstp->rq_arg); + +clear_bit (XPT_OLD , &xprt->xpt_flags); + + rqstp->rq_chandle.defer = svc_defer; + +if (serv->sv_stats) + serv->sv_stats->netcnt++; +percpu_counter_inc (&rqstp->rq_pool->sp_messages_arrived); + rqstp->rq_stime =ktime_get (); +svc_process (rqstp); }else svc_xprt_received (xprt); out: -return len; + rqstp->rq_res.len =0 ; +svc_xprt_release (rqstp); }/**
@@ -843,11 +858,9 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)void svc_recv (struct svc_rqst *rqstp) {struct svc_xprt *xprt =NULL ; -struct svc_serv *serv = rqstp->rq_server; -int len;if (!svc_alloc_arg (rqstp)) - goto out; +return ;try_to_freeze ();cond_resched ();
@@ -855,31 +868,9 @@ void svc_recv(struct svc_rqst *rqstp) goto out; xprt =svc_get_next_xprt (rqstp); -if (!xprt) - goto out; - - len =svc_handle_xprt (rqstp, xprt); - -/* No data, incomplete (TCP) read, or accept() */ -if (len <=0 ) - goto out_release; - -trace_svc_xdr_recvfrom (&rqstp->rq_arg); - -clear_bit (XPT_OLD , &xprt->xpt_flags); - - rqstp->rq_chandle.defer = svc_defer; - -if (serv->sv_stats) - serv->sv_stats->netcnt++; -percpu_counter_inc (&rqstp->rq_pool->sp_messages_arrived); - rqstp->rq_stime =ktime_get (); -svc_process (rqstp); +if (xprt) +svc_handle_xprt (rqstp, xprt); out: -return ; -out_release: - rqstp->rq_res.len =0 ; -svc_xprt_release (rqstp); }EXPORT_SYMBOL_GPL (svc_recv);
@@ -889,7 +880,6 @@ EXPORT_SYMBOL_GPL(svc_recv);void svc_drop (struct svc_rqst *rqstp) {trace_svc_drop (rqstp); -svc_xprt_release (rqstp); }EXPORT_SYMBOL_GPL (svc_drop);
@@ -905,8 +895,6 @@ void svc_send(struct svc_rqst *rqstp)int status; xprt = rqstp->rq_xprt; -if (!xprt) -return ;/* calculate over-all length */ xb = &rqstp->rq_res;
@@ -919,7 +907,6 @@ void svc_send(struct svc_rqst *rqstp) status = xprt->xpt_ops->xpo_sendto (rqstp);trace_svc_send (rqstp, status); -svc_xprt_release (rqstp); }/*
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/7 SUNRPC: Add a server-side API for retrieving an RPC's pseudoflavor
From: Chuck Lever <[email protected]>
[ Upstream commit deb704281f076097b0347116a82edeba96697db1 ]
NFSD will use this new API to determine whether nfsd_splice_read is safe to use. This avoids the need to add a dependency to NFSD for CONFIG_SUNRPC_GSS.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svcauth.h | 7 ++++++- net/sunrpc/auth_gss/svcauth_gss.c | 6 ++++++ net/sunrpc/svcauth.c | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-)
include/linux/sunrpc/svcauth.h +6 -1
@@ -131,8 +131,11 @@ enum svc_auth_status {* This call releases a domain. * * set_client() -* Givens a pending request (struct svc_rqst), finds and assigns +* Given a pending request (struct svc_rqst), finds and assigns * an appropriate 'auth_domain' as the client. +* +* pseudoflavor() +* Returns RPC_AUTH pseudoflavor in use by @rqstp. */ struct auth_ops {char * name;
@@ -143,11 +146,13 @@ struct auth_ops {int (*release)(struct svc_rqst *rqstp);void (*domain_release)(struct auth_domain *dom);enum svc_auth_status (*set_client)(struct svc_rqst *rqstp); +rpc_authflavor_t (*pseudoflavor)(struct svc_rqst *rqstp); };struct svc_xprt ;extern enum svc_auth_status svc_authenticate (struct svc_rqst *rqstp); +extern rpc_authflavor_t svc_auth_flavor (struct svc_rqst *rqstp);extern int svc_authorise (struct svc_rqst *rqstp);extern enum svc_auth_status svc_set_client (struct svc_rqst *rqstp);extern int svc_auth_register (rpc_authflavor_t flavor,struct auth_ops *aops);
net/sunrpc/auth_gss/svcauth_gss.c +6 -0
@@ -2009,6 +2009,11 @@ svcauth_gss_domain_release(struct auth_domain *dom)call_rcu (&dom->rcu_head, svcauth_gss_domain_release_rcu); } +static rpc_authflavor_t svcauth_gss_pseudoflavor (struct svc_rqst *rqstp) +{ +return svcauth_gss_flavor (rqstp->rq_gssclient); +} +static struct auth_ops svcauthops_gss = { .name ="rpcsec_gss" , .owner =THIS_MODULE ,
@@ -2017,6 +2022,7 @@ static struct auth_ops svcauthops_gss = { .release = svcauth_gss_release, .domain_release = svcauth_gss_domain_release, .set_client = svcauth_gss_set_client, + .pseudoflavor = svcauth_gss_pseudoflavor, };static int rsi_cache_create_net (struct net *net)
net/sunrpc/svcauth.c +16 -0
@@ -160,6 +160,22 @@ svc_auth_unregister(rpc_authflavor_t flavor) }EXPORT_SYMBOL_GPL (svc_auth_unregister); +/** +* svc_auth_flavor - return RPC transaction's RPC_AUTH flavor +* @rqstp: RPC transaction context +* +* Returns an RPC flavor or GSS pseudoflavor. +*/ +rpc_authflavor_t svc_auth_flavor (struct svc_rqst *rqstp) +{ +struct auth_ops *aops = rqstp->rq_authop; + +if (!aops->pseudoflavor) +return aops->flavour; +return aops->pseudoflavor (rqstp); +} +EXPORT_SYMBOL_GPL (svc_auth_flavor); +/************************************************** * 'auth_domains' are stored in a hash table indexed by name. * When the last reference to an 'auth_domain' is dropped,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/7 sunrpc: refactor pool_mode setting code
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit 5f71f3c325534da55dfda487a67208e2e0f0cd1b ]
Allow the pool_mode setting code to be called from internal callers so we can call it from a new netlink op. Add a new svc_pool_map_get function to return the current setting. Change the existing module parameter handling to use the new interfaces under the hood.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 2 + net/sunrpc/svc.c | 85 ++++++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 21 deletions(-)
include/linux/sunrpc/svc.h +2 -0
@@ -395,6 +395,8 @@ struct svc_procedure {/* * Function prototypes. */ +int sunrpc_set_pool_mode (const char *val); +int sunrpc_get_pool_mode (char *val,size_t size);int svc_rpcb_setup (struct svc_serv *serv,struct net *net);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net);int svc_bind (struct svc_serv *serv,struct net *net);
net/sunrpc/svc.c +64 -21
121 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/7 SUNRPC: make various functions static, or not exported.
In reply to Sasha LevinFrom: NeilBrown <[email protected]>
[ Upstream commit f2b27e1d72527f94f030b6356b3187576e60885b ]
Various functions are only used within the sunrpc module, and several are only use in the one file. So clean up:
These are marked static, and any EXPORT is removed.
svc_rcpb_setup()
svc_rqst_alloc()
svc_rqst_free() - also moved before first use svc_rpcbind_set_version()
svc_drop() - also moved to svc.c
These are now not EXPORTed, but are not static.
svc_authenticate()
svc_sock_update_bufs()
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 9 ------- include/linux/sunrpc/svcauth.h | 1 - include/linux/sunrpc/svcsock.h | 2 -- net/sunrpc/sunrpc.h | 4 +++ net/sunrpc/svc.c | 48 ++++++++++++++++++---------------- net/sunrpc/svc_xprt.c | 9 ------- net/sunrpc/svcauth.c | 1 - net/sunrpc/svcsock.c | 1 - 8 files changed, 29 insertions(+), 46 deletions(-)
include/linux/sunrpc/svc.h +0 -9
@@ -397,17 +397,13 @@ struct svc_procedure {*/ int sunrpc_set_pool_mode (const char *val);int sunrpc_get_pool_mode (char *val,size_t size); -int svc_rpcb_setup (struct svc_serv *serv,struct net *net);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net);int svc_bind (struct svc_serv *serv,struct net *net);struct svc_serv *svc_create (struct svc_program *,unsigned int ,int (*threadfn)(void *data)); -struct svc_rqst *svc_rqst_alloc (struct svc_serv *serv, -struct svc_pool *pool,int node);bool svc_rqst_replace_page (struct svc_rqst *rqstp,struct page *page);void svc_rqst_release_pages (struct svc_rqst *rqstp); -void svc_rqst_free (struct svc_rqst *);void svc_exit_thread (struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *prog, struct svc_stat *stats,
@@ -443,11 +439,6 @@ int svc_generic_rpcbind_set(struct net *net, u32 version, int family, unsigned short proto, unsigned short port); -int svc_rpcbind_set_version (struct net *net, -const struct svc_program *progp, -u32 version,int family, -unsigned short proto, -unsigned short port);#define RPC_MAX_ADDRBUFLEN (63U)
include/linux/sunrpc/svcauth.h +0 -1
@@ -151,7 +151,6 @@ struct auth_ops {struct svc_xprt ; -extern enum svc_auth_status svc_authenticate (struct svc_rqst *rqstp);extern rpc_authflavor_t svc_auth_flavor (struct svc_rqst *rqstp);extern int svc_authorise (struct svc_rqst *rqstp);extern enum svc_auth_status svc_set_client (struct svc_rqst *rqstp);
include/linux/sunrpc/svcsock.h +0 -2
@@ -61,8 +61,6 @@ static inline u32 svc_sock_final_rec(struct svc_sock *svsk)*/ void svc_recv (struct svc_rqst *rqstp);void svc_send (struct svc_rqst *rqstp); -void svc_drop (struct svc_rqst *); -void svc_sock_update_bufs (struct svc_serv *serv);int svc_addsock (struct svc_serv *serv,struct net *net,const int fd,char *name_return,const size_t len,const struct cred *cred);
net/sunrpc/sunrpc.h +4 -0
@@ -36,7 +36,11 @@ static inline int sock_is_loopback(struct sock *sk) return loopback; } +struct svc_serv ; +struct svc_rqst ;int rpc_clients_notifier_register (void );void rpc_clients_notifier_unregister (void );void auth_domain_cleanup (void ); +void svc_sock_update_bufs (struct svc_serv *serv); +enum svc_auth_status svc_authenticate (struct svc_rqst *rqstp);#endif /* _NET_SUNRPC_SUNRPC_H */
net/sunrpc/svc.c +25 -23
102 lines not on this page · raw message
net/sunrpc/svc_xprt.c +0 -9
@@ -874,15 +874,6 @@ void svc_recv(struct svc_rqst *rqstp) }EXPORT_SYMBOL_GPL (svc_recv); -/* -* Drop request -*/ -void svc_drop (struct svc_rqst *rqstp) -{ -trace_svc_drop (rqstp); -} -EXPORT_SYMBOL_GPL (svc_drop); -/** * svc_send - Return reply to client * @rqstp: RPC transaction context
net/sunrpc/svcauth.c +0 -1
@@ -98,7 +98,6 @@ enum svc_auth_status svc_authenticate(struct svc_rqst *rqstp) rqstp->rq_authop = aops;return aops->accept (rqstp); } -EXPORT_SYMBOL_GPL (svc_authenticate);/** * svc_set_client - Assign an appropriate 'auth_domain' as the client
net/sunrpc/svcsock.c +0 -1
@@ -1435,7 +1435,6 @@ void svc_sock_update_bufs(struct svc_serv *serv)set_bit (XPT_CHNGBUF , &svsk->sk_xprt.xpt_flags);spin_unlock_bh (&serv->sv_lock); } -EXPORT_SYMBOL_GPL (svc_sock_update_bufs);/* * Initialize socket for RPC use and create svc_sock struct
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/7 SUNRPC: Introduce xdr_set_scratch_folio()
In reply to Sasha LevinFrom: Anna Schumaker <[email protected]>
[ Upstream commit c8a127596edc026e5364a7a609986dcd3999914c ]
This will replace xdr_set_scratch_page() when we switch pages to folios.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/xdr.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
include/linux/sunrpc/xdr.h +13 -0
@@ -317,6 +317,19 @@ xdr_set_scratch_page(struct xdr_stream *xdr, struct page *page)xdr_set_scratch_buffer (xdr,page_address (page),PAGE_SIZE ); } +/** +* xdr_set_scratch_folio - Attach a scratch buffer for decoding data +* @xdr: pointer to xdr_stream struct +* @page: an anonymous folio +* +* See xdr_set_scratch_buffer(). +*/ +static inline void +xdr_set_scratch_folio (struct xdr_stream *xdr,struct folio *folio) +{ +xdr_set_scratch_buffer (xdr,folio_address (folio),folio_size (folio)); +} +/** * xdr_reset_scratch_buffer - Clear scratch buffer information * @xdr: pointer to xdr_stream struct
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 6/7 SUNRPC: Update svcxdr_init_decode() to call xdr_set_scratch_folio()
In reply to Sasha LevinFrom: Anna Schumaker <[email protected]>
[ Upstream commit d57e43b72bf2071caac90da323849c3983a695f0 ]
The only snag here is that __folio_alloc_node() doesn't handle NUMA_NO_NODE, so I also need to update svc_pool_map_get_node() to return numa_mem_id() instead. I arrived at this approach by looking at what other users of __folio_alloc_node() do for this case.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 4 ++-- net/sunrpc/svc.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-)
include/linux/sunrpc/svc.h +2 -2
@@ -206,7 +206,7 @@ struct svc_rqst {struct xdr_buf rq_arg;struct xdr_stream rq_arg_stream;struct xdr_stream rq_res_stream; -struct page *rq_scratch_page; +struct folio *rq_scratch_folio;struct xdr_buf rq_res;struct page *rq_pages[RPCSVC_MAXPAGES +1 ];struct page * *rq_respages;/* points into rq_pages */
@@ -469,7 +469,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp) buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;xdr_init_decode (xdr, buf, argv->iov_base,NULL ); -xdr_set_scratch_page (xdr, rqstp->rq_scratch_page); +xdr_set_scratch_folio (xdr, rqstp->rq_scratch_folio); }/**
net/sunrpc/svc.c +5 -5
@@ -366,7 +366,7 @@ static int svc_pool_map_get_node(unsigned int pidx)if (m->mode ==SVC_POOL_PERNODE )return m->pool_to[pidx]; } -return NUMA_NO_NODE ; +return numa_mem_id (); }/* * Set the given thread's cpus_allowed mask so that it
@@ -682,8 +682,8 @@ svc_rqst_free(struct svc_rqst *rqstp) {folio_batch_release (&rqstp->rq_fbatch);svc_release_buffer (rqstp); -if (rqstp->rq_scratch_page) -put_page (rqstp->rq_scratch_page); +if (rqstp->rq_scratch_folio) +folio_put (rqstp->rq_scratch_folio);kfree (rqstp->rq_resp);kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data);
@@ -705,8 +705,8 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node) rqstp->rq_server = serv; rqstp->rq_pool = pool; - rqstp->rq_scratch_page =alloc_pages_node (node,GFP_KERNEL ,0 ); -if (!rqstp->rq_scratch_page) + rqstp->rq_scratch_folio =__folio_alloc_node (GFP_KERNEL ,0 , node); +if (!rqstp->rq_scratch_folio) goto out_enomem; rqstp->rq_argp =kmalloc_node (serv->sv_xdrsize,GFP_KERNEL , node);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 7/7 sunrpc: defer rq_argp and rq_resp free until after RCU grace period
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit c479bde671cbe2f9e152834a8b0eb7c3c295bbaf ]
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
--- net/sunrpc/svc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/svc.c +10 -3
@@ -677,6 +677,15 @@ svc_release_buffer(struct svc_rqst *rqstp)put_page (rqstp->rq_pages[i]); } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +static void svc_rqst_free (struct svc_rqst *rqstp) {
@@ -684,10 +693,8 @@ svc_rqst_free(struct svc_rqst *rqstp)svc_release_buffer (rqstp);if (rqstp->rq_scratch_folio)folio_put (rqstp->rq_scratch_folio); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst *
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 1/4 SUNRPC: move all of xprt handling into svc_xprt_handle()
In reply to [email protected]From: NeilBrown <[email protected]>
[ Upstream commit e3274026e2ec69eec6ab51bc499e14bb548548d0 ]
svc_xprt_handle() does lots of things itself, but leaves some to the caller - svc_recv(). This isn't elegant.
Move that code out of svc_recv() into svc_xprt_handle()
Move the calls to svc_xprt_release() from svc_send() and svc_drop() (the two possible final steps in svc_process()) and from svc_recv() (in the case where svc_process() wasn't called) into svc_xprt_handle().
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- net/sunrpc/svc_xprt.c | 53 ++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 33 deletions(-)
net/sunrpc/svc_xprt.c +20 -33
@@ -784,7 +784,7 @@ static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxptsvc_xprt_received (newxpt); } -static int svc_handle_xprt (struct svc_rqst *rqstp,struct svc_xprt *xprt) +static void svc_handle_xprt (struct svc_rqst *rqstp,struct svc_xprt *xprt) {struct svc_serv *serv = rqstp->rq_server;int len =0 ;
@@ -825,11 +825,26 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt) len = xprt->xpt_ops->xpo_recvfrom (rqstp); rqstp->rq_reserved = serv->sv_max_mesg;atomic_add (rqstp->rq_reserved, &xprt->xpt_reserved); +if (len <=0 ) + goto out; + +trace_svc_xdr_recvfrom (&rqstp->rq_arg); + +clear_bit (XPT_OLD , &xprt->xpt_flags); + + rqstp->rq_chandle.defer = svc_defer; + +if (serv->sv_stats) + serv->sv_stats->netcnt++; +percpu_counter_inc (&rqstp->rq_pool->sp_messages_arrived); + rqstp->rq_stime =ktime_get (); +svc_process (rqstp); }else svc_xprt_received (xprt); out: -return len; + rqstp->rq_res.len =0 ; +svc_xprt_release (rqstp); }/**
@@ -843,11 +858,9 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)void svc_recv (struct svc_rqst *rqstp) {struct svc_xprt *xprt =NULL ; -struct svc_serv *serv = rqstp->rq_server; -int len;if (!svc_alloc_arg (rqstp)) - goto out; +return ;try_to_freeze ();cond_resched ();
@@ -855,31 +868,9 @@ void svc_recv(struct svc_rqst *rqstp) goto out; xprt =svc_get_next_xprt (rqstp); -if (!xprt) - goto out; - - len =svc_handle_xprt (rqstp, xprt); - -/* No data, incomplete (TCP) read, or accept() */ -if (len <=0 ) - goto out_release; - -trace_svc_xdr_recvfrom (&rqstp->rq_arg); - -clear_bit (XPT_OLD , &xprt->xpt_flags); - - rqstp->rq_chandle.defer = svc_defer; - -if (serv->sv_stats) - serv->sv_stats->netcnt++; -percpu_counter_inc (&rqstp->rq_pool->sp_messages_arrived); - rqstp->rq_stime =ktime_get (); -svc_process (rqstp); +if (xprt) +svc_handle_xprt (rqstp, xprt); out: -return ; -out_release: - rqstp->rq_res.len =0 ; -svc_xprt_release (rqstp); }EXPORT_SYMBOL_GPL (svc_recv);
@@ -889,7 +880,6 @@ EXPORT_SYMBOL_GPL(svc_recv);void svc_drop (struct svc_rqst *rqstp) {trace_svc_drop (rqstp); -svc_xprt_release (rqstp); }EXPORT_SYMBOL_GPL (svc_drop);
@@ -905,8 +895,6 @@ void svc_send(struct svc_rqst *rqstp)int status; xprt = rqstp->rq_xprt; -if (!xprt) -return ;/* calculate over-all length */ xb = &rqstp->rq_res;
@@ -919,7 +907,6 @@ void svc_send(struct svc_rqst *rqstp) status = xprt->xpt_ops->xpo_sendto (rqstp);trace_svc_send (rqstp, status); -svc_xprt_release (rqstp); }/*
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 SUNRPC: Add a server-side API for retrieving an RPC's pseudoflavor
From: Chuck Lever <[email protected]>
[ Upstream commit deb704281f076097b0347116a82edeba96697db1 ]
NFSD will use this new API to determine whether nfsd_splice_read is safe to use. This avoids the need to add a dependency to NFSD for CONFIG_SUNRPC_GSS.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svcauth.h | 7 ++++++- net/sunrpc/auth_gss/svcauth_gss.c | 6 ++++++ net/sunrpc/svcauth.c | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-)
include/linux/sunrpc/svcauth.h +6 -1
@@ -131,8 +131,11 @@ enum svc_auth_status {* This call releases a domain. * * set_client() -* Givens a pending request (struct svc_rqst), finds and assigns +* Given a pending request (struct svc_rqst), finds and assigns * an appropriate 'auth_domain' as the client. +* +* pseudoflavor() +* Returns RPC_AUTH pseudoflavor in use by @rqstp. */ struct auth_ops {char * name;
@@ -143,11 +146,13 @@ struct auth_ops {int (*release)(struct svc_rqst *rqstp);void (*domain_release)(struct auth_domain *dom);enum svc_auth_status (*set_client)(struct svc_rqst *rqstp); +rpc_authflavor_t (*pseudoflavor)(struct svc_rqst *rqstp); };struct svc_xprt ;extern enum svc_auth_status svc_authenticate (struct svc_rqst *rqstp); +extern rpc_authflavor_t svc_auth_flavor (struct svc_rqst *rqstp);extern int svc_authorise (struct svc_rqst *rqstp);extern enum svc_auth_status svc_set_client (struct svc_rqst *rqstp);extern int svc_auth_register (rpc_authflavor_t flavor,struct auth_ops *aops);
net/sunrpc/auth_gss/svcauth_gss.c +6 -0
@@ -2009,6 +2009,11 @@ svcauth_gss_domain_release(struct auth_domain *dom)call_rcu (&dom->rcu_head, svcauth_gss_domain_release_rcu); } +static rpc_authflavor_t svcauth_gss_pseudoflavor (struct svc_rqst *rqstp) +{ +return svcauth_gss_flavor (rqstp->rq_gssclient); +} +static struct auth_ops svcauthops_gss = { .name ="rpcsec_gss" , .owner =THIS_MODULE ,
@@ -2017,6 +2022,7 @@ static struct auth_ops svcauthops_gss = { .release = svcauth_gss_release, .domain_release = svcauth_gss_domain_release, .set_client = svcauth_gss_set_client, + .pseudoflavor = svcauth_gss_pseudoflavor, };static int rsi_cache_create_net (struct net *net)
net/sunrpc/svcauth.c +16 -0
@@ -160,6 +160,22 @@ svc_auth_unregister(rpc_authflavor_t flavor) }EXPORT_SYMBOL_GPL (svc_auth_unregister); +/** +* svc_auth_flavor - return RPC transaction's RPC_AUTH flavor +* @rqstp: RPC transaction context +* +* Returns an RPC flavor or GSS pseudoflavor. +*/ +rpc_authflavor_t svc_auth_flavor (struct svc_rqst *rqstp) +{ +struct auth_ops *aops = rqstp->rq_authop; + +if (!aops->pseudoflavor) +return aops->flavour; +return aops->pseudoflavor (rqstp); +} +EXPORT_SYMBOL_GPL (svc_auth_flavor); +/************************************************** * 'auth_domains' are stored in a hash table indexed by name. * When the last reference to an 'auth_domain' is dropped,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 sunrpc: refactor pool_mode setting code
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit 5f71f3c325534da55dfda487a67208e2e0f0cd1b ]
Allow the pool_mode setting code to be called from internal callers so we can call it from a new netlink op. Add a new svc_pool_map_get function to return the current setting. Change the existing module parameter handling to use the new interfaces under the hood.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 2 + net/sunrpc/svc.c | 85 ++++++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 21 deletions(-)
include/linux/sunrpc/svc.h +2 -0
@@ -395,6 +395,8 @@ struct svc_procedure {/* * Function prototypes. */ +int sunrpc_set_pool_mode (const char *val); +int sunrpc_get_pool_mode (char *val,size_t size);int svc_rpcb_setup (struct svc_serv *serv,struct net *net);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net);int svc_bind (struct svc_serv *serv,struct net *net);
net/sunrpc/svc.c +64 -21
121 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 sunrpc: defer rq_argp and rq_resp free until after RCU grace period
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit c479bde671cbe2f9e152834a8b0eb7c3c295bbaf ]
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
--- net/sunrpc/svc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/svc.c +10 -3
@@ -946,6 +946,15 @@ void svc_rqst_release_pages(struct svc_rqst *rqstp) } } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +/* * Called from a server thread as it's exiting. Caller must hold the "service * mutex" for the service.
@@ -957,10 +966,8 @@ svc_rqst_free(struct svc_rqst *rqstp)svc_release_buffer (rqstp);if (rqstp->rq_scratch_page)put_page (rqstp->rq_scratch_page); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); }EXPORT_SYMBOL_GPL (svc_rqst_free);
Signature · 1 line
2.53.0