FAILED: patch "[PATCH] nfsd: widen nfsd_genl_rqstp address fields to" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-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.12.y
git checkout FETCH_HEAD
git cherry-pick -x a99d720ed2a5258564e5e9d5f39f3184a030d354
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090358-starry-expulsion-6b91@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From a99d720ed2a5258564e5e9d5f39f3184a030d354 Mon Sep 17 00:00:00 2001
From: Jeff Layton <[email protected]>
Date: Tue, 2 Jun 2026 12:23:17 -0400
Subject: [PATCH] nfsd: widen nfsd_genl_rqstp address fields to
sockaddr_storage
struct nfsd_genl_rqstp declares rq_daddr and rq_saddr as plain "struct sockaddr" (16 bytes). When an IPv6 NFS client is connected, nfsd_genl_rpc_status_compose_msg() casts these fields to "struct sockaddr_in6 *" (28 bytes) and reads sin6_addr at offset 8..24, which extends 8 bytes past the end of the 16-byte sockaddr field into the adjacent rq_flags member. The 16-byte nla_put_in6_addr then ships 8 bytes of truncated IPv6 address followed by 8 bytes of rq_flags to userspace via the NFSD_A_RPC_STATUS_SADDR6/DADDR6 netlink attributes.
This is reachable by any unprivileged process in the network namespace because NFSD_CMD_RPC_STATUS_GET uses GENL_CMD_CAP_DUMP without GENL_ADMIN_PERM.
Fix by widening rq_daddr and rq_saddr to struct sockaddr_storage so the IPv6 casts operate within bounds, copying sizeof(struct sockaddr_storage) bytes in the memcpy calls so the full address is captured, and zero-initializing the genl_rqstp stack variable to prevent leaking uninitialized tail bytes through netlink.
fs/nfsd/nfsctl.c +6 -6
@@ -1414,8 +1414,8 @@ static int create_proc_exports_entry(void)unsigned int nfsd_net_id;struct nfsd_genl_rqstp { -struct sockaddr rq_daddr; -struct sockaddr rq_saddr; +struct sockaddr_storage rq_daddr; +struct sockaddr_storage rq_saddr;unsigned long rq_flags;ktime_t rq_stime;__be32 rq_xid;
@@ -1450,7 +1450,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, NFSD_A_RPC_STATUS_PAD))return -ENOBUFS ; -switch (genl_rqstp->rq_saddr.sa_family) { +switch (genl_rqstp->rq_saddr.ss_family) {case AF_INET : {const struct sockaddr_in *s_in, *d_in;
@@ -1527,7 +1527,7 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,list_for_each_entry_rcu (rqstp, &nn->nfsd_serv->sv_pools[i].sp_all_threads, rq_all) { -struct nfsd_genl_rqstp genl_rqstp; +struct nfsd_genl_rqstp genl_rqstp = {};unsigned int status_counter;if (rqstp_index++ < cb->args[1 ])/* already consumed */
@@ -1551,9 +1551,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, genl_rqstp.rq_stime = rqstp->rq_stime; genl_rqstp.rq_opcnt =0 ;memcpy (&genl_rqstp.rq_daddr,svc_daddr (rqstp), -sizeof (struct sockaddr )); +sizeof (struct sockaddr_storage ));memcpy (&genl_rqstp.rq_saddr,svc_addr (rqstp), -sizeof (struct sockaddr )); +sizeof (struct sockaddr_storage ));#ifdef CONFIG_NFSD_V4 if (rqstp->rq_vers == NFS4_VERSION &&
Sasha Levin
maintainerPATCH 1/4 NFSD: Rename a function parameter
From: Chuck Lever <[email protected]>
[ Upstream commit 71bc2c6c413b78e5a47b4a62284f712d9f719007 ]
Clean up: A function parameter called "rqstp" typically refers to an object of type "struct svc_rqst", so it's confusing when such an parameter refers to a different struct type with field names that are very similar to svc_rqst.
Stable-dep-of: a99d720ed2a5 ("nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage")
--- fs/nfsd/nfsctl.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
fs/nfsd/nfsctl.c +14 -14
@@ -1494,7 +1494,7 @@ unsigned int nfsd_net_id;static int nfsd_genl_rpc_status_compose_msg (struct sk_buff *skb,struct netlink_callback *cb, -struct nfsd_genl_rqstp *rqstp) +struct nfsd_genl_rqstp *genl_rqstp) {void *hdr;u32 i;
@@ -1504,22 +1504,22 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, if (!hdr) return -ENOBUFS; - if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, rqstp->rq_xid) || - nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, rqstp->rq_flags) || - nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, rqstp->rq_prog) || - nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, rqstp->rq_proc) || - nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, rqstp->rq_vers) || + if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, genl_rqstp->rq_xid) || + nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, genl_rqstp->rq_flags) || + nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, genl_rqstp->rq_prog) || + nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, genl_rqstp->rq_proc) || + nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, genl_rqstp->rq_vers) || nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME, - ktime_to_us(rqstp->rq_stime), + ktime_to_us(genl_rqstp->rq_stime), NFSD_A_RPC_STATUS_PAD)) return -ENOBUFS; - switch (rqstp->rq_saddr.sa_family) { + switch (genl_rqstp->rq_saddr.sa_family) { case AF_INET: { const struct sockaddr_in *s_in, *d_in; - s_in = (const struct sockaddr_in *)&rqstp->rq_saddr; - d_in = (const struct sockaddr_in *)&rqstp->rq_daddr; + s_in = (const struct sockaddr_in *)&genl_rqstp->rq_saddr; + d_in = (const struct sockaddr_in *)&genl_rqstp->rq_daddr; if (nla_put_in_addr(skb, NFSD_A_RPC_STATUS_SADDR4, s_in->sin_addr.s_addr) || nla_put_in_addr(skb, NFSD_A_RPC_STATUS_DADDR4,
@@ -1534,8 +1534,8 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, case AF_INET6: { const struct sockaddr_in6 *s_in, *d_in; - s_in = (const struct sockaddr_in6 *)&rqstp->rq_saddr; - d_in = (const struct sockaddr_in6 *)&rqstp->rq_daddr; + s_in = (const struct sockaddr_in6 *)&genl_rqstp->rq_saddr; + d_in = (const struct sockaddr_in6 *)&genl_rqstp->rq_daddr; if (nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_SADDR6, &s_in->sin6_addr) || nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_DADDR6,
@@ -1549,9 +1549,9 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, } } -for (i =0 ; i < rqstp->rq_opcnt; i++) +for (i =0 ; i < genl_rqstp->rq_opcnt; i++)if (nla_put_u32 (skb,NFSD_A_RPC_STATUS_COMPOUND_OPS , - rqstp->rq_opnum[i])) + genl_rqstp->rq_opnum[i]))return -ENOBUFS ;genlmsg_end (skb, hdr);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 NFSD: Make nfsd_genl_rqstp::rq_ops array best-effort
From: Chuck Lever <[email protected]>
[ Upstream commit a2d61427fb4b630b94ed9bd457dd8bc239b83e4b ]
To enable NFSD to handle NFSv4 COMPOUNDs of unrestricted size, resize the array in struct nfsd_genl_rqstp so it saves only up to 16 operations per COMPOUND.
Stable-dep-of: a99d720ed2a5 ("nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage")
--- fs/nfsd/nfsctl.c | 3 ++- fs/nfsd/nfsd.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
fs/nfsd/nfsctl.c +2 -1
@@ -1627,7 +1627,8 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,int j; args = rqstp->rq_argp; - genl_rqstp.rq_opcnt = args->opcnt; + genl_rqstp.rq_opcnt =min_t (u32, args->opcnt, +ARRAY_SIZE (genl_rqstp.rq_opnum));for (j =0 ; j < genl_rqstp.rq_opcnt; j++) genl_rqstp.rq_opnum[j] = args->ops[j].opnum;
fs/nfsd/nfsd.h +1 -1
@@ -82,7 +82,7 @@ struct nfsd_genl_rqstp {/* NFSv4 compound */ u32 rq_opcnt; -u32 rq_opnum[NFSD_MAX_OPS_PER_COMPOUND ]; +u32 rq_opnum[16 ]; };extern struct svc_program nfsd_programs[];
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 Revert "NFSD: Remove the cap on number of operations per NFSv4 COMPOUND"
In reply to Sasha LevinFrom: Chuck Lever <[email protected]>
[ Upstream commit 3e7f011c255582d7c914133785bbba1990441713 ]
I've found that pynfs COMP6 now leaves the connection or lease in a strange state, which causes CLOSE9 to hang indefinitely. I've dug into it a little, but I haven't been able to root-cause it yet. However, I bisected to commit 48aab1606fa8 ("NFSD: Remove the cap on number of operations per NFSv4 COMPOUND").
Tianshuo Han also reports a potential vulnerability when decoding an NFSv4 COMPOUND. An attacker can place an arbitrarily large op count in the COMPOUND header, which results in:
[ 51.410584] nfsd: vmalloc error: size 1209533382144, exceeds total
pages, mode:0xdc0(GFP_KERNEL|__GFP_ZERO),
nodemask=(null),cpuset=/,mems_allowed=0
when NFSD attempts to allocate the COMPOUND op array.
Let's restore the operation-per-COMPOUND limit, but increased to 200 for now.
The 6.12 tree already contains the functional changes from this revert and enforces a stricter 50-operation cap. Retain that cap and use this dependency to relocate struct nfsd_genl_rqstp into its sole user so the follow-up address-width fix applies at the same location as upstream.
[ sashal: Reduced backport -- upstream 3e7f011c25558 touches 5 file(s), this
backport carries 2. Not backported here:
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/xdr4.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: a99d720ed2a5 ("nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage")
--- fs/nfsd/nfsctl.c | 15 +++++++++++++++ fs/nfsd/nfsd.h | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-)
fs/nfsd/nfsctl.c +15 -0
@@ -1492,6 +1492,21 @@ static int create_proc_exports_entry(void)unsigned int nfsd_net_id; +struct nfsd_genl_rqstp { +struct sockaddr rq_daddr; +struct sockaddr rq_saddr; +unsigned long rq_flags; +ktime_t rq_stime; +__be32 rq_xid; +u32 rq_vers; +u32 rq_prog; +u32 rq_proc; + +/* NFSv4 compound */ +u32 rq_opcnt; +u32 rq_opnum[16 ]; +}; +static int nfsd_genl_rpc_status_compose_msg (struct sk_buff *skb,struct netlink_callback *cb,struct nfsd_genl_rqstp *genl_rqstp)
fs/nfsd/nfsd.h +0 -15
@@ -70,21 +70,6 @@ struct readdir_cd {/* Maximum number of operations per session compound */ #define NFSD_MAX_OPS_PER_COMPOUND 50 -struct nfsd_genl_rqstp { -struct sockaddr rq_daddr; -struct sockaddr rq_saddr; -unsigned long rq_flags; -ktime_t rq_stime; -__be32 rq_xid; -u32 rq_vers; -u32 rq_prog; -u32 rq_proc; - -/* NFSv4 compound */ -u32 rq_opcnt; -u32 rq_opnum[16 ]; -}; -extern struct svc_program nfsd_programs[];extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;extern struct mutex nfsd_mutex;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit a99d720ed2a5258564e5e9d5f39f3184a030d354 ]
struct nfsd_genl_rqstp declares rq_daddr and rq_saddr as plain "struct sockaddr" (16 bytes). When an IPv6 NFS client is connected, nfsd_genl_rpc_status_compose_msg() casts these fields to "struct sockaddr_in6 *" (28 bytes) and reads sin6_addr at offset 8..24, which extends 8 bytes past the end of the 16-byte sockaddr field into the adjacent rq_flags member. The 16-byte nla_put_in6_addr then ships 8 bytes of truncated IPv6 address followed by 8 bytes of rq_flags to userspace via the NFSD_A_RPC_STATUS_SADDR6/DADDR6 netlink attributes.
This is reachable by any unprivileged process in the network namespace because NFSD_CMD_RPC_STATUS_GET uses GENL_CMD_CAP_DUMP without GENL_ADMIN_PERM.
Fix by widening rq_daddr and rq_saddr to struct sockaddr_storage so the IPv6 casts operate within bounds, copying sizeof(struct sockaddr_storage) bytes in the memcpy calls so the full address is captured, and zero-initializing the genl_rqstp stack variable to prevent leaking uninitialized tail bytes through netlink.
--- fs/nfsd/nfsctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
fs/nfsd/nfsctl.c +6 -6
@@ -1493,8 +1493,8 @@ static int create_proc_exports_entry(void)unsigned int nfsd_net_id;struct nfsd_genl_rqstp { -struct sockaddr rq_daddr; -struct sockaddr rq_saddr; +struct sockaddr_storage rq_daddr; +struct sockaddr_storage rq_saddr;unsigned long rq_flags;ktime_t rq_stime;__be32 rq_xid;
@@ -1529,7 +1529,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, NFSD_A_RPC_STATUS_PAD))return -ENOBUFS ; -switch (genl_rqstp->rq_saddr.sa_family) { +switch (genl_rqstp->rq_saddr.ss_family) {case AF_INET : {const struct sockaddr_in *s_in, *d_in;
@@ -1606,7 +1606,7 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,list_for_each_entry_rcu (rqstp, &nn->nfsd_serv->sv_pools[i].sp_all_threads, rq_all) { -struct nfsd_genl_rqstp genl_rqstp; +struct nfsd_genl_rqstp genl_rqstp = {};unsigned int status_counter;if (rqstp_index++ < cb->args[1 ])/* already consumed */
@@ -1630,9 +1630,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, genl_rqstp.rq_stime = rqstp->rq_stime; genl_rqstp.rq_opcnt =0 ;memcpy (&genl_rqstp.rq_daddr,svc_daddr (rqstp), -sizeof (struct sockaddr )); +sizeof (struct sockaddr_storage ));memcpy (&genl_rqstp.rq_saddr,svc_addr (rqstp), -sizeof (struct sockaddr )); +sizeof (struct sockaddr_storage ));#ifdef CONFIG_NFSD_V4 if (rqstp->rq_vers == NFS4_VERSION &&
Signature · 1 line
2.53.0