6.18.51-rc1 review
Subsystems (5)
Greg Kroah-Hartman
maintainerPATCH 525/583 ipv4: use dst4_mtu() instead of dst_mtu()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit fe8570186f100b6cc499b2f7705946baf1388cde ]
When we expect an IPv4 dst, use dst4_mtu() instead of dst_mtu() to save some code space.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- net/ipv4/icmp.c | 2 +- net/ipv4/ip_output.c | 4 ++-- net/ipv4/ip_sockglue.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/netfilter/nf_reject_ipv4.c | 2 +- net/ipv4/tcp_ipv4.c | 13 ++++++------- net/ipv6/sit.c | 2 +- 7 files changed, 13 insertions(+), 14 deletions(-)
net/ipv4/icmp.c +1 -1
@@ -790,7 +790,7 @@ void __icmp_send(struct sk_buff *skb_in,/* RFC says return as much as we can without exceeding 576 bytes. */ - room =dst_mtu (&rt->dst); + room =dst4_mtu (&rt->dst);if (room >576 ) room =576 ; room -=sizeof (struct iphdr ) + icmp_param.replyopts.opt.opt.optlen;
net/ipv4/ip_output.c +2 -2
@@ -1303,7 +1303,7 @@ static int ip_setup_cork(struct sock *skreturn -EFAULT ; cork->fragsize =ip_sk_use_pmtu (sk) ? -dst_mtu (&rt->dst) :READ_ONCE (rt->dst.dev->mtu); +dst4_mtu (&rt->dst) :READ_ONCE (rt->dst.dev->mtu);if (!inetdev_valid_mtu (cork->fragsize))return -ENETUNREACH ;
@@ -1442,7 +1442,7 @@ struct sk_buff *__ip_make_skb(struct soc pmtudisc =READ_ONCE (inet->pmtudisc);if (pmtudisc ==IP_PMTUDISC_DO || pmtudisc ==IP_PMTUDISC_PROBE || - (skb->len <=dst_mtu (&rt->dst) && + (skb->len <=dst4_mtu (&rt->dst) &&ip_dont_fragment (sk, &rt->dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -1634,7 +1634,7 @@ int do_ip_getsockopt(struct sock *sk, in val =0 ; dst =sk_dst_get (sk);if (dst) { - val =dst_mtu (dst); + val =dst4_mtu (dst);dst_release (dst); }if (!val)
net/ipv4/ipmr.c +1 -1
@@ -1895,7 +1895,7 @@ static int ipmr_prepare_xmit(struct netreturn -1 ; } -if (skb->len+encap >dst_mtu (&rt->dst) && (ntohs (iph->frag_off) &IP_DF )) { +if (skb->len+encap >dst4_mtu (&rt->dst) && (ntohs (iph->frag_off) &IP_DF )) {/* Do not fragment multicasts. Alas, IPv4 does not * allow to send ICMP, so that packets will disappear * to blackhole.
net/ipv4/netfilter/nf_reject_ipv4.c +1 -1
@@ -303,7 +303,7 @@ void nf_send_reset(struct net *net, stru goto free_nskb;/* "Never happens" */ -if (nskb->len >dst_mtu (skb_dst (nskb))) +if (nskb->len >dst4_mtu (skb_dst (nskb))) goto free_nskb;nf_ct_attach (nskb, oldskb);
net/ipv4/tcp_ipv4.c +6 -7
@@ -373,7 +373,7 @@ void tcp_v4_mtu_reduced(struct sock *sk) {struct inet_sock *inet =inet_sk (sk);struct dst_entry *dst; -u32 mtu; +u32 mtu, dmtu;if ((1 << sk->sk_state) & (TCPF_LISTEN |TCPF_CLOSE ))return ;
@@ -385,15 +385,14 @@ void tcp_v4_mtu_reduced(struct sock *sk)/* Something is about to be wrong... Remember soft error * for the case, if this connection will not able to recover. */ -if (mtu <dst_mtu (dst) &&ip_dont_fragment (sk, dst)) + dmtu =dst4_mtu (dst); +if (mtu < dmtu &&ip_dont_fragment (sk, dst))WRITE_ONCE (sk->sk_err_soft,EMSGSIZE ); - mtu =dst_mtu (dst); -if (inet->pmtudisc !=IP_PMTUDISC_DONT &&ip_sk_accept_pmtu (sk) && -inet_csk (sk)->icsk_pmtu_cookie > mtu) { -tcp_sync_mss (sk, mtu); +inet_csk (sk)->icsk_pmtu_cookie > dmtu) { +tcp_sync_mss (sk, dmtu);/* Resend the TCP packet because it's * clear that the old packet has been
@@ -1817,7 +1816,7 @@ struct sock *tcp_v4_syn_recv_sock(const#endif tcp_ca_openreq_child (newsk, dst); -tcp_sync_mss (newsk,dst_mtu (dst)); +tcp_sync_mss (newsk,dst4_mtu (dst)); newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst));tcp_initialize_rcv_mss (newsk);
net/ipv6/sit.c +1 -1
@@ -963,7 +963,7 @@ static netdev_tx_t ipip6_tunnel_xmit(str iph6 =ipv6_hdr (skb);if (df) { - mtu =dst_mtu (&rt->dst) - t_hlen; + mtu =dst4_mtu (&rt->dst) - t_hlen;if (mtu <IPV4_MIN_MTU ) {DEV_STATS_INC (dev, collisions);
Greg Kroah-Hartman
maintainerPATCH 526/583 tcp: clamp route advmss to TCP_MIN_MSS
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yong Wang <[email protected]>
[ Upstream commit 870a9e42ecc6fe1b8c25d87af043cb0d9c178fe1 ]
tcp_select_initial_window() assumes that callers never pass an MSS smaller than 1, but route-derived advmss values can violate that assumption.
A too-small explicit RTAX_ADVMSS is one way to get there, but it is not the only one. The same divide-by-zero can also be reached through the "default advmss" path when RTAX_ADVMSS is left at 0 and the effective advmss is later driven down by route MTU and min_adv_mss.
Introduce a tcp_dst_advmss() helper that clamps route advmss to TCP_MIN_MSS before TCP consumes it, and use it in the TCP paths that derive advmss from dst metrics. This keeps the effective MSS from dropping to zero before tcp_select_initial_window() rounds the receive window.
--- include/net/tcp.h | 5 +++++ net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_minisocks.c | 2 +- net/ipv4/tcp_output.c | 6 +++--- net/ipv6/tcp_ipv6.c | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-)
include/net/tcp.h +5 -0
@@ -1686,6 +1686,11 @@ static inline int tcp_full_space(const sreturn tcp_win_from_space (sk,READ_ONCE (sk->sk_rcvbuf)); } +static inline u32 tcp_dst_advmss (const struct dst_entry *dst) +{ +return max_t (u32,dst_metric_advmss (dst),TCP_MIN_MSS ); +} +static inline void __tcp_adjust_rcv_ssthresh (struct sock *sk,u32 new_ssthresh) {int unused_mem =sk_unused_reserved_mem (sk);
net/ipv4/tcp_ipv4.c +1 -1
@@ -1817,7 +1817,7 @@ struct sock *tcp_v4_syn_recv_sock(consttcp_ca_openreq_child (newsk, dst);tcp_sync_mss (newsk,dst4_mtu (dst)); - newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst)); + newtp->advmss =tcp_mss_clamp (tcp_sk (sk),tcp_dst_advmss (dst));tcp_initialize_rcv_mss (newsk);
net/ipv4/tcp_minisocks.c +1 -1
@@ -445,7 +445,7 @@ void tcp_openreq_init_rwin(struct requesu32 rcv_wnd;int mss; - mss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + mss =tcp_mss_clamp (tp,tcp_dst_advmss (dst)); window_clamp =READ_ONCE (tp->window_clamp);/* Set this up on the first call only */ req->rsk_window_clamp = window_clamp ? :dst_metric (dst,RTAX_WINDOW );
net/ipv4/tcp_output.c +3 -3
@@ -129,7 +129,7 @@ static __u16 tcp_advertise_mss(struct soint mss = tp->advmss;if (dst) { -unsigned int metric =dst_metric_advmss (dst); +unsigned int metric =tcp_dst_advmss (dst);if (metric < mss) { mss = metric;
@@ -3906,7 +3906,7 @@ struct sk_buff *tcp_make_synack(const st }skb_dst_set (skb, dst); - mss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + mss =tcp_mss_clamp (tp,tcp_dst_advmss (dst));memset (&opts,0 ,sizeof (opts)); now =tcp_clock_ns ();
@@ -4062,7 +4062,7 @@ static void tcp_connect_init(struct sockif (!tp->window_clamp)WRITE_ONCE (tp->window_clamp,dst_metric (dst,RTAX_WINDOW )); - tp->advmss =tcp_mss_clamp (tp,dst_metric_advmss (dst)); + tp->advmss =tcp_mss_clamp (tp,tcp_dst_advmss (dst));tcp_initialize_rcv_mss (sk);
net/ipv6/tcp_ipv6.c +1 -1
@@ -1501,7 +1501,7 @@ static struct sock *tcp_v6_syn_recv_socktcp_ca_openreq_child (newsk, dst);tcp_sync_mss (newsk,dst6_mtu (dst)); - newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst)); + newtp->advmss =tcp_mss_clamp (tcp_sk (sk),tcp_dst_advmss (dst));tcp_initialize_rcv_mss (newsk);
Greg Kroah-Hartman
maintainerPATCH 527/583 net: advertise TCP MSS from the configured MTU, not the learned PMTU
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
commit 2640e64195948a601430d230c9864f5426574cde upstream.
The MSS a host puts in its SYN tells the peer how big a segment it may send us. Right now we can shrink it with a PMTU we learned on our own send path, which is the wrong direction entirely.
On asymmetric paths this bites - think DSR load balancers, where the request side goes through a smaller-MTU overlay. We learn a small PMTU going out, then advertise a small MSS, and the peer stays capped for the whole connection even though its path back to us is wide. MSS only shows up in the SYN and never grows back.
On symmetric paths we lose nothing by dropping it either: the peer runs its own PMTU discovery and usually already knows the real path MTU.
So work out the advertised MSS from the configured route or device MTU and ignore the learned PMTU. Our send side is unchanged, still clamped by tcp_current_mss(). Add ip_dst_mtu_configured()/ip6_dst_mtu_configured() and use them from the two default_advmss() paths.
--- include/net/ip.h | 25 +++++++++++++++++++++++++ include/net/ip6_route.h | 37 +++++++++++++++++++++++++++++++++++++ net/ipv4/route.c | 4 ++-- net/ipv6/route.c | 2 +- 4 files changed, 65 insertions(+), 3 deletions(-)
include/net/ip.h +25 -0
@@ -506,6 +506,31 @@ out:return res; } +/* Configured/administrative MTU of a route, for advertising the TCP MSS. +* +* Unlike ip_dst_mtu_maybe_forward(), this deliberately ignores the +* ICMP-learned path MTU (rt->rt_pmtu). The advertised MSS bounds what the +* peer may send to us and must reflect our receive capability (the device or +* route-configured MTU), not a path MTU learned on the reverse (send) +* direction, which may not apply to the peer->us path and outlives the fnhe +* for the whole connection. See RFC 2923 section 2.3 and the comment above +* tcp_advertise_mss(). +*/ +static inline unsigned int ip_dst_mtu_configured (const struct dst_entry *dst) +{ +unsigned int mtu, res; + +rcu_read_lock (); + mtu =dst_metric_raw (dst,RTAX_MTU ); +if (!mtu) + mtu =READ_ONCE (dst_dev_rcu (dst)->mtu); + mtu = min_t(unsigned int, mtu, IP_MAX_MTU); + res = mtu -lwtunnel_headroom (dst->lwtstate, mtu); +rcu_read_unlock (); + +return res; +} +static inline unsigned int ip_skb_dst_mtu (struct sock *sk,const struct sk_buff *skb) {
include/net/ip6_route.h +37 -0
@@ -352,6 +352,43 @@ out:return mtu -lwtunnel_headroom (dst->lwtstate, mtu); } +/* Configured/administrative MTU of a route, for advertising the TCP MSS. +* +* Unlike ip6_dst_mtu_maybe_forward(), this ignores any ICMPv6-learned path +* MTU (which is kept on the RTF_CACHE exception route) and returns the MTU of +* the underlying route (fib6_pmtu) or the egress device. The advertised MSS +* bounds what the peer may send to us and must reflect our receive +* capability, not a path MTU learned on the reverse (send) direction. See +* RFC 2923 section 2.3 and the comment above tcp_advertise_mss(). +*/ +static inline unsigned int ip6_dst_mtu_configured (const struct dst_entry *dst) +{ +const struct rt6_info *rt =dst_rt6_info (dst); +const struct fib6_info *from; +struct inet6_dev *idev; +unsigned int mtu =0 ; + +rcu_read_lock (); +/* IPv6 keeps the learned PMTU and the configured MTU in the same +* RTAX_MTU slot: the learned value sits on this (possibly RTF_CACHE) +* dst, the configured one on the underlying route. Reach the latter +* via ->from (fib6_pmtu), populated by ip6_route_info_create(). +*/ + from =rcu_dereference (rt->from); +if (from) + mtu = from->fib6_pmtu; +if (!mtu) { + mtu =IPV6_MIN_MTU ; + idev =__in6_dev_get (dst_dev_rcu (dst)); +if (idev) + mtu = max_t(unsigned int, mtu, READ_ONCE(idev->cnf.mtu6)); + } +rcu_read_unlock (); + + mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); +return mtu -lwtunnel_headroom (dst->lwtstate, mtu); +} +u32 ip6_mtu_from_fib6 (const struct fib6_result *res,const struct in6_addr *daddr,const struct in6_addr *saddr);
net/ipv4/route.c +2 -2
@@ -1360,8 +1360,8 @@ static unsigned int ipv4_default_advmss(rcu_read_lock (); net =dst_dev_net_rcu (dst); - advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, - net->ipv4.ip_rt_min_advmss); + advmss = max_t(unsigned int, ip_dst_mtu_configured(dst) - header_size, + net->ipv4.ip_rt_min_advmss);rcu_read_unlock ();return min (advmss,IPV4_MAX_PMTU - header_size);
net/ipv6/route.c +1 -1
@@ -3254,7 +3254,7 @@ EXPORT_SYMBOL_GPL(ip6_sk_redirect);static unsigned int ip6_default_advmss (const struct dst_entry *dst) { -unsigned int mtu =dst6_mtu (dst); +unsigned int mtu =ip6_dst_mtu_configured (dst);struct net *net; mtu -=sizeof (struct ipv6hdr ) +sizeof (struct tcphdr );
Greg Kroah-Hartman
maintainerPATCH 528/583 nfsd: check nfsd4_acl_to_attr() return value in nfsd4_create()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit 2c7912732184773dbd371a411da87af1cc080b86 upstream.
nfsd4_create() stores the return value of nfsd4_acl_to_attr() in status, but the switch(create->cr_type) block unconditionally overwrites it in every branch. ACL translation errors are silently discarded, and the CREATE proceeds without the requested ACL.
Add an early exit check after nfsd4_acl_to_attr(), matching the pattern already used in nfsd4_setattr().
--- fs/nfsd/nfs4proc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
fs/nfsd/nfs4proc.c +17 -0
@@ -800,7 +800,23 @@ nfsd4_create(struct svc_rqst *rqstp, strif (status)return status; +/* Sanitize cr_type to avoid returning ATTRNOTSUPP. */ +switch (create->cr_type) { +case NF4LNK : +case NF4BLK : +case NF4CHR : +case NF4SOCK : +case NF4FIFO : +case NF4DIR : +break ; +default : + status = nfserr_badtype; + goto out_aftermask; + } + status =nfsd4_acl_to_attr (create->cr_type, create->cr_acl, &attrs); +if (status != nfs_ok) + goto out_aftermask; current->fs->umask = create->cr_umask;switch (create->cr_type) {case NF4LNK :
@@ -867,6 +883,7 @@ out:fh_put (&resfh); out_umask: current->fs->umask =0 ; +out_aftermask:nfsd_attrs_free (&attrs);return status; }
Greg Kroah-Hartman
maintainerPATCH 529/583 nfsd: move struct nfsd_genl_rqstp to nfsctl.c
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit 1ed3df33bdbda5fd639571afe9c7cd282ff82cd9 upstream.
It's not used outside of that file.
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
@@ -1411,6 +1411,21 @@ static int create_proc_exports_entry(voiunsigned 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
@@ -60,21 +60,6 @@ struct readdir_cd {/* Maximum number of operations per session compound */ #define NFSD_MAX_OPS_PER_COMPOUND 200 -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;
Greg Kroah-Hartman
maintainerPATCH 530/583 nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit a99d720ed2a5258564e5e9d5f39f3184a030d354 upstream.
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
@@ -1412,8 +1412,8 @@ static int create_proc_exports_entry(voiunsigned 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;
@@ -1448,7 +1448,7 @@ static int nfsd_genl_rpc_status_compose_ NFSD_A_RPC_STATUS_PAD)) goto out_cancel; -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;
@@ -1539,7 +1539,7 @@ int nfsd_nl_rpc_status_get_dumpit(structlist_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++ < thread_skip)/* already consumed */
@@ -1563,9 +1563,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct 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 &&
Greg Kroah-Hartman
maintainerPATCH 531/583 nfsd: fix clock domain mismatch in clients_still_reclaiming()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 09ea3eb9a518565f5bca386e81b993ed8825f5e8 ]
clients_still_reclaiming() computes a deadline from nn->boot_time (CLOCK_REALTIME, ~1.7 billion) but compares it against ktime_get_boottime_seconds() (CLOCK_BOOTTIME, seconds since boot). The comparison is always false — it would take ~54 years of uptime for BOOTTIME to exceed the REALTIME-derived deadline.
This means any client can hold the server in grace indefinitely by sending CLAIM_PREVIOUS OPEN requests, blocking all non-reclaim operations for all other clients.
Add boot_time_bt (CLOCK_BOOTTIME) alongside the existing boot_time and use it for the deadline computation. boot_time (CLOCK_REALTIME) is preserved for its cl_boot clientid-nonce role.
--- fs/nfsd/netns.h | 1 + fs/nfsd/nfs4state.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
fs/nfsd/netns.h +1 -0
@@ -69,6 +69,7 @@ struct nfsd_net {bool grace_end_forced;bool client_tracking_active;time64_t boot_time; +time64_t boot_time_bt;/* same instant in CLOCK_BOOTTIME */ struct dentry *nfsd_client_dir;
fs/nfsd/nfs4state.c +2 -1
@@ -6769,7 +6769,7 @@ bool nfsd4_force_end_grace(struct nfsd_n*/ static bool clients_still_reclaiming (struct nfsd_net *nn) { -time64_t double_grace_period_end = nn->boot_time + +time64_t double_grace_period_end = nn->boot_time_bt +2 * nn->nfsd4_lease;if (READ_ONCE (nn->grace_end_forced))
@@ -9120,6 +9120,7 @@ static int nfs4_state_create_net(struct nn->conf_name_tree =RB_ROOT ; nn->unconf_name_tree =RB_ROOT ; nn->boot_time =ktime_get_real_seconds (); + nn->boot_time_bt =ktime_get_boottime_seconds (); nn->grace_ended = false; nn->grace_end_forced = false; nn->client_tracking_active = false;
Greg Kroah-Hartman
maintainerPATCH 532/583 nfsd: fix fcache_disposal UAF by inlining dispose state into nfsd_net
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit bbf13732f74351d21c5e0e8dd9bd8e1c48dc35d4 ]
nfsd_file_dispose_list_delayed() defers fput() to nfsd service threads via a per-net freeme queue, preventing the shrinker and GC worker from bearing the cost of closing files (see ffb402596147). However, the queue lives in a separately-allocated struct nfsd_fcache_disposal that is freed by nfsd_free_fcache_disposal_net() during per-net teardown. The global shrinker, laundrette, and fsnotify callbacks can still be inside nfsd_file_dispose_list_delayed() dereferencing that pointer, causing a use-after-free.
Inline the spinlock and freeme list directly into struct nfsd_net (as fcache_dispose_lock and fcache_dispose_list), eliminating the separately allocated struct nfsd_fcache_disposal entirely. These fields now have the same lifetime as the net namespace itself, so there is no dangling pointer to chase.
nfsd_file_cache_start_net() now just initializes the inline fields and cannot fail due to allocation. nfsd_file_cache_shutdown_net() drains the inline list directly instead of freeing a separate struct. The alloc/free helpers are removed.
--- fs/nfsd/filecache.c | 77 ++++++++++++++-------------------------------------- fs/nfsd/netns.h | 3 +- 2 files changed, 24 insertions(+), 56 deletions(-)
fs/nfsd/filecache.c +22 -55
@@ -62,11 +62,6 @@ static DEFINE_PER_CPU(unsigned long, nfs static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age); static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions); -struct nfsd_fcache_disposal { -spinlock_t lock; -struct list_head freeme; -}; -static struct kmem_cache *nfsd_file_slab;static struct kmem_cache *nfsd_file_mark_slab;static struct list_lru nfsd_file_lru;
@@ -422,25 +417,25 @@ nfsd_file_dispose_list(struct list_head }/** -* nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list +* nfsd_file_dispose_list_delayed - queue dead files for nfsd thread disposal * @dispose: list of nfsd_files to be disposed * -* Transfers each file to the "freeme" list for its nfsd_net, to eventually -* be disposed of by the per-net garbage collector. +* Transfers each file to the dispose list in its nfsd_net and wakes an nfsd +* thread to do the actual close. This keeps the cost of fput() in the nfsd +* threads rather than in the shrinker or GC worker. */ static void nfsd_file_dispose_list_delayed (struct list_head *dispose) { -while (!list_empty (dispose)) { +while (!list_empty (dispose)) {struct nfsd_file *nf =list_first_entry (dispose, struct nfsd_file, nf_gc);struct nfsd_net *nn =net_generic (nf->nf_net, nfsd_net_id); -struct nfsd_fcache_disposal *l = nn->fcache_disposal;struct svc_serv *serv; -spin_lock (&l->lock); -list_move_tail (&nf->nf_gc, &l->freeme); -spin_unlock (&l->lock); +spin_lock (&nn->fcache_dispose_lock); +list_move_tail (&nf->nf_gc, &nn->fcache_dispose_list); +spin_unlock (&nn->fcache_dispose_lock);/* * The filecache laundrette is shut down after the
@@ -464,17 +459,15 @@ nfsd_file_dispose_list_delayed(struct li*/ void nfsd_file_net_dispose (struct nfsd_net *nn) { -struct nfsd_fcache_disposal *l = nn->fcache_disposal; - -if (!list_empty (&l->freeme)) { +if (!list_empty (&nn->fcache_dispose_list)) {LIST_HEAD (dispose);int i; -spin_lock (&l->lock); -for (i =0 ; i <8 && !list_empty (&l->freeme); i++) -list_move (l->freeme.next, &dispose); -spin_unlock (&l->lock); -if (!list_empty (&l->freeme)) { +spin_lock (&nn->fcache_dispose_lock); +for (i =0 ; i <8 && !list_empty (&nn->fcache_dispose_list); i++) +list_move (nn->fcache_dispose_list.next, &dispose); +spin_unlock (&nn->fcache_dispose_lock); +if (!list_empty (&nn->fcache_dispose_list)) {/* * Wake up another thread to share the work * *before* doing any actual disposing.
@@ -698,11 +691,11 @@ nfsd_file_queue_for_close(struct inode * }/** -* nfsd_file_close_inode - attempt a delayed close of a nfsd_file +* nfsd_file_close_inode - attempt a deferred close of a nfsd_file * @inode: inode of the file to attempt to remove * * Close out any open nfsd_files that can be reaped for @inode. The -* actual freeing is deferred to the dispose_list_delayed infrastructure. +* actual freeing is deferred to the nfsd service threads. * * This is used by the fsnotify callbacks and setlease notifier. */
@@ -906,42 +899,14 @@ __nfsd_file_cache_purge(struct net *net)nfsd_file_dispose_list (&dispose); } -static struct nfsd_fcache_disposal * -nfsd_alloc_fcache_disposal (void ) -{ -struct nfsd_fcache_disposal *l; - - l =kmalloc (sizeof (*l),GFP_KERNEL ); -if (!l) -return NULL ; -spin_lock_init (&l->lock); -INIT_LIST_HEAD (&l->freeme); -return l; -} - -static void -nfsd_free_fcache_disposal (struct nfsd_fcache_disposal *l) -{ -nfsd_file_dispose_list (&l->freeme); -kfree (l); -} - -static void -nfsd_free_fcache_disposal_net (struct net *net) -{ -struct nfsd_net *nn =net_generic (net, nfsd_net_id); -struct nfsd_fcache_disposal *l = nn->fcache_disposal; - -nfsd_free_fcache_disposal (l); -} -int nfsd_file_cache_start_net (struct net *net) {struct nfsd_net *nn =net_generic (net, nfsd_net_id); - nn->fcache_disposal =nfsd_alloc_fcache_disposal (); -return nn->fcache_disposal ?0 : -ENOMEM ; +spin_lock_init (&nn->fcache_dispose_lock); +INIT_LIST_HEAD (&nn->fcache_dispose_list); +return 0 ; }/**
@@ -960,8 +925,10 @@ nfsd_file_cache_purge(struct net *net)void nfsd_file_cache_shutdown_net (struct net *net) { +struct nfsd_net *nn =net_generic (net, nfsd_net_id); +nfsd_file_cache_purge (net); -nfsd_free_fcache_disposal_net (net); +nfsd_file_dispose_list (&nn->fcache_dispose_list); }void
fs/nfsd/netns.h +2 -1
@@ -201,7 +201,8 @@ struct nfsd_net {/* utsname taken from the process that starts the server */ char nfsd_name[UNX_MAXNODENAME +1 ]; -struct nfsd_fcache_disposal *fcache_disposal; +spinlock_t fcache_dispose_lock; +struct list_head fcache_dispose_list;siphash_key_t siphash_key;
Greg Kroah-Hartman
maintainerPATCH 533/583 nfsd: fix UAF in async copy cancel and shutdown
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 62c0f6eaf050bb9284c1f9cac6ed1770092e6b95 ]
An async copy could be freed or used after free while a teardown caller (OFFLOAD_CANCEL, nfsd4_shutdown_copy, nfsd4_cancel_copy_by_sb) raced the copy kthread:
- find_async_copy() bumped copy->refcount but left the copy on clp->async_copies, so the reaper's cleanup_async_copy() could run release_copy_files() concurrently with a cancel/shutdown caller. Both put and NULL nf_src/nf_dst without a common lock, double-putting the nfsd_file and freeing it early.
- nfsd4_do_async_copy() set NFSD4_COPY_F_STOPPED before its final uses of the copy (nfsd_update_cmtime_attr() on copy->nf_dst, nfsd4_send_cb_offload()). nfsd4_stop_copy() treats a set STOPPED bit as "kthread done, skip kthread_stop()", so a teardown caller ran release_copy_files() -- which puts and NULLs nf_dst -- while the kthread still dereferenced it (NULL/UAF).
- copy->copy_task was never pinned. The one-shot kthread self-reaps on return, so kthread_stop()'s get_task_struct() could touch a freed task_struct.
- co_cb is embedded in the copy, but nfsd4_send_cb_offload() held a reference only on the client, so a concurrent teardown could free the copy while the CB_OFFLOAD callback was in flight.
Fix the teardown lifetime as a whole:
- find_async_copy() unlinks the copy (clear cp_clp, list_del_init) under async_lock; the cancel, shutdown, and sb-cancel paths drop the list-membership reference via nfs4_put_copy() after nfsd4_stop_copy(). Drop the now-redundant list_del fixup from cleanup_async_copy().
- Because unlinking hides the copy from the reaper, its cleanup_async_copy() can no longer remove the copy's s2s_cp_stateids entry; the cancel/shutdown/sb-cancel paths now call nfs4_free_copy_state() themselves (while cp_clp is still valid) so the entry does not dangle at freed memory for the laundromat and manage_cpntf_state() to dereference.
- Give the kthread its own reference, taken in nfsd4_copy() before wake_up_process() and dropped at the end of nfsd4_do_async_copy(); call wake_up_process() before list_add().
- Pin the task_struct with get_task_struct() in nfsd4_copy(), released in nfs4_put_copy(), so kthread_stop() is safe whenever the kthread exits. Set NFSD4_COPY_F_STOPPED only in nfsd4_stop_copy(), which now always kthread_stop()s before release_copy_files(); completion is still reported via NFSD4_COPY_F_COMPLETED, so nfsd4_has_active_async_copies() is unaffected. Each teardown caller removes the copy from clp->async_copies first, so kthread_stop() runs exactly once.
- Take a copy reference in nfsd4_send_cb_offload(), dropped in nfsd4_cb_offload_release(). The kthread still holds its own reference there, so the refcount_inc() cannot race the final free.
- Read cp_clp with smp_load_acquire() to pair with the unordered set_bit()/clear_bit() writers (Documentation/atomic_bitops.rst).
--- fs/nfsd/nfs4proc.c | 122 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 25 deletions(-)
fs/nfsd/nfs4proc.c +97 -25
@@ -1445,18 +1445,30 @@ static void nfs4_put_copy(struct nfsd4_c {if (!refcount_dec_and_test (©->refcount))return ; +/* Drop the task_struct pinned in nfsd4_copy(); NULL on sync copies. */ +if (copy->copy_task) +put_task_struct (copy->copy_task);kfree (copy->cp_src);kfree (copy); } +static void release_copy_files (struct nfsd4_copy *copy); +static void nfsd4_stop_copy (struct nfsd4_copy *copy) {trace_nfsd_copy_async_cancel (copy); -if (!test_and_set_bit (NFSD4_COPY_F_STOPPED , ©->cp_flags)) { -kthread_stop (copy->copy_task); - copy->nfserr = nfs_ok; -set_bit (NFSD4_COPY_F_COMPLETED , ©->cp_flags); - } +/* +* Join the kthread before releasing its resources. The task_struct is +* pinned in nfsd4_copy(), so kthread_stop() is safe even after the +* one-shot kthread has exited. The caller already unlinked the copy, +* so this runs once per copy. +*/ +set_bit (NFSD4_COPY_F_STOPPED , ©->cp_flags); +kthread_stop (copy->copy_task); + copy->nfserr = nfs_ok; +set_bit (NFSD4_COPY_F_COMPLETED , ©->cp_flags); + +release_copy_files (copy);nfs4_put_copy (copy); }
@@ -1469,7 +1481,13 @@ static struct nfsd4_copy *nfsd4_unhash_c copy = list_first_entry(&clp->async_copies, struct nfsd4_copy, copies);refcount_inc (©->refcount); - copy->cp_clp =NULL ; +/* +* Unlinking hides the copy from the reaper, so drop its +* s2s_cp_stateids entry here while cp_clp is still valid. +*/ +nfs4_free_copy_state (copy); +/* Pairs with smp_load_acquire() in nfsd4_send_cb_offload(). */ +smp_store_release (©->cp_clp,NULL );if (!list_empty (©->copies))list_del_init (©->copies); }
@@ -1481,8 +1499,11 @@ void nfsd4_shutdown_copy(struct nfs4_cli {struct nfsd4_copy *copy; -while ((copy =nfsd4_unhash_copy (clp)) !=NULL ) +while ((copy =nfsd4_unhash_copy (clp)) !=NULL ) {nfsd4_stop_copy (copy); +/* Reaper can't reach the unhashed copy; drop its membership ref. */ +nfs4_put_copy (copy); + } }#ifdef CONFIG_NFSD_V4_2_INTER_SSC
@@ -1773,6 +1794,8 @@ static void nfsd4_cb_offload_release(str container_of(cbo, struct nfsd4_copy, cp_cb_offload);set_bit (NFSD4_COPY_F_OFFLOAD_DONE , ©->cp_flags); +/* Drop the copy reference taken in nfsd4_send_cb_offload(). */ +nfs4_put_copy (copy); } static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
@@ -1900,34 +1923,52 @@ static void release_copy_files(struct nfnfsd_file_put (copy->nf_dst); } +/* +* Called from the reaper and from nfsd4_copy()'s error path; in both +* cases the copy is already unreachable from clp->async_copies. +*/ static void cleanup_async_copy (struct nfsd4_copy *copy) {nfs4_free_copy_state (copy);release_copy_files (copy); -if (copy->cp_clp) { -spin_lock (©->cp_clp->async_lock); -if (!list_empty (©->copies)) -list_del_init (©->copies); -spin_unlock (©->cp_clp->async_lock); - }nfs4_put_copy (copy); }static void nfsd4_send_cb_offload (struct nfsd4_copy *copy) {struct nfsd4_cb_offload *cbo = ©->cp_cb_offload; +struct nfs4_client *clp; + +/* +* Pairs with smp_store_release(&cp_clp) in find_async_copy() and +* nfsd4_unhash_copy(); the set_bit/clear_bit writers are unordered. +* cp_clp is NULL once the copy was canceled; skip the callback, the +* canceling path owns the notification. +*/ + clp =smp_load_acquire (©->cp_clp); +if (!clp) { +set_bit (NFSD4_COPY_F_OFFLOAD_DONE , ©->cp_flags); +return ; + }memcpy (&cbo->co_res, ©->cp_res,sizeof (copy->cp_res));memcpy (&cbo->co_fh, ©->fh,sizeof (copy->fh)); cbo->co_nfserr = copy->nfserr; cbo->co_retries =5 ; -nfsd4_init_cb (&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, +/* +* Hold the copy across the in-flight callback; co_cb is embedded in +* the copy, so it must outlive the callback. The reference is dropped +* in nfsd4_cb_offload_release(). +*/ +refcount_inc (©->refcount); + +nfsd4_init_cb (&cbo->co_cb, clp, &nfsd4_cb_offload_ops,NFSPROC4_CLNT_CB_OFFLOAD );nfsd41_cb_referring_call (&cbo->co_cb, &cbo->co_referring_sessionid, cbo->co_referring_slotid, cbo->co_referring_seqno); -trace_nfsd_cb_offload (copy->cp_clp, &cbo->co_res.cb_stateid, +trace_nfsd_cb_offload (clp, &cbo->co_res.cb_stateid, &cbo->co_fh, copy->cp_count, copy->nfserr);nfsd4_try_run_cb (&cbo->co_cb); }
@@ -1969,16 +2010,20 @@ static int nfsd4_do_async_copy(void *dat } do_callback: -/* The kthread exits forthwith. Ensure that a subsequent -* OFFLOAD_CANCEL won't try to kill it again. */ -set_bit (NFSD4_COPY_F_STOPPED , ©->cp_flags); - +/* +* Don't set NFSD4_COPY_F_STOPPED here: it tells a teardown caller it +* may skip kthread_stop(), which would then release nf_dst and the +* client while still in use. Only nfsd4_stop_copy() sets it, after +* joining. +*/ set_bit (NFSD4_COPY_F_COMPLETED , ©->cp_flags);trace_nfsd_copy_async_done (copy);atomic_dec (©->cp_nn->pending_async_copies);if (copy->cp_res.wr_bytes_written >0 && copy->attr_update)nfsd_update_cmtime_attr (copy->nf_dst->nf_file,0 );nfsd4_send_cb_offload (copy); +/* Drop the kthread's reference (taken in nfsd4_copy()); copy may be freed after this. */ +nfs4_put_copy (copy);return 0 ; }
@@ -2017,6 +2062,8 @@ nfsd4_copy(struct svc_rqst *rqstp, strucmemcpy (©->fh, &cstate->current_fh.fh_handle,sizeof (struct knfsd_fh ));if (nfsd4_copy_is_async (copy)) { +struct task_struct *task; + async_copy =kzalloc (sizeof (struct nfsd4_copy ),GFP_KERNEL );if (!async_copy) goto out_err;
@@ -2045,15 +2092,27 @@ nfsd4_copy(struct svc_rqst *rqstp, struc NFS4_MAX_SESSIONID_LEN); async_copy->cp_cb_offload.co_referring_slotid = cstate->slot->sl_index; async_copy->cp_cb_offload.co_referring_seqno = cstate->slot->sl_seqid; - async_copy->copy_task =kthread_create (nfsd4_do_async_copy, - async_copy,"%s" ,"copy thread" ); -if (IS_ERR (async_copy->copy_task)) + task =kthread_create (nfsd4_do_async_copy, async_copy, +"%s" ,"copy thread" ); +if (IS_ERR (task)) goto out_dec_async_copy_err; +/* +* Pin the task_struct so kthread_stop() is safe after this +* one-shot kthread exits. Released by nfs4_put_copy(). +*/ +get_task_struct (task); + async_copy->copy_task = task; +/* +* Take the kthread's ref and wake it before publishing, so the +* publisher touches async_copy no further and teardown can +* drain it. +*/ +refcount_inc (&async_copy->refcount); +wake_up_process (async_copy->copy_task);spin_lock (&async_copy->cp_clp->async_lock);list_add (&async_copy->copies, &async_copy->cp_clp->async_copies);spin_unlock (&async_copy->cp_clp->async_lock); -wake_up_process (async_copy->copy_task); status = nfs_ok; }else { status = nfsd4_do_copy(copy, copy->nf_src->nf_file,
@@ -2107,8 +2166,18 @@ find_async_copy(struct nfs4_client *clp,spin_lock (&clp->async_lock); copy =find_async_copy_locked (clp, stateid); -if (copy) +if (copy) {refcount_inc (©->refcount); +nfs4_free_copy_state (copy); +/* +* Mirror nfsd4_unhash_copy(): unlink and clear cp_clp under +* async_lock so the reaper can't reach it. Caller drops the +* membership ref after nfsd4_stop_copy(). +*/ +smp_store_release (©->cp_clp,NULL ); +if (!list_empty (©->copies)) +list_del_init (©->copies); + }spin_unlock (&clp->async_lock);return copy; }
@@ -2127,8 +2196,11 @@ nfsd4_offload_cancel(struct svc_rqst *rqstruct nfsd_net *nn =net_generic (SVC_NET (rqstp), nfsd_net_id);return manage_cpntf_state (nn, &os->stateid, clp,NULL ); - }else + }else {nfsd4_stop_copy (copy); +/* find_async_copy() unlinked it from the reaper; drop the membership ref. */ +nfs4_put_copy (copy); + }return nfs_ok; }
Greg Kroah-Hartman
maintainerPATCH 534/583 nfsd: close shrinker/GC/fsnotify vs per-net shutdown race in filecache
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 40162cfea79b9510380decfdd1795b754dc9f972 ]
The shrinker, GC worker, and fsnotify/lease callbacks can unhash an nfsd_file from the rhashtable and then call nfsd_file_dispose_list_delayed() to move it to the per-net dispose list. If nfsd_file_cache_shutdown_net() runs concurrently, its rhashtable walk misses the already-unhashed file, and its drain of the per-net dispose list can run before the file has been queued. The file then sits on the per-net list with no thread to drain it, leaking both the file and its associated state.
The GC worker and shrinker already hold nfsd_gc_lock while walking the LRU, but in the original code they release it before calling nfsd_file_dispose_list_delayed(). The fsnotify/lease path (nfsd_file_close_inode) has no synchronization at all.
Fix this by:
1. Widening nfsd_gc_lock in both nfsd_file_gc() and nfsd_file_lru_scan() to cover the nfsd_file_dispose_list_delayed() call.
2. Wrapping nfsd_file_close_inode() in nfsd_gc_lock so that all three callers of nfsd_file_dispose_list_delayed() hold the lock.
3. Adding a spin_lock/unlock(nfsd_gc_lock) barrier in nfsd_file_cache_shutdown_net() after the purge, so that any in-progress disposal has fully completed before the per-net list is drained.
All operations inside the lock are non-sleeping (rhashtable lookups, atomic bit/refcount ops, list moves, svc_wake_up), so the spinlock is appropriate.
--- fs/nfsd/filecache.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-)
fs/nfsd/filecache.c +29 -9
@@ -55,6 +55,17 @@/* We only care about NFSD_MAY_READ/WRITE for this cache */ #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO) +/* If the shrinker runs between calls to list_lru_walk_node() in +* nfsd_file_gc(), the "remaining" count will be wrong. This could +* result in premature freeing of some files. This may not matter much +* but is easy to fix with this spinlock which temporarily disables +* the shrinker. +* +* It also serializes callers of nfsd_file_dispose_list_delayed() +* against per-net shutdown. +*/ +static DEFINE_SPINLOCK (nfsd_gc_lock); + static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits); static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions); static DEFINE_PER_CPU(unsigned long, nfsd_file_allocations);
@@ -423,10 +434,16 @@ nfsd_file_dispose_list(struct list_head* Transfers each file to the dispose list in its nfsd_net and wakes an nfsd * thread to do the actual close. This keeps the cost of fput() in the nfsd * threads rather than in the shrinker or GC worker. +* +* All callers must hold nfsd_gc_lock, so that nfsd_file_cache_shutdown_net() +* can synchronize against them before draining the per-net dispose list. +* This guarantees nf_net is still live when we call net_generic(). */ static void nfsd_file_dispose_list_delayed (struct list_head *dispose) { +lockdep_assert_held (&nfsd_gc_lock); +while (!list_empty (dispose)) {struct nfsd_file *nf =list_first_entry (dispose, struct nfsd_file, nf_gc);
@@ -557,13 +574,6 @@ nfsd_file_gc_cb(struct list_head *item,return nfsd_file_lru_cb (item, lru, arg); } -/* If the shrinker runs between calls to list_lru_walk_node() in -* nfsd_file_gc(), the "remaining" count will be wrong. This could -* result in premature freeing of some files. This may not matter much -* but is easy to fix with this spinlock which temporarily disables -* the shrinker. -*/ -static DEFINE_SPINLOCK (nfsd_gc_lock);static void nfsd_file_gc (void ) {
@@ -586,9 +596,9 @@ nfsd_file_gc(void) remaining =0 ; } } +nfsd_file_dispose_list_delayed (&dispose);spin_unlock (&nfsd_gc_lock);trace_nfsd_file_gc_removed (ret,list_lru_count (&nfsd_file_lru)); -nfsd_file_dispose_list_delayed (&dispose); } static void
@@ -616,9 +626,9 @@ nfsd_file_lru_scan(struct shrinker *s, s ret =list_lru_shrink_walk (&nfsd_file_lru, sc, nfsd_file_lru_cb, &dispose); +nfsd_file_dispose_list_delayed (&dispose);spin_unlock (&nfsd_gc_lock);trace_nfsd_file_shrinker_removed (ret,list_lru_count (&nfsd_file_lru)); -nfsd_file_dispose_list_delayed (&dispose);return ret; }
@@ -704,8 +714,10 @@ nfsd_file_close_inode(struct inode *inod {LIST_HEAD (dispose); +spin_lock (&nfsd_gc_lock);nfsd_file_queue_for_close (inode, &dispose);nfsd_file_dispose_list_delayed (&dispose); +spin_unlock (&nfsd_gc_lock); }/**
@@ -928,6 +940,14 @@ nfsd_file_cache_shutdown_net(struct netstruct nfsd_net *nn =net_generic (net, nfsd_net_id);nfsd_file_cache_purge (net); +/* +* Ensure any in-progress shrinker, GC, or fsnotify/lease callback +* (all of which hold nfsd_gc_lock while calling +* nfsd_file_dispose_list_delayed()) has fully completed before +* draining the per-net dispose list. +*/ +spin_lock (&nfsd_gc_lock); +spin_unlock (&nfsd_gc_lock);nfsd_file_dispose_list (&nn->fcache_dispose_list); }
Greg Kroah-Hartman
maintainerPATCH 535/583 nfsd: convert global state_lock to per-net deleg_lock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 8be12e0cf21110f1e0b7fd21711ff13fb75bee72 ]
Replace the global state_lock spinlock with a per-nfsd_net deleg_lock. The state_lock was only used to protect delegation lifecycle operations (the del_recall_lru list and delegation hash/unhash), all of which are scoped to a single network namespace. Making the lock per-net removes a source of unnecessary contention between containers.
Stable-dep-of: 4683ca76b3b7 ("NFSD: Prevent client use-after-free during delegation revoke")
--- fs/nfsd/netns.h | 3 ++ fs/nfsd/nfs4state.c | 53 ++++++++++++++++++++++++++-------------------------- fs/nfsd/state.h | 2 - 3 files changed, 31 insertions(+), 27 deletions(-)
fs/nfsd/netns.h +3 -0
@@ -101,6 +101,9 @@ struct nfsd_net {*/ struct list_head client_lru;struct list_head close_lru; + +/* protects del_recall_lru and delegation hash/unhash */ +spinlock_t deleg_lock____cacheline_aligned ;struct list_head del_recall_lru;/* protected by blocked_locks_lock */
fs/nfsd/nfs4state.c +27 -26
176 lines not on this page · raw message
fs/nfsd/state.h +1 -1
@@ -123,7 +123,7 @@ struct nfs4_stid {#define SC_TYPE_LAYOUT BIT(3)unsigned short sc_type; -/* state_lock protects sc_status for delegation stateids. +/* nn->deleg_lock protects sc_status for delegation stateids. * ->cl_lock protects sc_status for open and lock stateids. * ->st_mutex also protect sc_status for open stateids. * ->ls_lock protects sc_status for layout stateids.
Greg Kroah-Hartman
maintainerPATCH 536/583 NFSD: Prevent client use-after-free during delegation revoke
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 4683ca76b3b7e5808338491c6eb3c20e6b4894d5 ]
A delegation stateid holds only a bare pointer to its owning nfs4_client and does not keep it alive. The client survives its stateids only because __destroy_client() drains cl_delegations and cl_revoked before free_client() runs.
nfs4_laundromat() breaks that invariant: it unhashes an expired delegation from cl_delegations, drops deleg_lock, then revoke_delegation() relinks it onto cl_revoked under cl_lock. In that window the delegation is on neither list, so client_has_state() can report no remaining state.
Every teardown path first requires cl_rpc_users to be zero, but the laundromat holds no such reference. A client whose recalled delegation has just timed out can therefore reach free_client() while revoke_delegation() is still about to dereference cl_lock, a use-after-free.
Pin the client with cl_rpc_users across the revoke so teardown blocks until it completes, then reap the delegation from cl_revoked. A client already expiring reaps its own, so skip it and leave the delegation on del_recall_lru.
--- fs/nfsd/netns.h | 6 ++++-- fs/nfsd/nfs4state.c | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-)
fs/nfsd/netns.h +4 -2
@@ -102,7 +102,8 @@ struct nfsd_net {struct list_head client_lru;struct list_head close_lru; -/* protects del_recall_lru and delegation hash/unhash */ +/* protects del_recall_lru and delegation hash/unhash; +* nests outside client_lock */ spinlock_t deleg_lock____cacheline_aligned ;struct list_head del_recall_lru;
@@ -111,7 +112,8 @@ struct nfsd_net {struct delayed_work laundromat_work; -/* client_lock protects the client lru list and session hash table */ +/* client_lock protects the client lru list and session hash +* table; nests inside deleg_lock */ spinlock_t client_lock;/* protects blocked_locks_lru */
fs/nfsd/nfs4state.c +23 -0
@@ -7052,6 +7052,7 @@ nfs4_laundromat(struct nfsd_net *nn) .new_timeo = nn->nfsd4_lease };struct nfs4_cpntf_state *cps; +struct nfs4_client *clp;copy_stateid_t *cps_t;int i;
@@ -7080,6 +7081,18 @@ nfs4_laundromat(struct nfsd_net *nn) dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);if (!state_expired (<, dp->dl_time))break ; + clp = dp->dl_stid.sc_client; +spin_lock (&nn->client_lock); +if (is_client_expired (clp)) { +spin_unlock (&nn->client_lock); +continue ; + } +/* +* Pin without reviving: get_client_locked() would +* flip a courtesy client back to NFSD4_ACTIVE. +*/ +atomic_inc (&clp->cl_rpc_users); +spin_unlock (&nn->client_lock);refcount_inc (&dp->dl_stid.sc_count);unhash_delegation_locked (dp,SC_STATUS_REVOKED );list_add (&dp->dl_recall_lru, &reaplist);
@@ -7088,8 +7101,18 @@ nfs4_laundromat(struct nfsd_net *nn)while (!list_empty (&reaplist)) { dp = list_first_entry(&reaplist, struct nfs4_delegation, dl_recall_lru); + clp = dp->dl_stid.sc_client;list_del_init (&dp->dl_recall_lru);revoke_delegation (dp); +/* +* Unpin without renewing: put_client_renew() would +* renew the reaped client's lease. +*/ +if (atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock)) { +if (is_client_expired (clp)) +wake_up_all (&expiry_wq); +spin_unlock (&nn->client_lock); + } }spin_lock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 537/583 NFSD: Prevent client use-after-free during admin state revocation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit e270e5a0778e5bff852c8862ce9576ce70359393 ]
A stateid holds only a bare pointer to its nfs4_client; a stateid reference does not pin it. The client survives only because __destroy_client() drains its stateids before free_client() runs.
nfsd4_revoke_states() drops nn->client_lock across revoke_one_stid(), which dereferences the client to revoke a stateid and read clp->cl_minorversion. A teardown racing the dropped lock can free the client first.
Pinning cl_rpc_users under client_lock blocks the DESTROY_CLIENTID and EXCHANGE_ID teardown, which refuses while cl_rpc_users is non-zero. force_expire_client() ignores it: once its wait for cl_rpc_users to reach zero has passed, a later pin goes unnoticed.
Under client_lock, skip a client whose cl_time is already zero -- force_expire_client() clears it there before waiting -- otherwise pin cl_rpc_users before dropping the lock. The walk then either sees the expiry and skips, or pins in time for that wait to cover the revoke.
--- fs/nfsd/nfs4state.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +20 -2
@@ -1841,13 +1841,23 @@ void nfsd4_revoke_states(struct nfsd_netstruct nfs4_client *clp; retry:list_for_each_entry (clp, head, cl_idhash) { -struct nfs4_stid *stid =find_one_sb_stid (clp, sb, - sc_types); +struct nfs4_stid *stid; + +/* +* force_expire_client() ignores cl_rpc_users once +* its wait_event() has passed, so pinning cannot +* keep an already-expiring client alive; the +* expiry path revokes its states instead. +*/ +if (is_client_expired (clp)) +continue ; + stid =find_one_sb_stid (clp, sb, sc_types);if (stid) {struct nfs4_ol_stateid *stp;struct nfs4_delegation *dp;struct nfs4_layout_stateid *ls; +atomic_inc (&clp->cl_rpc_users);spin_unlock (&nn->client_lock);switch (stid->sc_type) {case SC_TYPE_OPEN :
@@ -1931,6 +1941,9 @@ void nfsd4_revoke_states(struct nfsd_net*/ nn->nfs40_last_revoke =ktime_get_boottime_seconds (); +if (atomic_dec_and_test (&clp->cl_rpc_users) && +is_client_expired (clp)) +wake_up_all (&expiry_wq); goto retry; } }
@@ -3217,6 +3230,11 @@ static void force_expire_client(struct ntrace_nfsd_clid_admin_expired (&clp->cl_clientid); +/* +* cl_time is cleared under client_lock before the wait so a +* revocation walk pinning cl_rpc_users under it either skips +* this client or is seen by this wait_event(). +*/ spin_lock (&nn->client_lock); clp->cl_time =0 ;spin_unlock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 538/583 cifs: Scripted clean up fs/smb/client/cifs_unicode.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 696ca7d95658224ba807813101d0d5714d1d8aa4 ]
Remove externs, correct argument names and reformat declarations.
For the stable backport, drop the original header cleanup because the stable-only __KERNEL__ guard makes it conflict and the target does not touch or depend on those declarations. Instead, carry only a behavior-preserving local snapshot of mnt_cifs_flags in cifs_setattr_nounix(); the target patch refers to that snapshot in its new setuid/setgid condition.
[ sashal: Reduced backport -- upstream 696ca7d956582 touches 1 file(s), this
backport carries 1. Not backported here:
fs/smb/client/cifs_unicode.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: b8e5dc4f95e5 ("smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions")
--- fs/smb/client/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/smb/client/inode.c +2 -1
@@ -3307,6 +3307,7 @@ cifs_setattr_nounix(struct dentry *direnkgid_t gid =INVALID_GID ;struct inode *inode =d_inode (direntry);struct cifs_sb_info *cifs_sb =CIFS_SB (inode->i_sb); +unsigned int sbflags = cifs_sb->mnt_cifs_flags;struct cifsInodeInfo *cifsInode =CIFS_I (inode);struct cifsFileInfo *cfile =NULL ;const char *full_path;
@@ -3321,7 +3322,7 @@ cifs_setattr_nounix(struct dentry *direncifs_dbg (FYI ,"setattr on file %pd attrs->ia_valid 0x%x\n" , direntry, attrs->ia_valid); -if (cifs_sb->mnt_cifs_flags &CIFS_MOUNT_NO_PERM ) +if (sbflags &CIFS_MOUNT_NO_PERM ) attrs->ia_valid |=ATTR_FORCE ; rc =setattr_prepare (&nop_mnt_idmap, direntry, attrs);
Greg Kroah-Hartman
maintainerPATCH 539/583 cifs: Scripted clean up fs/smb/client/fscache.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit bc3de356aae666de5d8b5131545be87cf7754431 ]
Remove externs, correct argument names and reformat declarations.
Stable-dep-of: b8e5dc4f95e5 ("smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions")
--- fs/smb/client/fscache.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
fs/smb/client/fscache.h +5 -5
@@ -38,12 +38,12 @@ struct cifs_fscache_inode_coherency_data/* * fscache.c */ -extern int cifs_fscache_get_super_cookie (struct cifs_tcon *); -extern void cifs_fscache_release_super_cookie (struct cifs_tcon *); +int cifs_fscache_get_super_cookie (struct cifs_tcon *tcon); +void cifs_fscache_release_super_cookie (struct cifs_tcon *tcon); -extern void cifs_fscache_get_inode_cookie (struct inode *inode); -extern void cifs_fscache_release_inode_cookie (struct inode *); -extern void cifs_fscache_unuse_inode_cookie (struct inode *inode,bool update); +void cifs_fscache_get_inode_cookie (struct inode *inode); +void cifs_fscache_release_inode_cookie (struct inode *inode); +void cifs_fscache_unuse_inode_cookie (struct inode *inode,bool update); static inline void cifs_fscache_fill_coherency(struct inode *inode,
Greg Kroah-Hartman
maintainerPATCH 540/583 cifs: Scripted clean up fs/smb/client/fs_context.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 047e504766a357ab751f76ccdec7a2e824e75e6d ]
Remove externs, correct argument names and reformat declarations.
Stable-dep-of: b8e5dc4f95e5 ("smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions")
--- fs/smb/client/fs_context.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
fs/smb/client/fs_context.h +9 -7
@@ -359,18 +359,20 @@ static inline enum cifs_symlink_type cifreturn CIFS_SYMLINK_TYPE_NONE ; } -extern int smb3_init_fs_context (struct fs_context *fc); -extern void smb3_cleanup_fs_context_contents (struct smb3_fs_context *ctx); -extern void smb3_cleanup_fs_context (struct smb3_fs_context *ctx); +int smb3_init_fs_context (struct fs_context *fc); +void smb3_cleanup_fs_context_contents (struct smb3_fs_context *ctx); +void smb3_cleanup_fs_context (struct smb3_fs_context *ctx);static inline struct smb3_fs_context *smb3_fc2context (const struct fs_context *fc) {return fc->fs_private; } -extern int smb3_fs_context_dup (struct smb3_fs_context *new_ctx,struct smb3_fs_context *ctx); -extern int smb3_sync_session_ctx_passwords (struct cifs_sb_info *cifs_sb,struct cifs_ses *ses); -extern void smb3_update_mnt_flags (struct cifs_sb_info *cifs_sb); +int smb3_fs_context_dup (struct smb3_fs_context *new_ctx, +struct smb3_fs_context *ctx); +int smb3_sync_session_ctx_passwords (struct cifs_sb_info *cifs_sb, +struct cifs_ses *ses); +void smb3_update_mnt_flags (struct cifs_sb_info *cifs_sb);/* * max deferred close timeout (jiffies) - 2^30
@@ -378,7 +380,7 @@ extern void smb3_update_mnt_flags(struct#define SMB3_MAX_DCLOSETIMEO (1 << 30)#define SMB3_DEF_DCLOSETIMEO (1 * HZ)/* even 1 sec enough to help eg open/write/close/open/read */ #define MAX_CACHED_FIDS 16 -extern char *cifs_sanitize_prepath (char *prepath,gfp_t gfp); +char *cifs_sanitize_prepath (char *prepath,gfp_t gfp);extern struct mutex cifs_mount_mutex;
Greg Kroah-Hartman
maintainerPATCH 541/583 cifs: Scripted clean up fs/smb/client/smb2proto.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 1e009e3346db230787685a3989fd9c346fb412fb ]
Remove externs, correct argument names and reformat declarations.
For the stable backport, drop the original smb2proto.h cleanup because the stable header has gained and changed declarations that make the scripted rewrite conflict, while the target does not touch or depend on the header. Instead, carry only behavior-preserving context preparation in inode.c: use the type-safe kmalloc_obj() spelling and the existing local sbflags snapshot, and brace the adjacent else-if. This allows both target hunks to apply cleanly without pre-applying the target's behavioral change.
[ sashal: Reduced backport -- upstream 1e009e3346db touches 1 file(s), this
backport carries 1. Not backported here:
fs/smb/client/smb2proto.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
[ sashal: Reduced backport -- upstream 1e009e3346db2 touches 1 file(s), this
backport carries 1. Not backported here:
fs/smb/client/smb2proto.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: b8e5dc4f95e5 ("smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions")
--- fs/smb/client/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/smb/client/inode.c +3 -3
@@ -3202,7 +3202,7 @@ cifs_setattr_unix(struct dentry *direntrif (attrs->ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID )) attrs->ia_valid &= ~ATTR_MODE ; - args =kmalloc (sizeof (*args),GFP_KERNEL ); + args =kmalloc_obj (*args);if (args ==NULL ) { rc = -ENOMEM ; goto out;
@@ -3395,9 +3395,9 @@ cifs_setattr_nounix(struct dentry *diren goto cifs_setattr_exit; } } - }else -if (!(cifs_sb->mnt_cifs_flags &CIFS_MOUNT_SET_UID )) + }else if (!(sbflags &CIFS_MOUNT_SET_UID )) { attrs->ia_valid &= ~(ATTR_UID |ATTR_GID ); + }/* skip mode change if it's just for clearing setuid/setgid */ if (attrs->ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID ))
Greg Kroah-Hartman
maintainerPATCH 542/583 smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
[ Upstream commit b8e5dc4f95e5484159b343903f302eb6d783f2e6 ]
When a file has the setuid or setgid bit set and is written to, the VFS strips those bits and issues a setattr with ATTR_KILL_SUID/ATTR_KILL_SGID together with an ATTR_MODE carrying the already-cleared mode.
Both cifs_setattr_unix() and cifs_setattr_nounix() unconditionally dropped ATTR_MODE in that case:
/* skip mode change if it's just for clearing setuid/setgid */ if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) attrs->ia_valid &= ~ATTR_MODE;
This is fine for the default mount, where the mode is only emulated via the DOS read-only attribute and cannot represent the setuid/setgid bits anyway. However, with the "cifsacl" or "modefromsid" mount options the mode is stored on the server through an ACL (id_mode_to_cifs_acl()), with the SMB3.1.1 POSIX extensions the mode is sent to the server directly, and with the SMB1 Unix extensions (cifs_setattr_unix) the mode is sent via CIFSSMBUnixSetPathInfo(). In all those cases dropping ATTR_MODE means the cleared mode is never pushed to the server, so the setuid/setgid bit survives the write.
This is a security issue: on local filesystems the setuid bit is stripped when a file is written, but over these cifs.ko mounts the bit persists on the server, potentially allowing an unexpected privilege escalation on subsequent execution.
Fix this in two places:
1. cifs_setattr_nounix(): only take the "skip mode change" shortcut when the mode is emulated via the DOS read-only attribute (i.e. neither cifsacl/modefromsid nor the SMB3.1.1 POSIX extensions are in effect), so that the cleared mode is propagated to the server in the ACL / POSIX cases.
2. cifs_setattr_unix(): this function is only called when Unix extensions are in effect, so the mode is always stored on the server. Remove the shortcut entirely so that the cleared mode is always pushed.
--- fs/smb/client/inode.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)
fs/smb/client/inode.c +24 -5
@@ -3198,9 +3198,13 @@ cifs_setattr_unix(struct dentry *direntr attrs->ia_valid &= ~(ATTR_CTIME |ATTR_MTIME ); } -/* skip mode change if it's just for clearing setuid/setgid */ -if (attrs->ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID )) - attrs->ia_valid &= ~ATTR_MODE ; +/* +* This function is only called when Unix extensions are in effect, +* so the mode is always sent to and stored on the server. Do not +* skip the mode change when clearing setuid/setgid bits: dropping +* ATTR_MODE here would leave those bits set on the server after a +* write, which is a security issue. +*/ args =kmalloc_obj (*args);if (args ==NULL ) {
@@ -3399,8 +3403,23 @@ cifs_setattr_nounix(struct dentry *diren attrs->ia_valid &= ~(ATTR_UID |ATTR_GID ); } -/* skip mode change if it's just for clearing setuid/setgid */ -if (attrs->ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID )) +/* +* Skip the mode change if it is only being done to clear the +* setuid/setgid bits *and* the mode is emulated via the DOS +* read-only attribute (the default, non-ACL case), which cannot +* represent the setuid/setgid bits anyway. +* +* When the mode is instead stored on the server - i.e. with the +* cifsacl or modefromsid mount options (via an ACL) or with the +* SMB3.1.1 POSIX extensions - the cleared mode must be pushed to +* the server. Dropping ATTR_MODE here would leave the setuid/ +* setgid bit set on the server after a write, which is a security +* issue (the bits are not stripped as they are on local +* filesystems). +*/ +if ((attrs->ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID )) && + !((sbflags & (CIFS_MOUNT_CIFS_ACL |CIFS_MOUNT_MODE_FROM_SID )) || +cifs_sb_master_tcon (cifs_sb)->posix_extensions)) attrs->ia_valid &= ~ATTR_MODE ;if (attrs->ia_valid &ATTR_MODE ) {
Greg Kroah-Hartman
maintainerPATCH 543/583 cifs: Remove dead function prototypes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 9146c7e53fa8e5024e0f61d67129afd45a179a3b ]
Remove a bunch of dead function prototypes.
Stable-dep-of: 730d0bb19507 ("smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2")
--- fs/smb/client/cifsproto.h | 6 ------ fs/smb/client/smb2proto.h | 12 ------------ 2 files changed, 18 deletions(-)
fs/smb/client/cifsproto.h +0 -6
@@ -82,7 +82,6 @@ extern char *cifs_build_path_to_root(strstruct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, int add_treename); -extern char *build_wildcard_path_from_dentry (struct dentry *direntry);char *cifs_build_devname (char *nodename,const char *prepath);extern void delete_mid (struct mid_q_entry *mid);void __release_mid (struct kref *refcount);
@@ -166,8 +165,6 @@ extern bool is_valid_oplock_break(char *extern bool backup_cred (struct cifs_sb_info *);extern bool is_size_safe_to_change (struct cifsInodeInfo *cifsInode,__u64 eof,bool from_readdir); -extern void cifs_update_eof (struct cifsInodeInfo *cifsi,loff_t offset, -unsigned int bytes_written);void cifs_write_subrequest_terminated (struct cifs_io_subrequest *wdata,ssize_t result);extern struct cifsFileInfo *find_writable_file (struct cifsInodeInfo *,int ); extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
@@ -194,8 +191,6 @@ extern void header_assemble(struct smb_hextern int small_smb_init_no_tc (const int smb_cmd,const int wct,struct cifs_ses *ses,void **request_buf); -extern enum securityEnum select_sectype (struct TCP_Server_Info *server, -enum securityEnum requested);extern int CIFS_SessSetup (const unsigned int xid,struct cifs_ses *ses,struct TCP_Server_Info *server,const struct nls_table *nls_cp);
@@ -651,7 +646,6 @@ void cifs_free_hash(struct shash_desc **int cifs_try_adding_channels (struct cifs_ses *ses);bool is_ses_using_iface (struct cifs_ses *ses,struct cifs_server_iface *iface); -void cifs_ses_mark_for_reconnect (struct cifs_ses *ses); int cifs_ses_get_chan_index(struct cifs_ses *ses,
fs/smb/client/smb2proto.h +0 -12
@@ -39,15 +39,11 @@ extern struct mid_q_entry *smb2_setup_as struct TCP_Server_Info *server, struct smb_rqst *rqst);extern struct cifs_tcon *smb2_find_smb_tcon (struct TCP_Server_Info *server,__u64 ses_id,__u32 tid); -extern void smb2_echo_request (struct work_struct *work);extern __le32 smb2_get_lease_state (struct cifsInodeInfo *cinode);extern bool smb2_is_valid_oplock_break (char *buffer,struct TCP_Server_Info *srv);extern int smb3_handle_read_data (struct TCP_Server_Info *server,struct mid_q_entry *mid); -extern int smb2_query_reparse_tag (const unsigned int xid,struct cifs_tcon *tcon, -struct cifs_sb_info *cifs_sb,const char *path, -__u32 *reparse_tag); struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data, struct super_block *sb, const unsigned int xid,
@@ -300,17 +296,9 @@ extern int smb2_query_info_compound(cons struct kvec *rsp, int *buftype, struct cifs_sb_info *cifs_sb);/* query path info from the server using SMB311 POSIX extensions*/ -int smb311_posix_query_path_info (const unsigned int xid, -struct cifs_tcon *tcon, -struct cifs_sb_info *cifs_sb, -const char *full_path, -struct cifs_open_info_data *data);int posix_info_parse (const void *beg,const void *end,struct smb2_posix_info_parsed *out);int posix_info_sid_size (const void *beg,const void *end); -int smb2_make_nfs_node (unsigned int xid,struct inode *inode, -struct dentry *dentry,struct cifs_tcon *tcon, -const char *full_path,umode_t mode,dev_t dev);int smb2_rename_pending_delete (const char *full_path,struct dentry *dentry,const unsigned int xid);
Greg Kroah-Hartman
maintainerPATCH 544/583 cifs: SMB1 split: Create smb1proto.h for SMB1 declarations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 86c666506ea2c42649879eeac7f29e7bedef2f23 ]
Stable-dep-of: 730d0bb19507 ("smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2")
--- fs/smb/client/cifsglob.h | 2 -- fs/smb/client/cifsproto.h | 11 +---------- fs/smb/client/smb1proto.h | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 fs/smb/client/smb1proto.h
fs/smb/client/cifsglob.h +0 -2
@@ -2110,8 +2110,6 @@ extern mempool_t cifs_io_subrequest_pool/* Operations for different SMB versions */ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern struct smb_version_operations smb1_operations; -extern struct smb_version_values smb1_values;extern struct smb_version_operations smb20_operations;extern struct smb_version_values smb20_values;#endif /* CIFS_ALLOW_INSECURE_LEGACY */
fs/smb/client/cifsproto.h +1 -10
@@ -14,6 +14,7 @@#ifdef CONFIG_CIFS_DFS_UPCALL #include "dfs_cache.h" #endif +#include "smb1proto.h" struct statfs ;struct smb_rqst ;
@@ -430,16 +431,6 @@ extern int CIFSSMBSetFileSize(const unsi struct cifsFileInfo *cfile, __u64 size, bool set_allocation); -struct cifs_unix_set_info_args { -__u64 ctime; -__u64 atime; -__u64 mtime; -__u64 mode; -kuid_t uid; -kgid_t gid; -dev_t device; -}; - extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, const struct cifs_unix_set_info_args *args,
fs/smb/client/smb1proto.h new file +38 -0
@@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* +* +* Copyright (c) International Business Machines Corp., 2002,2008 +* Author(s): Steve French ([email protected]) +* +*/ +#ifndef _SMB1PROTO_H +#define _SMB1PROTO_H + +struct cifs_unix_set_info_args { +__u64 ctime; +__u64 atime; +__u64 mtime; +__u64 mode; +kuid_t uid; +kgid_t gid; +dev_t device; +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + +/* +* cifssmb.c +*/ + +/* +* smb1ops.c +*/ +extern struct smb_version_operations smb1_operations; +extern struct smb_version_values smb1_values; + +/* +* smb1transport.c +*/ + +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +#endif /* _SMB1PROTO_H */
Greg Kroah-Hartman
maintainerPATCH 545/583 cifs: SMB1 split: Separate out SMB1 decls into smb1proto.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 645427b7a6c59e0074df29bf939aec2e9d6f2819 ]
Separate out SMB1 declarations scriptedly into smb1proto.h. Script below:
#!/usr/bin/perl -w
use strict;
unless (@ARGV) {
die "Usage: $0 <c_file1> [<c_file2> ...]\n";
}
# Data tracking
my %funcs = (); # Func name => { func prototype }
my %headers = (); # Header filename => { header content }
my %c_files = (); # C filename => { ordered func list, header pref }
my %cmarkers = (); # C filename marker => { header filename it's in }
# Parse state
my $pathname = "-";
my $lineno = 0;
sub error(@) {
print STDERR $pathname, ":", $lineno, ": ", @_, "\n";
exit(1);
}
sub pad($) {
# Reindent the function arguments to line the arguments up with the char
# after the opening bracket on the func argument list
my ($lines) = @_;
return $lines if ($#{$lines} <= 0);
my $has_empty = 0;
for (my $i = 0; $i <= $#{$lines}; $i++) {
$lines->[$i] =~ s/^[ \t]+//;
$has_empty = 1 if ($lines->[$i] eq "");
}
if ($has_empty) {
my @clean = grep /.+/, @{$lines};
$lines = \@clean;
}
my $indlen = index($lines->[0], "(");
return $lines if ($indlen < 0);
my $indent = "";
$indlen++;
$indent .= "\t" x ($indlen / 8);
$indent .= " " x ($indlen % 8);
my @padded = ();
my $acc = "";
my $len = -$indlen;
for (my $i = 0; $i <= $#{$lines}; $i++) {
my $argument = $lines->[$i];
my $arglen = length($argument);
my $last = ($i == $#{$lines} ? 1 : 0);
if ($i == 0 ||
$i == 1) {
$acc .= $argument;
$acc .= ";" if ($last);
$len += $arglen + $last;
next;
}
if (!$acc) {
$acc = $indent . $argument;
$acc .= ";" if ($last);
$len += $arglen + $last;
next;
}
if ($indlen + $len + 1 + $arglen + $last > 79) {
push @padded, $acc;
$acc = $indent . $argument;
$acc .= ";" if ($last);
$len = $arglen + $last;
next;
}
$acc .= " " . $argument;
$acc .= ";" if ($last);
$len += 1 + $arglen + $last;
}
push @padded, $acc if ($acc);
return \@padded;
}
sub earliest(@) {
my $ret = -1;
foreach (@_) {
$ret = $_ if ($ret < 0 || ($_ >= 0 && $_ < $ret));
}
return $ret;
}
foreach my $file (@ARGV) {
# Open the file for reading.
next if $file =~ /trace[.]h$/;
next if $file =~ /smbdirect[.][ch]$/;
open my $fh, "<$file"
or die "Could not open file '$file'";
$pathname = $file;
$lineno = 0;
my $filename;
my @file_content = ();
my @copy = ();
my $state = 0;
my $qual = "";
my $type = "";
my $funcname = "";
my @funcdef = ();
my $bracket = 0;
my $comment = 0;
my $smb1 = 0;
my $header = 0;
my $inline = 0;
my $file_marker = "";
my $config = "";
my $c_file = 0;
$filename = $pathname;
$filename =~ s!.*/!!;
if ($file =~ m!.h$!) {
my %new_h_file = (
path => $pathname,
fname => $filename,
content => [],
);
$header = \%new_h_file;
$headers{$filename} = \%new_h_file;
} elsif ($file =~ m!.c$!) {
my %new_c_file = (
path => $pathname,
fname => $filename,
funcs => [],
);
$c_file = \%new_c_file;
$c_files{$filename} = \%new_c_file;
} else {
warn("Ignoring unexpected file $file\n");
next;
}
$smb1 = 1 if ($file =~ m!/smb1ops.c|/cifssmb.c|/cifstransport.c!);
foreach my $line (<$fh>) {
$lineno++;
chomp($line);
push @copy, $line;
if (!$line) {
# Blank line
push @file_content, @copy;
@copy = ();
next;
}
# Handle continuation or end of block comment. Look for C file
# prototype insertion point markers.
if ($comment) {
if ($line =~ m![*]/!) {
if ($comment == 2 && $file_marker) {
$cmarkers{$file_marker} = $file_marker;
push @copy, "#C_MARKER " . $file_marker;
$file_marker = 0;
}
$comment = 0;
} else {
$comment++;
if ($comment == 2 && $line =~ m! [*] ([a-z][a-z_0-9]*[.][c])$!) {
$file_marker = $1;
print("Found file marker ", $file_marker, " in ", $filename, "\n");
}
}
push @file_content, @copy;
@copy = ();
next;
}
# Check cpp directives, particularly looking for SMB1 bits
if ($line =~ /^[#]/) {
if ($header) {
if ($line =~ /ifdef.*(CONFIG_[A-Z0-9_])/) {
error("multiconfig") if $config;
$config = $1;
$smb1++ if ($config eq "CONFIG_CIFS_ALLOW_INSECURE_LEGACY");
} elsif ($line =~ /endif/) {
$smb1-- if ($config eq "CONFIG_CIFS_ALLOW_INSECURE_LEGACY");
$config = "";
}
}
push @file_content, @copy;
@copy = ();
next;
}
# Exclude interference in finding func names and return types
if ($line =~ /^[{]/ ||
$line =~ /##/ ||
$line =~ /^[_a-z0-9A-Z]+:$/ || # goto label
$line =~ /^do [{]/ ||
$line =~ m!^//!) {
push @file_content, @copy;
@copy = ();
next;
}
# Start of a block comment
if ($line =~ m!^/[*]!) {
$comment = 1 unless ($line =~ m![*]/!);
push @file_content, @copy;
@copy = ();
next;
}
# End of a braced section, such as a function implementation
if ($line =~ /^[}]/) {
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
push @file_content, @copy;
@copy = ();
next;
}
if ($line =~ /^typedef/) {
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
push @file_content, @copy;
@copy = ();
next;
}
# Extract function qualifiers. There may be multiple of these in more
# or less any order. Some of them cause the func to be skipped (e.g. inline).
if ($line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)\W/ ||
$line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)$/) {
error("Unexpected qualifier '$1'") if ($state != 0);
while ($line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)\W/ ||
$line =~ /^(static|extern|inline|noinline|noinline_for_stack|__always_inline)$/) {
$qual .= " " if ($qual);
$qual .= $1;
$inline = 1 if ($1 eq "inline");
$inline = 1 if ($1 eq "__always_inline");
$line = substr($line, length($1));
$line =~ s/^\s+//;
}
}
if ($state == 0) {
# Extract what we assume to be the return type
if ($line =~ /^\s/) {
push @file_content, @copy;
@copy = ();
next;
}
while ($line =~ /^(unsigned|signed|bool|char|short|int|long|void|const|volatile|(struct|union|enum)\s+[_a-zA-Z][_a-zA-Z0-9]*|[*]|__init|__exit|__le16|__le32|__le64|__be16|__be32|__be64)/) {
$type .= " " if $type;
$type .= $1;
$line = substr($line, length($1));
$line =~ s/^\s+//;
}
if ($line =~ /^struct [{]/) {
# Ignore structure definitions
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
push @file_content, @copy;
@copy = ();
next;
}
if (index($line, "=") >= 0) {
# Ignore assignments
$type = "";
$qual = "";
$funcname = "";
@funcdef = "";
push @file_content, @copy;
@copy = ();
next;
}
# Try and extract a function's type and name
while ($line =~ /(^[_a-zA-Z][_a-zA-Z0-9]*)/) {
my $name = $1;
$line = substr($line, length($name));
next if ($line =~ /^[{]/);
$line =~ s/^\s+//;
my $ch = substr($line, 0, 1);
last if ($ch eq "[" || $ch eq ";"); # Global variables
if ($ch eq "(") {
# Found the function name
$state = 1;
$line = substr($line, 1);
$funcname = $name;
my $tmp = $qual . $type . " " . $funcname . "(";
$tmp =~ s/[*] /*/;
push @funcdef, $tmp;
$bracket = 1;
last;
}
if ($type) {
last if (index($line, ";") >= 0 && index($line, "(") == -1);
error("Unexpected name '$name' after '$type'");
}
$type .= " " if $type;
$type .= $name;
if ($line =~ /^(\s*[*]+)/) {
my $ptr = $1;
$type .= $ptr;
$line = substr($line, length($ptr));
}
}
}
# Try and extract a function's argument list
my $from = 0;
if ($state == 1) {
while (1) {
my $o = index($line, "(", $from);
my $c = index($line, ")", $from);
my $m = index($line, ",", $from);
my $b = earliest($o, $c, $m);
if ($b < 0) {
push @funcdef, $line
unless ($line eq "");
last;
}
my $ch = substr($line, $b, 1);
# Push the arguments separately on to the list
if ($ch eq ",") {
push @funcdef, substr($line, 0, $b + 1);
$line = substr($line, $b + 1);
$from = 0;
} elsif ($ch eq "(") {
# Handle brackets in the argument list (e.g. function
# pointers)
$bracket++;
$from = $b + 1;
} elsif ($ch eq ")") {
$bracket--;
if ($bracket == 0) {
push @funcdef, substr($line, 0, $b + 1);
$line = substr($line, $b + 1);
$state = 2;
last;
}
$from = $b + 1;
}
}
}
if ($state == 2) {
$inline = 1 if ($qual =~ /inline/);
#print("QUAL $qual $type $funcname $inline ", $#funcdef, "\n");
if (!$header &&
$qual !~ /static/ &&
$funcname ne "__acquires" &&
$funcname ne "__releases" &&
$funcname ne "module_init" &&
$funcname ne "module_exit" &&
$funcname ne "module_param" &&
$funcname ne "module_param_call" &&
$funcname ne "PROC_FILE_DEFINE" &&
$funcname !~ /MODULE_/ &&
$funcname !~ /DEFINE_/) {
# Okay, we appear to have a function implementation
my $func;
my $dup = 0;
if (exists($funcs{$funcname})) {
$func = $funcs{$funcname};
if (exists $func->{body}) {
print("dup $funcname\n");
$dup = 1;
}
} else {
my %new_func = (
name => $funcname,
cond => "",
legacy => 0,
);
$func = \%new_func;
$funcs{$funcname} = $func;
$func->{body} = pad(\@funcdef);
}
$func->{body} = pad(\@funcdef);
$func->{legacy} = 1 if $smb1;
if ($funcname eq "cifs_inval_name_dfs_link_error") {
$func->{cond} = "#ifdef CONFIG_CIFS_DFS_UPCALL";
} elsif ($funcname eq "cifs_listxattr") {
$func->{cond} = "#ifdef CONFIG_CIFS_XATTR";
}
push @{$c_file->{funcs}}, $func
unless $dup;
} elsif (!$header || $inline) {
# Ignore inline function implementations and other weirdies
push @file_content, @copy;
} elsif ($header && !$inline) {
push @file_content, "#FUNCPROTO " . $funcname;
my $func;
if (exists($funcs{$funcname})) {
$func = $funcs{$funcname};
$func->{lineno} = $lineno;
$func->{pathname} = $pathname;
} else {
my %new_func = (
name => $funcname,
cond => "",
lineno => $lineno,
pathname => $pathname,
legacy => 0,
);
$func = \%new_func;
$funcs{$funcname} = $func;
}
$func->{legacy} = 1 if $smb1;
}
@funcdef = ();
$type = "";
$qual = "";
$funcname = "";
$inline = 0;
$state = 0;
@copy = ();
}
if ($line =~ /;/) {
$type = "";
$qual = "";
$funcname = "";
@funcdef = ();
$state = 0;
push @file_content, @copy;
@copy = ();
}
}
close($fh);
if ($header) {
$header->{content} = \@file_content;
}
}
sub write_header($)
{
my ($header) = @_;
my $path = $header->{path};
my $legacy = 0;
$legacy = 1 if ($path =~ m!smb1proto[.]h!);
my @output = ();
foreach my $line (@{$header->{content}}) {
if ($line =~ "^[#]C_MARKER (.*)") {
my $file_marker = $cmarkers{$1};
my $c_file = $c_files{$file_marker};
print("Found $line\n");
foreach my $func (@{$c_file->{funcs}}) {
print("func ", $func->{name}, "\n");
push @output, @{$func->{body}};
}
next;
} elsif ($line =~ "^[#]FUNCPROTO ([_a-zA-Z0-9]+)") {
my $funcname = $1;
my $func = $funcs{$funcname};
if (!$func->{body}) {
print($func->{pathname}, ":", $func->{lineno}, ": '", $funcname,
"' dead prototype\n");
next;
}
if ($func->{legacy} == $legacy) {
#push @output, $line;
push @output, @{$func->{body}};
}
} else {
push @output, $line;
}
}
open my $fh, ">$path"
or die "Could not open file '$path' for writing";
foreach my $f (@output) {
print($fh $f, "\n") or die $path;
}
close($fh) or die $path;
}
foreach my $h (keys(%headers)) {
write_header($headers{$h});
}[ sashal: Reduced backport -- upstream 645427b7a6c59 touches 3 file(s), this
backport carries 3. Not backported here:
fs/smb/client/cifsproto.h
fs/smb/client/fscache.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 730d0bb19507 ("smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2")
--- fs/smb/client/smb1ops.c | 171 ------------------------------------------ fs/smb/client/smb1proto.h | 2 fs/smb/client/smb1transport.c | 171 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 171 deletions(-)
fs/smb/client/smb1ops.c +0 -171
@@ -238,146 +238,6 @@ cifs_get_next_mid(struct TCP_Server_Inforeturn mid; } -/* -return codes: -0 not a transact2, or all data present ->0 transact2 with that much data missing --EINVAL invalid transact2 -*/ -static int -check2ndT2 (char *buf) -{ -struct smb_hdr *pSMB = (struct smb_hdr *)buf; -struct smb_t2_rsp *pSMBt; -int remaining; -__u16 total_data_size, data_in_this_rsp; - -if (pSMB->Command !=SMB_COM_TRANSACTION2 ) -return 0 ; - -/* check for plausible wct, bcc and t2 data and parm sizes */ -/* check for parm and data offset going beyond end of smb */ -if (pSMB->WordCount !=10 ) {/* coalesce_t2 depends on this */ -cifs_dbg (FYI ,"Invalid transact2 word count\n" ); -return -EINVAL ; - } - - pSMBt = (struct smb_t2_rsp *)pSMB; - - total_data_size =get_unaligned_le16 (&pSMBt->t2_rsp.TotalDataCount); - data_in_this_rsp =get_unaligned_le16 (&pSMBt->t2_rsp.DataCount); - -if (total_data_size == data_in_this_rsp) -return 0 ; -else if (total_data_size < data_in_this_rsp) { -cifs_dbg (FYI ,"total data %d smaller than data in frame %d\n" , - total_data_size, data_in_this_rsp); -return -EINVAL ; - } - - remaining = total_data_size - data_in_this_rsp; - -cifs_dbg (FYI ,"missing %d bytes from transact2, check next response\n" , - remaining); -if (total_data_size > CIFSMaxBufSize) { -cifs_dbg (VFS ,"TotalDataSize %d is over maximum buffer %d\n" , - total_data_size, CIFSMaxBufSize); -return -EINVAL ; - } -return remaining; -} - -static int -coalesce_t2 (char *second_buf,struct smb_hdr *target_hdr) -{ -struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; -struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; -char *data_area_of_tgt; -char *data_area_of_src; -int remaining; -unsigned int byte_count, total_in_tgt; -__u16 tgt_total_cnt, src_total_cnt, total_in_src; - - src_total_cnt =get_unaligned_le16 (&pSMBs->t2_rsp.TotalDataCount); - tgt_total_cnt =get_unaligned_le16 (&pSMBt->t2_rsp.TotalDataCount); - -if (tgt_total_cnt != src_total_cnt) -cifs_dbg (FYI ,"total data count of primary and secondary t2 differ source=%hu target=%hu\n" , - src_total_cnt, tgt_total_cnt); - - total_in_tgt =get_unaligned_le16 (&pSMBt->t2_rsp.DataCount); - - remaining = tgt_total_cnt - total_in_tgt; - -if (remaining <0 ) { -cifs_dbg (FYI ,"Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n" , - tgt_total_cnt, total_in_tgt); -return -EPROTO ; - } - -if (remaining ==0 ) { -/* nothing to do, ignore */ -cifs_dbg (FYI ,"no more data remains\n" ); -return 0 ; - } - - total_in_src =get_unaligned_le16 (&pSMBs->t2_rsp.DataCount); -if (remaining < total_in_src) -cifs_dbg (FYI ,"transact2 2nd response contains too much data\n" ); - -/* find end of first SMB data area */ - data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + -get_unaligned_le16 (&pSMBt->t2_rsp.DataOffset); - -/* validate target area */ - data_area_of_src = (char *)&pSMBs->hdr.Protocol + -get_unaligned_le16 (&pSMBs->t2_rsp.DataOffset); - - data_area_of_tgt += total_in_tgt; - - total_in_tgt += total_in_src; -/* is the result too big for the field? */ -if (total_in_tgt >USHRT_MAX ) { -cifs_dbg (FYI ,"coalesced DataCount too large (%u)\n" , - total_in_tgt); -return -EPROTO ; - } -put_unaligned_le16 (total_in_tgt, &pSMBt->t2_rsp.DataCount); - -/* fix up the BCC */ - byte_count =get_bcc (target_hdr); - byte_count += total_in_src; -/* is the result too big for the field? */ -if (byte_count >USHRT_MAX ) { -cifs_dbg (FYI ,"coalesced BCC too large (%u)\n" , byte_count); -return -EPROTO ; - } -put_bcc (byte_count, target_hdr); - - byte_count =be32_to_cpu (target_hdr->smb_buf_length); - byte_count += total_in_src; -/* don't allow buffer to overflow */ -if (byte_count > CIFSMaxBufSize +MAX_CIFS_HDR_SIZE -4 ) { -cifs_dbg (FYI ,"coalesced BCC exceeds buffer size (%u)\n" , - byte_count); -return -ENOBUFS ; - } - target_hdr->smb_buf_length =cpu_to_be32 (byte_count); - -/* copy second buffer into end of first buffer */ -memcpy (data_area_of_tgt, data_area_of_src, total_in_src); - -if (remaining != total_in_src) { -/* more responses to go */ -cifs_dbg (FYI ,"waiting for more secondary responses\n" ); -return 1 ; - } - -/* we are done */ -cifs_dbg (FYI ,"found the last secondary response\n" ); -return 0 ; -} - static void cifs_downgrade_oplock(struct TCP_Server_Info *server, struct cifsInodeInfo *cinode, __u32 oplock,
@@ -387,37 +247,6 @@ cifs_downgrade_oplock(struct TCP_Server_ }static bool -cifs_check_trans2 (struct mid_q_entry *mid,struct TCP_Server_Info *server, -char *buf,int malformed) -{ -if (malformed) -return false; -if (check2ndT2 (buf) <=0 ) -return false; - mid->multiRsp = true; -if (mid->resp_buf) { -/* merge response - fix up 1st*/ - malformed =coalesce_t2 (buf, mid->resp_buf); -if (malformed >0 ) -return true; -/* All parts received or packet is malformed. */ - mid->multiEnd = true; -dequeue_mid (mid, malformed); -return true; - } -if (!server->large_buf) { -/*FIXME: switch to already allocated largebuf?*/ -cifs_dbg (VFS ,"1st trans2 resp needs bigbuf\n" ); - }else { -/* Have first buffer */ - mid->resp_buf = buf; - mid->large_buf = true; - server->bigbuf =NULL ; - } -return true; -} - -static bool cifs_need_neg (struct TCP_Server_Info *server) {return server->maxBuf ==0 ;
fs/smb/client/smb1proto.h +2 -0
@@ -33,6 +33,8 @@ extern struct smb_version_values smb1_va/* * smb1transport.c */ +bool cifs_check_trans2 (struct mid_q_entry *mid,struct TCP_Server_Info *server, +char *buf,int malformed);#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* _SMB1PROTO_H */
fs/smb/client/smb1transport.c +171 -0
174 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 546/583 smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Sorenson <[email protected]>
[ Upstream commit 730d0bb19507b9e19c2fe5343109ac618e2fbce5 ]
When a valid primary TRANSACT2 response has been received (mid->resp_buf set, mid->multiRsp true) and a subsequent secondary response causes cifs_check_trans2() to return false -- either because the SMB header is invalid (malformed != 0) or because check2ndT2() rejects the PDU -- handle_mid() overwrites mid->resp_buf with the new buffer (leaking the primary buffer) and, because mid->multiRsp is set, skips the server->smallbuf/bigbuf NULL-out. When the user thread frees mid->resp_buf, server->smallbuf or server->bigbuf is left dangling; the demux thread reuses it for the next packet, resulting in a use-after-free.
Combine both early-exit conditions and, when mid->multiRsp is already set, abort the pending transaction inline: set multiEnd, call dequeue_mid() with malformed=true, and return true so handle_mid() exits without touching mid->resp_buf or the server buffer pointers.
--- fs/smb/client/smb1transport.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
fs/smb/client/smb1transport.c +11 -3
@@ -708,10 +708,18 @@ bool cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, char *buf, int malformed) { - if (malformed) - return false; -if (check2ndT2 (buf) <=0 ) + if (malformed || check2ndT2(buf) <= 0) { + /* mid->multiRsp blocks the server buf detach in handle_mid(); + * returning false here would leak resp_buf and leave a dangling + * server->smallbuf/bigbuf after the user thread frees resp_buf. + */ + if (mid->multiRsp) { + mid->multiEnd = true; +dequeue_mid (mid, true); +return true; + }return false; + } mid->multiRsp = true;if (mid->resp_buf) {/* merge response - fix up 1st*/
Greg Kroah-Hartman
maintainerPATCH 547/583 smb: client: fix OOB read/write from unvalidated DataOffset in coalesce_t2()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Sorenson <[email protected]>
[ Upstream commit 6343c1da561962688f203362d80d6a3bfa39fa1b ]
coalesce_t2() computes data pointers directly from server-supplied DataOffset fields with no validation against buffer bounds:
data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
data_area_of_src = (char *)&pSMBs->hdr.Protocol +
get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
data_area_of_tgt += total_in_tgt;
...
memcpy(data_area_of_tgt, data_area_of_src, total_in_src);A small DataOffset can push a pointer below the actual byte area, overwriting header fields; a large one can push it past the buffer end, causing out-of-bounds heap reads (source) or writes (target). The BCC overflow guard does not prevent this: BCC reflects how much data is present, while DataOffset controls where in the buffer it starts.
The "validate target area" comment present since the function was first written in 2005 was a placeholder that was never implemented.
Add lower- and upper-bound checks for both data pointers before the memcpy, and before any target header fields are modified.
--- fs/smb/client/smb1transport.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
fs/smb/client/smb1transport.c +20 -1
@@ -655,12 +655,31 @@ coalesce_t2(char *second_buf, struct smb data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +get_unaligned_le16 (&pSMBt->t2_rsp.DataOffset); -/* validate target area */ data_area_of_src = (char *)&pSMBs->hdr.Protocol +get_unaligned_le16 (&pSMBs->t2_rsp.DataOffset); data_area_of_tgt += total_in_tgt; +/* +* DataOffset fields are server-supplied and not validated against +* buffer bounds; check both data pointers before mutating the +* target header. +*/ +if (data_area_of_tgt < (char *)target_hdr + +sizeof (struct smb_t2_rsp ) +sizeof (__le16) || + data_area_of_tgt + total_in_src > + (char *)target_hdr + CIFSMaxBufSize +MAX_CIFS_HDR_SIZE ) { +cifs_dbg (VFS ,"%s: target data area out of bounds\n" , __func__); +return -EPROTO ; + } +if (data_area_of_src < second_buf + +sizeof (struct smb_t2_rsp ) +sizeof (__le16) || + data_area_of_src + total_in_src > + second_buf +smbCalcSize ((struct smb_hdr *)second_buf)) { +cifs_dbg (VFS ,"%s: secondary data area out of bounds\n" , __func__); +return -EPROTO ; + } + total_in_tgt += total_in_src;/* is the result too big for the field? */ if (total_in_tgt >USHRT_MAX ) {
Greg Kroah-Hartman
maintainerPATCH 548/583 cifs: Do some preparation prior to organising the function declarations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 32a60868093aed5ccaa5e57f3b57f791f22ac133 ]
Make some preparatory cleanups prior to running a script to organise the function declarations within the fs/smb/client/ headers. These include:
(1) Remove "inline" from the dummy cifs_proc_init/clean() functions as they are in a .c file.
(2) Move should_compress()'s kdoc comment to the .c file and remove kdoc markers from the comments.
(3) Rename CIFS_ALLOW_INSECURE_LEGACY in #endif comments to have CONFIG_ on the front to allow the script to recognise it.
(4) Don't let comments have bare words at the left margin as that confused the simplistic function detection code in the script.
(5) Adjust some argument lists so that when and if the cleanup script is run they don't end up over 100 chars.
(6) Fix a few comments to have missing '*' added or the "*/" moved to their own lines so that checkpatch doesn't moan over the cleanup script patch.
(7) Move struct cifs_calc_sig_ctx to cifsglob.h.
(8) Remove some __KERNEL__ conditionals.
Stable-dep-of: 65deb1835934 ("smb: client: reject a tree connect response whose byte count is too small")
--- fs/smb/client/cifs_debug.c | 4 - fs/smb/client/cifs_spnego.h | 2 fs/smb/client/cifs_unicode.h | 3 fs/smb/client/cifsfs.c | 5 - fs/smb/client/cifsglob.h | 8 ++ fs/smb/client/cifsproto.h | 12 +-- fs/smb/client/cifssmb.c | 140 ++++++++++++++++++++++++++++++++++++++++ fs/smb/client/compress.c | 21 +++++- fs/smb/client/compress.h | 19 ----- fs/smb/client/connect.c | 142 ---------------------------------------- fs/smb/client/dir.c | 2 fs/smb/client/dns_resolve.h | 4 - fs/smb/client/fs_context.c | 2 fs/smb/client/misc.c | 1 fs/smb/client/netmisc.c | 2 fs/smb/client/smb2file.c | 3 fs/smb/client/smb2inode.c | 1 fs/smb/client/smb2maperror.c | 3 fs/smb/client/smb2ops.c | 4 - fs/smb/client/smb2pdu.c | 5 - fs/smb/client/smb2proto.h | 2 fs/smb/client/trace.h | 150 +++++++++++++++++++++++++++++++++++++++++++ 22 files changed, 342 insertions(+), 193 deletions(-)
fs/smb/client/cifs_debug.c +2 -2
@@ -1307,11 +1307,11 @@ static const struct proc_ops cifs_mount_ };#else -inline void cifs_proc_init (void ) +void cifs_proc_init (void ) { } -inline void cifs_proc_clean (void ) +void cifs_proc_clean (void ) { }#endif /* PROC_FS */
fs/smb/client/cifs_spnego.h +0 -2
@@ -27,10 +27,8 @@ struct cifs_spnego_msg { uint8_t data[]; }; -#ifdef __KERNEL__extern struct key_type cifs_spnego_key_type;extern struct key *cifs_get_spnego_key (struct cifs_ses *sesInfo,struct TCP_Server_Info *server); -#endif /* KERNEL */ #endif /* _CIFS_SPNEGO_H */
fs/smb/client/cifs_unicode.h +0 -3
@@ -54,7 +54,6 @@#define SFM_MAP_UNI_RSVD 1#define SFU_MAP_UNI_RSVD 2 -#ifdef __KERNEL__int cifs_from_utf16 (char *to,const __le16 *from,int tolen,int fromlen,const struct nls_table *cp,int map_type); int cifs_utf16_bytes(const __le16 *from, int maxbytes,
@@ -69,8 +68,6 @@ extern int cifs_remap(struct cifs_sb_infextern __le16 *cifs_strndup_to_utf16 (const char *src,const int maxlen,int *utf16_len,const struct nls_table *cp,int remap); -#endif -wchar_t cifs_toupper (wchar_t in);#endif /* _CIFS_UNICODE_H */
fs/smb/client/cifsfs.c +3 -2
@@ -28,6 +28,8 @@#include <linux/splice.h> #include <linux/uuid.h> #include <linux/xattr.h> +#include <linux/mm.h> +#include <linux/key-type.h> #include <uapi/linux/magic.h> #include <net/ipv6.h> #include "cifsfs.h"
@@ -35,10 +37,9 @@#define DECLARE_GLOBALS_HERE #include "cifsglob.h" #include "cifsproto.h" +#include "smb2proto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" -#include <linux/mm.h> -#include <linux/key-type.h> #include "cifs_spnego.h" #include "fscache.h" #ifdef CONFIG_CIFS_DFS_UPCALL
fs/smb/client/cifsglob.h +7 -1
@@ -2112,7 +2112,7 @@ extern mempool_t cifs_io_subrequest_pool#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY extern struct smb_version_operations smb20_operations;extern struct smb_version_values smb20_values; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ extern struct smb_version_operations smb21_operations;extern struct smb_version_values smb21_values;extern struct smb_version_values smbdefault_values;
@@ -2383,4 +2383,10 @@ static inline int cifs_open_create_optio*/ #define CIFS_INO_BLOCKS (size) DIV_ROUND_UP_ULL((u64)(size), 512) +struct cifs_calc_sig_ctx { +struct md5_ctx *md5; +struct hmac_sha256_ctx *hmac; +struct shash_desc *shash; +}; +#endif /* _CIFS_GLOB_H */
fs/smb/client/cifsproto.h +6 -6
@@ -590,7 +590,7 @@ extern int cifs_do_set_acl(const unsigne const struct nls_table *nls_codepage, int remap);extern int CIFSGetExtAttr (const unsigned int xid,struct cifs_tcon *tcon,const int netfid,__u64 *pExtAttrBits,__u64 *pMask); -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ extern void cifs_autodisable_serverino (struct cifs_sb_info *cifs_sb);extern bool couldbe_mf_symlink (const struct cifs_fattr *fattr); extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
@@ -622,11 +622,6 @@ int cifs_create_mf_symlink(unsigned intstruct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, unsigned int *pbytes_written); -struct cifs_calc_sig_ctx { -struct md5_ctx *md5; -struct hmac_sha256_ctx *hmac; -struct shash_desc *shash; -};int __cifs_calc_signature (struct smb_rqst *rqst,struct TCP_Server_Info *server,char *signature,struct cifs_calc_sig_ctx *ctx); enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
@@ -775,4 +770,9 @@ static inline void cifs_free_open_info(s memset(data, 0, sizeof(*data)); } +#define smb_EIO2 (trace, info, info2) ({ \ + trace_smb3_eio(trace, info, info2); \ + -EIO; \ +}) +#endif /* _CIFSPROTO_H */
fs/smb/client/cifssmb.c +140 -0
146 lines not on this page · raw message
fs/smb/client/compress.c +18 -3
@@ -44,7 +44,7 @@ struct bucket {unsigned int count; }; -/** +/* * has_low_entropy() - Compute Shannon entropy of the sampled data. * @bkt: Bytes counts of the sample. * @slen: Size of the sample.
@@ -82,7 +82,7 @@ static bool has_low_entropy(struct bucke#define BYTE_DIST_BAD 0#define BYTE_DIST_GOOD 1#define BYTE_DIST_MAYBE 2 -/** +/* * calc_byte_distribution() - Compute byte distribution on the sampled data. * @bkt: Byte counts of the sample. * @slen: Size of the sample.
@@ -182,7 +182,7 @@ static int collect_sample(const struct ireturn s; } -/** +/* * is_compressible() - Determines if a chunk of data is compressible. * @data: Iterator containing uncompressed data. *
@@ -261,6 +261,21 @@ out:return ret; } +/* +* should_compress() - Determines if a request (write) or the response to a +* request (read) should be compressed. +* @tcon: tcon of the request is being sent to +* @rqst: request to evaluate +* +* Return: true iff: +* - compression was successfully negotiated with server +* - server has enabled compression for the share +* - it's a read or write request +* - (write only) request length is >= SMB_COMPRESS_MIN_LEN +* - (write only) is_compressible() returns 1 +* +* Return false otherwise. +*/ bool should_compress (const struct cifs_tcon *tcon,const struct smb_rqst *rq) {const struct smb2_hdr *shdr = rq->rq_iov->iov_base;
fs/smb/client/compress.h +2 -17
@@ -29,26 +29,11 @@#ifdef CONFIG_CIFS_COMPRESSION typedef int (*compress_send_fn )(struct TCP_Server_Info *,int ,struct smb_rqst *); -int smb_compress (struct TCP_Server_Info *server,struct smb_rqst *rq,compress_send_fn send_fn); -/** -* should_compress() - Determines if a request (write) or the response to a -* request (read) should be compressed. -* @tcon: tcon of the request is being sent to -* @rqst: request to evaluate -* -* Return: true iff: -* - compression was successfully negotiated with server -* - server has enabled compression for the share -* - it's a read or write request -* - (write only) request length is >= SMB_COMPRESS_MIN_LEN -* - (write only) is_compressible() returns 1 -* -* Return false otherwise. -*/ +int smb_compress (struct TCP_Server_Info *server,struct smb_rqst *rq,compress_send_fn send_fn);bool should_compress (const struct cifs_tcon *tcon,const struct smb_rqst *rq); -/** +/* * smb_compress_alg_valid() - Validate a compression algorithm. * @alg: Compression algorithm to check. * @valid_none: Conditional check whether NONE algorithm should be
fs/smb/client/connect.c +0 -142
148 lines not on this page · raw message
fs/smb/client/dir.c +1 -1
@@ -458,7 +458,7 @@ out_err:int cifs_atomic_open (struct inode *inode,struct dentry *direntry, -struct file *file,unsigned oflags,umode_t mode) +struct file *file,unsigned int oflags,umode_t mode) {int rc;unsigned int xid;
fs/smb/client/dns_resolve.h +0 -4
@@ -15,8 +15,6 @@#include "cifsglob.h" #include "cifsproto.h" -#ifdef __KERNEL__ -int dns_resolve_name (const char *dom,const char *name,size_t namelen,struct sockaddr *ip_addr);
@@ -36,6 +34,4 @@ static inline int dns_resolve_unc(const return dns_resolve_name(dom, name, namelen, ip_addr); } -#endif /* KERNEL */ -#endif /* _DNS_RESOLVE_H */
fs/smb/client/fs_context.c +1 -1
@@ -505,7 +505,7 @@ cifs_parse_smb_version(struct fs_contextcase Smb_20:cifs_errorf (fc,"vers=2.0 mount not permitted when legacy dialects disabled\n" );return 1 ; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ case Smb_21: ctx->ops = &smb21_operations; ctx->vals = &smb21_values;
fs/smb/client/misc.c +1 -0
@@ -18,6 +18,7 @@#include "nterr.h" #include "cifs_unicode.h" #include "smb2pdu.h" +#include "smb2proto.h" #include "cifsfs.h" #ifdef CONFIG_CIFS_DFS_UPCALL #include "dns_resolve.h"
fs/smb/client/netmisc.c +1 -1
@@ -200,7 +200,7 @@ cifs_set_port(struct sockaddr *addr, con }/***************************************************************************** -convert a NT status code to a dos class/code +*convert a NT status code to a dos class/code *****************************************************************************/ /* NT status -> dos error map */ static const struct {
fs/smb/client/smb2file.c +2 -1
@@ -163,7 +163,8 @@ int smb2_parse_symlink_response(struct c cifs_sb); } -int smb2_open_file (const unsigned int xid,struct cifs_open_parms *oparms,__u32 *oplock,void *buf) +int smb2_open_file (const unsigned int xid,struct cifs_open_parms *oparms, +__u32 *oplock,void *buf) {int rc;__le16 *smb2_path;
fs/smb/client/smb2inode.c +0 -1
@@ -21,7 +21,6 @@#include "cifs_unicode.h" #include "fscache.h" #include "smb2glob.h" -#include "smb2pdu.h" #include "smb2proto.h" #include "cached_dir.h" #include "../common/smb2status.h"
fs/smb/client/smb2maperror.c +1 -2
@@ -10,10 +10,9 @@#include <linux/errno.h> #include "cifsglob.h" #include "cifs_debug.h" -#include "smb2pdu.h" #include "smb2proto.h" -#include "../common/smb2status.h" #include "smb2glob.h" +#include "../common/smb2status.h" #include "trace.h" struct status_to_posix_error {
fs/smb/client/smb2ops.c +2 -2
@@ -17,9 +17,9 @@#include <uapi/linux/magic.h> #include "cifsfs.h" #include "cifsglob.h" -#include "smb2pdu.h" -#include "smb2proto.h" #include "cifsproto.h" +#include "smb2proto.h" +#include "smb2pdu.h" #include "cifs_debug.h" #include "cifs_unicode.h" #include "../common/smb2status.h"
fs/smb/client/smb2pdu.c +3 -2
@@ -26,8 +26,8 @@#include <linux/netfs.h> #include <trace/events/netfs.h> #include "cifsglob.h" -#include "cifsacl.h" #include "cifsproto.h" +#include "cifsacl.h" #include "smb2proto.h" #include "cifs_unicode.h" #include "cifs_debug.h"
@@ -3921,7 +3921,8 @@ int SMB2_query_info(const unsigned int x/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ int SMB311_posix_query_info (const unsigned int xid,struct cifs_tcon *tcon, -u64 persistent_fid,u64 volatile_fid,struct smb311_posix_qinfo *data,u32 *plen) +u64 persistent_fid,u64 volatile_fid, +struct smb311_posix_qinfo *data,u32 *plen) {size_t output_len =sizeof (struct smb311_posix_qinfo ) + (sizeof (struct smb_sid ) *2 ) + (PATH_MAX *2 );
fs/smb/client/smb2proto.h +2 -0
@@ -9,8 +9,10 @@*/ #ifndef _SMB2PROTO_H#define _SMB2PROTO_H +#include <linux/nls.h> #include <linux/key-type.h> +#include "cached_dir.h" struct statfs ;struct smb_rqst ;
fs/smb/client/trace.h +150 -0
174 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 549/583 smb: client: reject a tree connect response whose byte count is too small
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
[ Upstream commit 65deb18359341141d37dc86fc7853511be3c87a7 ]
CIFSTCon() bounds its strnlen() over the byte area with the server's ByteCount minus two, which for ByteCount 0 or 1 goes negative as an int and converts to a huge size_t. The later subtraction wraps the __u16 bytes_left, and that is what bounds cifs_strndup_from_utf16(): a bound of up to 65535 against a ~16 KB cifs_req_poolp object runs off the end of the slab object, and the bytes reach userspace through tcon->nativeFileSystem in /proc/fs/cifs/DebugData.
Reject a byte area too small for what the parser consumes. Two bytes is the least it can consume, and no conformant response carries fewer. The new trace point is the 129th smb_eio_trace entry, which __mode(byte) cannot represent, so the attribute goes with it.
--- fs/smb/client/cifssmb.c | 6 ++++++ fs/smb/client/trace.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-)
fs/smb/client/cifssmb.c +6 -0
@@ -608,6 +608,11 @@ CIFSTCon(const unsigned int xid, struct tcon->tid = smb_buffer_response->Tid; bcc_ptr =pByteArea (smb_buffer_response); bytes_left =get_bcc (smb_buffer_response); +if (bytes_left <2 ) { + rc =smb_EIO2 (smb_eio_trace_tcon_bcc_too_small, + bytes_left,2 ); + goto out; + } length =strnlen (bcc_ptr, bytes_left -2 );if (smb_buffer->Flags2 &SMBFLG2_UNICODE ) is_unicode = true;
@@ -663,6 +668,7 @@ CIFSTCon(const unsigned int xid, structreset_cifs_unix_caps (xid, tcon,NULL ,NULL ); } } +out:cifs_buf_release (smb_buffer);return rc; }
fs/smb/client/trace.h +2 -1
@@ -133,6 +133,7 @@ EM(smb_eio_trace_sym_slash, "sym_slash") \ EM(smb_eio_trace_sym_target_len, "sym_target_len") \ EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \ + EM(smb_eio_trace_tcon_bcc_too_small, "tcon_bcc_too_small") \ EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \ EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \EM (smb_eio_trace_tx_compress_failed,"tx_compress_failed" ) \
@@ -214,7 +215,7 @@#define EM (a, b) a,#define E_ (a, b) a -enum smb_eio_trace { smb_eio_traces }__mode (byte ); +enum smb_eio_trace { smb_eio_traces };enum smb3_rw_credits_trace { smb3_rw_credits_traces }__mode (byte );enum smb3_tcon_ref_trace { smb3_tcon_ref_traces }__mode (byte );
Greg Kroah-Hartman
maintainerPATCH 550/583 NFSD: Consolidate the revocation-path client unpin
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 3308cf3f11ed23c79f9f3f90b34bbbad3e3a6ea9 ]
The client use-after-free fixes in the state-revocation paths left four open-coded copies of one idiom: drop a cl_rpc_users pin without renewing the client's lease, waking force_expire_client() when the last pin drops on a client it is tearing down. The accompanying "do not renew" rationale was documented at only one of the four sites.
put_client_renew_locked() and put_client_renew() already carry the same pin-drop logic, but they renew a non-expired client's lease and so would resurrect the client whose state is being revoked. Factor the common pin-drop into __put_client_locked(), parameterized by whether to renew. The renew helpers pass true; the new put_client_no_renew_locked() and put_client_no_renew() pass false and carry the revocation paths, which must not revive the client they are tearing down. No change in behavior.
Stable-dep-of: 9026932ac8be ("NFSD: Prevent client use-after-free during blocked-lock reaping")
--- fs/nfsd/nfs4state.c | 65 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 25 deletions(-)
fs/nfsd/nfs4state.c +40 -25
@@ -200,18 +200,28 @@ renew_client_locked(struct nfs4_client * clp->cl_state =NFSD4_ACTIVE ; } +/* +* Finish a cl_rpc_users unpin with the client_lock held. A +* revocation walk clears @renew so the client whose state it is +* revoking is not revived; every other caller renews the lease of +* a still-active client. +*/ +static void __put_client_locked (struct nfs4_client *clp,bool renew) +{ +if (is_client_expired (clp)) +wake_up_all (&expiry_wq); +else if (renew) +renew_client_locked (clp); +} +static void put_client_renew_locked (struct nfs4_client *clp) {struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id);lockdep_assert_held (&nn->client_lock); -if (!atomic_dec_and_test (&clp->cl_rpc_users)) -return ; -if (!is_client_expired (clp)) -renew_client_locked (clp); -else -wake_up_all (&expiry_wq); +if (atomic_dec_and_test (&clp->cl_rpc_users)) +__put_client_locked (clp, true); }static void put_client_renew (struct nfs4_client *clp)
@@ -220,10 +230,27 @@ static void put_client_renew(struct nfs4if (!atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock))return ; -if (!is_client_expired (clp)) -renew_client_locked (clp); -else -wake_up_all (&expiry_wq); +__put_client_locked (clp, true); +spin_unlock (&nn->client_lock); +} + +static void put_client_no_renew_locked (struct nfs4_client *clp) +{ +struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); + +lockdep_assert_held (&nn->client_lock); + +if (atomic_dec_and_test (&clp->cl_rpc_users)) +__put_client_locked (clp, false); +} + +static void put_client_no_renew (struct nfs4_client *clp) +{ +struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); + +if (!atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock)) +return ; +__put_client_locked (clp, false);spin_unlock (&nn->client_lock); }
@@ -1941,9 +1968,7 @@ void nfsd4_revoke_states(struct nfsd_net*/ nn->nfs40_last_revoke =ktime_get_boottime_seconds (); -if (atomic_dec_and_test (&clp->cl_rpc_users) && -is_client_expired (clp)) -wake_up_all (&expiry_wq); +put_client_no_renew_locked (clp); goto retry; } }
@@ -7047,9 +7072,7 @@ retry:nfsd4_drop_revoked_stid (stid);nfs4_put_stid (stid);spin_lock (&nn->client_lock); -if (atomic_dec_and_test (&clp->cl_rpc_users) && -is_client_expired (clp)) -wake_up_all (&expiry_wq); +put_client_no_renew_locked (clp); goto retry; }spin_unlock (&clp->cl_lock);
@@ -7122,15 +7145,7 @@ nfs4_laundromat(struct nfsd_net *nn) clp = dp->dl_stid.sc_client;list_del_init (&dp->dl_recall_lru);revoke_delegation (dp); -/* -* Unpin without renewing: put_client_renew() would -* renew the reaped client's lease. -*/ -if (atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock)) { -if (is_client_expired (clp)) -wake_up_all (&expiry_wq); -spin_unlock (&nn->client_lock); - } +put_client_no_renew (clp); }spin_lock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 551/583 NFSD: Prevent client use-after-free during blocked-lock reaping
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 9026932ac8be4d0ae01db47f23619a98cc57b671 ]
A bare lock owner -- its only remaining reference a blocked lock on nn->blocked_locks_lru -- holds a raw pointer to its nfs4_client but no reference keeping the client alive. When the per-net laundromat reaps such a lock, freeing the nbl drops the owner reference held through flc_owner, and the final nfs4_put_stateowner() takes the client's cl_lock. Because the laundromat detaches the nbl first, __destroy_client() no longer finds it, so a concurrent force_expire_client() can free the client before nfs4_put_stateowner() runs, dereferencing cl_lock in freed memory.
Pin the client with cl_rpc_users before dropping nn->blocked_locks_lock, and skip clients already expiring, whose blocked locks __destroy_client() frees while holding an owner reference. Take nn->client_lock outside nn->blocked_locks_lock. Every other site holds nn->blocked_locks_lock as a leaf, acquiring no further lock, so placing nn->client_lock outside it cannot form a lock-order cycle.
--- fs/nfsd/nfs4state.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +20 -3
@@ -351,6 +351,16 @@ free_blocked_lock(struct nfsd4_blocked_lkref_put (&nbl->nbl_kref, free_nbl); } +/* A blocked lock's flc_owner is its nfs4_lockowner. */ +static struct nfs4_client * +nbl_client (struct nfsd4_blocked_lock *nbl) +{ +struct nfs4_lockowner *lo; + + lo = (struct nfs4_lockowner *)nbl->nbl_lock.c.flc_owner; +return lo->lo_owner.so_client; +} +static void remove_blocked_locks (struct nfs4_lockowner *lo) {
@@ -7175,22 +7185,29 @@ nfs4_laundromat(struct nfsd_net *nn)* indefinitely once the lock does become free. */ BUG_ON (!list_empty (&reaplist)); +spin_lock (&nn->client_lock);spin_lock (&nn->blocked_locks_lock); -while (!list_empty (&nn->blocked_locks_lru)) { - nbl =list_first_entry (&nn->blocked_locks_lru, - struct nfsd4_blocked_lock, nbl_lru); +list_for_each_safe (pos, next, &nn->blocked_locks_lru) { + nbl = list_entry(pos, struct nfsd4_blocked_lock, nbl_lru);if (!state_expired (<, nbl->nbl_time))break ; + clp =nbl_client (nbl); +if (is_client_expired (clp)) +continue ; +atomic_inc (&clp->cl_rpc_users);list_move (&nbl->nbl_lru, &reaplist);list_del_init (&nbl->nbl_list); }spin_unlock (&nn->blocked_locks_lock); +spin_unlock (&nn->client_lock);while (!list_empty (&reaplist)) { nbl =list_first_entry (&reaplist, struct nfsd4_blocked_lock, nbl_lru); + clp =nbl_client (nbl);list_del_init (&nbl->nbl_lru);free_blocked_lock (nbl); +put_client_no_renew (clp); }#ifdef CONFIG_NFSD_V4_2_INTER_SSC /* service the server-to-server copy delayed unmount list */
Greg Kroah-Hartman
maintainerPATCH 552/583 NFSD: Prevent client use-after-free during close_lru reaping
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 2330b788d732f43668b965b3105b37ceb276dfea ]
An nfs4_openowner left on nn->close_lru after its final CLOSE keeps its last closed stateid in oo_last_closed_stid, holding only a raw pointer to its nfs4_client. The laundromat reaps timed-out entries, drops nn->client_lock, and calls nfs4_put_stid(), which dereferences the client through cl_lock. Nothing pins the client across that window, so a concurrent force_expire_client() can free it and nfs4_put_stid() reads freed memory. __destroy_client() hits the same race, walking clp->cl_openowners without cl_lock.
Pin the client with cl_rpc_users before dropping client_lock, and skip clients already expiring. __destroy_client() then cleans up its own close_lru entries through release_last_closed_stateid(), so teardown no longer races the laundromat.
--- fs/nfsd/nfs4state.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/nfsd/nfs4state.c +5 -0
@@ -7165,11 +7165,16 @@ nfs4_laundromat(struct nfsd_net *nn)if (!state_expired (<, oo->oo_time))break ;list_del_init (&oo->oo_close_lru); + clp = oo->oo_owner.so_client; +if (is_client_expired (clp)) +continue ; stp = oo->oo_last_closed_stid; oo->oo_last_closed_stid =NULL ; +atomic_inc (&clp->cl_rpc_users);spin_unlock (&nn->client_lock);nfs4_put_stid (&stp->st_stid);spin_lock (&nn->client_lock); +put_client_no_renew_locked (clp); }spin_unlock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 553/583 zram: fixup read_block_state()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit 8b05d2d8af817c6a1e23032df51e7ad83030d543 ]
ac_time is now in seconds, do not use ktime_to_timespec64()
[ Stable tree note: The ac_time conversion described above is not present
here, so retain the ktime_t conversion and microsecond output. Move the timespec64 temporary into the scan loop instead, allowing the follow-up bounds fix to move nr_pages initialization under init_lock cleanly. ]
Stable-dep-of: 391f057f44a5 ("zram: fix out-of-bounds access in read_block_state()")
--- drivers/block/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/block/zram/zram_drv.c +1 -1
@@ -1136,7 +1136,6 @@ static ssize_t read_block_state(struct fssize_t index, written =0 ;struct zram *zram = file->private_data;unsigned long nr_pages = zram->disksize >>PAGE_SHIFT ; -struct timespec64 ts; kbuf =kvmalloc (count,GFP_KERNEL );if (!kbuf)
@@ -1151,6 +1150,7 @@ static ssize_t read_block_state(struct ffor (index = *ppos; index < nr_pages; index++) {int copied; +struct timespec64 ts;zram_slot_lock (zram, index);if (!zram_allocated (zram, index))
Greg Kroah-Hartman
maintainerPATCH 554/583 zram: fix out-of-bounds access in read_block_state()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Longlong Xia <[email protected]>
[ Upstream commit 391f057f44a51cc9418da5cba78b014324174264 ]
read_block_state() calculates nr_pages before taking dev_lock. If the device is reset and reinitialized with a smaller disksize before lock acquisition, nr_pages still describes the old table. The subsequent loop can then call slot_lock() past the end of the newly allocated table.
Read disksize after acquiring dev_lock and checking that the device is initialized. The read lock then keeps the table and its bound stable for the duration of the scan.
--- drivers/block/zram/zram_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/block/zram/zram_drv.c +3 -1
@@ -1135,7 +1135,7 @@ static ssize_t read_block_state(struct fchar *kbuf;ssize_t index, written =0 ;struct zram *zram = file->private_data; -unsigned long nr_pages = zram->disksize >>PAGE_SHIFT ; +unsigned long nr_pages; kbuf =kvmalloc (count,GFP_KERNEL );if (!kbuf)
@@ -1148,6 +1148,8 @@ static ssize_t read_block_state(struct freturn -EINVAL ; } + nr_pages = zram->disksize >>PAGE_SHIFT ; +for (index = *ppos; index < nr_pages; index++) {int copied;struct timespec64 ts;
Greg Kroah-Hartman
maintainerPATCH 555/583 zram: take write lock in wb limit store handlers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit 7c929664fddfdaaa4afe5ae833d0f3044709d95c ]
Write device attrs handlers should take write zram init_lock. While at it, fixup coding styles.
Stable-dep-of: dde75313eed0 ("zram: set default primary compressor in zram_destroy_comps()")
--- drivers/block/zram/zram_drv.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
drivers/block/zram/zram_drv.c +10 -7
@@ -501,7 +501,8 @@ out:#ifdef CONFIG_ZRAM_WRITEBACK static ssize_t writeback_limit_enable_store (struct device *dev, -struct device_attribute *attr,const char *buf,size_t len) +struct device_attribute *attr, +const char *buf,size_t len) {struct zram *zram =dev_to_zram (dev);u64 val;
@@ -510,18 +511,19 @@ static ssize_t writeback_limit_enable_stif (kstrtoull (buf,10 , &val))return ret; -down_read (&zram->init_lock); +down_write (&zram->init_lock);spin_lock (&zram->wb_limit_lock); zram->wb_limit_enable = val;spin_unlock (&zram->wb_limit_lock); -up_read (&zram->init_lock); +up_write (&zram->init_lock); ret = len;return ret; }static ssize_t writeback_limit_enable_show (struct device *dev, -struct device_attribute *attr,char *buf) +struct device_attribute *attr, +char *buf) {bool val;struct zram *zram =dev_to_zram (dev);
@@ -536,7 +538,8 @@ static ssize_t writeback_limit_enable_sh }static ssize_t writeback_limit_store (struct device *dev, -struct device_attribute *attr,const char *buf,size_t len) +struct device_attribute *attr, +const char *buf,size_t len) {struct zram *zram =dev_to_zram (dev);u64 val;
@@ -545,11 +548,11 @@ static ssize_t writeback_limit_store(strif (kstrtoull (buf,10 , &val))return ret; -down_read (&zram->init_lock); +down_write (&zram->init_lock);spin_lock (&zram->wb_limit_lock); zram->bd_wb_limit = val;spin_unlock (&zram->wb_limit_lock); -up_read (&zram->init_lock); +up_write (&zram->init_lock); ret = len;return ret;
Greg Kroah-Hartman
maintainerPATCH 556/583 zram: drop wb_limit_lock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit a4f506c569e1320c2db4a32955e47961fcf02b05 ]
We don't need wb_limit_lock. Writeback limit setters take an exclusive write zram init_lock, while wb_limit modifications happen only from a single task and under zram read init_lock. No concurrent wb_limit modifications are possible (we permit only one post-processing task at a time). Add lockdep assertions to wb_limit mutators.
While at it, fixup coding styles.
Stable-dep-of: dde75313eed0 ("zram: set default primary compressor in zram_destroy_comps()")
--- drivers/block/zram/zram_drv.c | 23 ++++------------------- drivers/block/zram/zram_drv.h | 1 - 2 files changed, 4 insertions(+), 20 deletions(-)
drivers/block/zram/zram_drv.c +4 -19
@@ -512,9 +512,7 @@ static ssize_t writeback_limit_enable_streturn ret;down_write (&zram->init_lock); -spin_lock (&zram->wb_limit_lock); zram->wb_limit_enable = val; -spin_unlock (&zram->wb_limit_lock);up_write (&zram->init_lock); ret = len;
@@ -529,9 +527,7 @@ static ssize_t writeback_limit_enable_shstruct zram *zram =dev_to_zram (dev);down_read (&zram->init_lock); -spin_lock (&zram->wb_limit_lock); val = zram->wb_limit_enable; -spin_unlock (&zram->wb_limit_lock);up_read (&zram->init_lock);return sysfs_emit (buf,"%d\n" , val);
@@ -549,9 +545,7 @@ static ssize_t writeback_limit_store(strreturn ret;down_write (&zram->init_lock); -spin_lock (&zram->wb_limit_lock); zram->bd_wb_limit = val; -spin_unlock (&zram->wb_limit_lock);up_write (&zram->init_lock); ret = len;
@@ -559,15 +553,13 @@ static ssize_t writeback_limit_store(str }static ssize_t writeback_limit_show (struct device *dev, -struct device_attribute *attr,char *buf) +struct device_attribute *attr,char *buf) {u64 val;struct zram *zram =dev_to_zram (dev);down_read (&zram->init_lock); -spin_lock (&zram->wb_limit_lock); val = zram->bd_wb_limit; -spin_unlock (&zram->wb_limit_lock);up_read (&zram->init_lock);return sysfs_emit (buf,"%llu\n" , val);
@@ -747,18 +739,17 @@ static int zram_writeback_slots(struct zint ret =0 , err;u32 index; +lockdep_assert_held_read (&zram->init_lock); + page =alloc_page (GFP_KERNEL );if (!page)return -ENOMEM ;while ((pps =select_pp_slot (ctl))) { -spin_lock (&zram->wb_limit_lock);if (zram->wb_limit_enable && !zram->bd_wb_limit) { -spin_unlock (&zram->wb_limit_lock); ret = -EIO ;break ; } -spin_unlock (&zram->wb_limit_lock);if (!blk_idx) { blk_idx =alloc_block_bdev (zram);
@@ -824,10 +815,8 @@ static int zram_writeback_slots(struct zzram_set_handle (zram, index, blk_idx); blk_idx =0 ;atomic64_inc (&zram->stats.pages_stored); -spin_lock (&zram->wb_limit_lock);if (zram->wb_limit_enable && zram->bd_wb_limit >0 ) zram->bd_wb_limit -=1UL << (PAGE_SHIFT -12 ); -spin_unlock (&zram->wb_limit_lock); next:zram_slot_unlock (zram, index);release_pp_slot (zram, pps);
@@ -2469,7 +2458,7 @@ static void zram_destroy_comps(struct zrstatic void zram_reset_device (struct zram *zram) { -down_write (&zram->init_lock); +guard (rwsem_write)(&zram->init_lock); zram->limit_pages =0 ;
@@ -2485,7 +2474,6 @@ static void zram_reset_device(struct zrareset_bdev (zram);comp_algorithm_set (zram,ZRAM_PRIMARY_COMP , default_compressor); -up_write (&zram->init_lock); } static ssize_t disksize_store(struct device *dev,
@@ -2697,9 +2685,6 @@ static int zram_add(void) device_id = ret;init_rwsem (&zram->init_lock); -#ifdef CONFIG_ZRAM_WRITEBACK -spin_lock_init (&zram->wb_limit_lock); -#endif /* gendisk structure */ zram->disk =blk_alloc_disk (&lim,NUMA_NO_NODE );
drivers/block/zram/zram_drv.h +0 -1
@@ -127,7 +127,6 @@ struct zram {bool claim;/* Protected by disk->open_mutex */ #ifdef CONFIG_ZRAM_WRITEBACK struct file *backing_dev; -spinlock_t wb_limit_lock;bool wb_limit_enable;u64 bd_wb_limit;struct block_device *bdev;
Greg Kroah-Hartman
maintainerPATCH 557/583 zram: read slot block idx under slot lock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit 1b1a4e4d6797a57fefa40569fc920ce573bbf75b ]
Read slot's block id under slot-lock. We release the slot-lock for bdev read so, technically, slot still can get freed in the meantime, but at least we will read bdev block (page) that holds previous know slot data, not from slot->handle bdev block, which can be anything at that point.
Stable-dep-of: dde75313eed0 ("zram: set default primary compressor in zram_destroy_comps()")
--- drivers/block/zram/zram_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/block/zram/zram_drv.c +3 -3
@@ -1734,14 +1734,14 @@ static int zram_read_page(struct zram *z ret =zram_read_from_zspool (zram, page, index);zram_slot_unlock (zram, index); }else { +unsigned long blk_idx =zram_get_handle (zram, index); +/* * The slot should be unlocked before reading from the backing * device. */ zram_slot_unlock (zram, index); - - ret =read_from_bdev (zram, page,zram_get_handle (zram, index), - parent); + ret =read_from_bdev (zram, page, blk_idx, parent); }/* Should NEVER happen. Return bio error if it does. */
Greg Kroah-Hartman
maintainerPATCH 558/583 zram: fix the issue that the write - back limits might overflow
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuwen Chen <[email protected]>
[ Upstream commit 04d31610a7221cca624646241b1f6b3edd6c99fd ]
When the page size exceeds 4KB, if bd_wb_limit is set to a value that is not aligned with the page size, it will cause a numerical wrap-around issue for bd_wb_limit. For example, when the page size is set to 16KB and bd_wb_limit is set to 3, after one write-back operation, the value of bd_wb_limit will become -1. More seriously, since bd_wb_limit is an unsigned number, its value may become as large as 2^64 - 1.
The core reason for this problem is that the unit of bd_wb_limit is 4KB. For example, when a write-back occurs on a system with a page size of 16KB, 4 needs to be subtracted from bd_wb_limit. This operation takes place in the zram_account_writeback_submit function.
This patch fixes the issue by limiting bd_wb_limit to be an integer multiple of PAGE_SIZE / 4096.
Stable-dep-of: dde75313eed0 ("zram: set default primary compressor in zram_destroy_comps()")
--- drivers/block/zram/zram_drv.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/block/zram/zram_drv.c +10 -0
@@ -544,6 +544,16 @@ static ssize_t writeback_limit_store(strif (kstrtoull (buf,10 , &val))return ret; +/* +* When the page size is greater than 4KB, if bd_wb_limit is set to +* a value that is not page - size aligned, it will cause value +* wrapping. For example, when the page size is set to 16KB and +* bd_wb_limit is set to 3, a single write - back operation will +* cause bd_wb_limit to become -1. Even more terrifying is that +* bd_wb_limit is an unsigned number. +*/ + val =rounddown (val,PAGE_SIZE /4096 ); +down_write (&zram->init_lock); zram->bd_wb_limit = val;up_write (&zram->init_lock);
Greg Kroah-Hartman
maintainerPATCH 559/583 zram: set default primary compressor in zram_destroy_comps()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit dde75313eed0b014c437f48dd75c0308b592cbf9 ]
Patch series "zram: fix zram issues reported by sashiko".
Sashiko drove by and reported [1] a couple of zram issues:
a possible BUG_ON() in zlib code due to missing winbits range validation and one possible NULL-ptr dereference in zcomp. Both are low risk yet still worth fixing.
This patch (of 2):
zram_destroy_comps() resets all compressors and leaves them set to NULL, including the primary one, which is invalid device state, as now comp_algorithm_show()->strcmp() can be called on a NULL compressor. Set default primary compressor in zram_destroy_comps().
--- drivers/block/zram/zram_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/block/zram/zram_drv.c +1 -2
@@ -2464,6 +2464,7 @@ static void zram_destroy_comps(struct zr }zram_comp_params_reset (zram); +comp_algorithm_set (zram,ZRAM_PRIMARY_COMP , default_compressor); }static void zram_reset_device (struct zram *zram)
@@ -2482,8 +2483,6 @@ static void zram_reset_device(struct zramemset (&zram->stats,0 ,sizeof (zram->stats));atomic_set (&zram->pp_in_progress,0 );reset_bdev (zram); - -comp_algorithm_set (zram,ZRAM_PRIMARY_COMP , default_compressor); } static ssize_t disksize_store(struct device *dev,
Greg Kroah-Hartman
maintainerPATCH 560/583 mm: rework compound_head() for power-of-2 sizeof(struct page)
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiryl Shutsemau <[email protected]>
[ Upstream commit 8c846c879e226c312c2c7a7bc1e323779903530f ]
For tail pages, the kernel uses the 'compound_info' field to get to the head page. The bit 0 of the field indicates whether the page is a tail page, and if set, the remaining bits represent a pointer to the head page.
For cases when size of struct page is power-of-2, change the encoding of compound_info to store a mask that can be applied to the virtual address of the tail page in order to access the head page. It is possible because struct page of the head page is naturally aligned with regards to order of the page.
The significant impact of this modification is that all tail pages of the same order will now have identical 'compound_info', regardless of the compound page they are associated with. This paves the way for eliminating fake heads.
The HugeTLB Vmemmap Optimization (HVO) creates fake heads and it is only applied when the sizeof(struct page) is power-of-2. Having identical tail pages allows the same page to be mapped into the vmemmap of all pages, maintaining memory savings without fake heads.
If sizeof(struct page) is not power-of-2, there is no functional changes.
Limit mask usage to HugeTLB vmemmap optimization (HVO) where it makes a difference. The approach with mask would work in the wider set of conditions, but it requires validating that struct pages are naturally aligned for all orders up to the MAX_FOLIO_ORDER, which can be tricky.
[ stable: Drop the original compound_info conversion, which depends on
preparatory interface changes absent from this tree and is not needed by the target fix. Move the stable-only HVO static-key increment ahead of the target's context without changing its behavior. ]
For tail pages, the kernel uses the 'compound_info' field to get to the head page. The bit 0 of the field indicates whether the page is a tail page, and if set, the remaining bits represent a pointer to the head page.
For cases when size of struct page is power-of-2, change the encoding of compound_info to store a mask that can be applied to the virtual address of the tail page in order to access the head page. It is possible because struct page of the head page is naturally aligned with regards to order of the page.
The significant impact of this modification is that all tail pages of the same order will now have identical 'compound_info', regardless of the compound page they are associated with. This paves the way for eliminating fake heads.
The HugeTLB Vmemmap Optimization (HVO) creates fake heads and it is only applied when the sizeof(struct page) is power-of-2. Having identical tail pages allows the same page to be mapped into the vmemmap of all pages, maintaining memory savings without fake heads.
If sizeof(struct page) is not power-of-2, there is no functional changes.
Limit mask usage to HugeTLB vmemmap optimization (HVO) where it makes a difference. The approach with mask would work in the wider set of conditions, but it requires validating that struct pages are naturally aligned for all orders up to the MAX_FOLIO_ORDER, which can be tricky.
[ sashal: Reduced backport -- upstream 8c846c879e226 touches 3 file(s), this
backport carries 1. Not backported here:
include/linux/page-flags.h
mm/slab.h
mm/util.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 6e25c6a9c7ef ("mm/hugetlb_vmemmap: fix __hugetlb_vmemmap_optimize_folios()")
--- mm/hugetlb_vmemmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
mm/hugetlb_vmemmap.c +2 -1
@@ -618,6 +618,8 @@ static void __hugetlb_vmemmap_optimize_funsigned long spfn, epfn;if (boot &&folio_test_hugetlb_vmemmap_optimized (folio)) { +static_branch_inc (&hugetlb_optimize_vmemmap_key); +/* * Already optimized by pre-HVO, just map the * mirrored tail page structs RO.
@@ -629,7 +631,6 @@ static void __hugetlb_vmemmap_optimize_fregister_page_bootmem_memmap (pfn_to_section_nr (spfn), &folio->page,HUGETLB_VMEMMAP_RESERVE_SIZE ); -static_branch_inc (&hugetlb_optimize_vmemmap_key);continue ; }
Greg Kroah-Hartman
maintainerPATCH 561/583 hugetlb: remove VMEMMAP_SYNCHRONIZE_RCU
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiryl Shutsemau <[email protected]>
[ Upstream commit 01b1d0ffb6f7f37f466033cf167959946309ed3d ]
The VMEMMAP_SYNCHRONIZE_RCU flag triggered synchronize_rcu() calls to prevent a race between HVO remapping and page_ref_add_unless(). The race could occur when a speculative PFN walker tried to modify the refcount on a struct page that was in the process of being remapped to a fake head.
With fake heads eliminated, page_ref_add_unless() no longer needs RCU protection.
Remove the flag and synchronize_rcu() calls.
Stable-dep-of: 6e25c6a9c7ef ("mm/hugetlb_vmemmap: fix __hugetlb_vmemmap_optimize_folios()")
--- mm/hugetlb_vmemmap.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-)
mm/hugetlb_vmemmap.c +4 -16
@@ -45,8 +45,6 @@ struct vmemmap_remap_walk {#define VMEMMAP_SPLIT_NO_TLB_FLUSH BIT(0)/* Skip the TLB flush when we remap the PTE */ #define VMEMMAP_REMAP_NO_TLB_FLUSH BIT(1) -/* synchronize_rcu() to avoid writes from page_ref_add_unless() */ -#define VMEMMAP_SYNCHRONIZE_RCU BIT(2)unsigned long flags; };
@@ -407,9 +405,6 @@ static int __hugetlb_vmemmap_restore_folif (!folio_test_hugetlb_vmemmap_optimized (folio))return 0 ; -if (flags &VMEMMAP_SYNCHRONIZE_RCU ) -synchronize_rcu (); - vmemmap_start = (unsigned long )&folio->page; vmemmap_end = vmemmap_start +hugetlb_vmemmap_size (h);
@@ -442,7 +437,7 @@ static int __hugetlb_vmemmap_restore_fol*/ int hugetlb_vmemmap_restore_folio (const struct hstate *h,struct folio *folio) { -return __hugetlb_vmemmap_restore_folio (h, folio,VMEMMAP_SYNCHRONIZE_RCU ); +return __hugetlb_vmemmap_restore_folio (h, folio,0 ); }/**
@@ -465,14 +460,11 @@ long hugetlb_vmemmap_restore_folios(consstruct folio *folio, *t_folio;long restored =0 ;long ret =0 ; -unsigned long flags =VMEMMAP_REMAP_NO_TLB_FLUSH |VMEMMAP_SYNCHRONIZE_RCU ; +unsigned long flags =VMEMMAP_REMAP_NO_TLB_FLUSH ;list_for_each_entry_safe (folio, t_folio, folio_list, lru) {if (folio_test_hugetlb_vmemmap_optimized (folio)) { ret =__hugetlb_vmemmap_restore_folio (h, folio, flags); -/* only need to synchronize_rcu() once for each batch */ - flags &= ~VMEMMAP_SYNCHRONIZE_RCU ; -if (ret)break ; restored++;
@@ -521,8 +513,6 @@ static int __hugetlb_vmemmap_optimize_fostatic_branch_inc (&hugetlb_optimize_vmemmap_key); -if (flags &VMEMMAP_SYNCHRONIZE_RCU ) -synchronize_rcu ();/* * Very Subtle * If VMEMMAP_REMAP_NO_TLB_FLUSH is set, TLB flushing is not performed
@@ -582,7 +572,7 @@ void hugetlb_vmemmap_optimize_folio(cons {LIST_HEAD (vmemmap_pages); -__hugetlb_vmemmap_optimize_folio (h, folio, &vmemmap_pages,VMEMMAP_SYNCHRONIZE_RCU ); +__hugetlb_vmemmap_optimize_folio (h, folio, &vmemmap_pages,0 );free_vmemmap_page_list (&vmemmap_pages); }
@@ -610,7 +600,7 @@ static void __hugetlb_vmemmap_optimize_fstruct folio *folio;int nr_to_optimize;LIST_HEAD (vmemmap_pages); -unsigned long flags =VMEMMAP_REMAP_NO_TLB_FLUSH |VMEMMAP_SYNCHRONIZE_RCU ; +unsigned long flags =VMEMMAP_REMAP_NO_TLB_FLUSH ; nr_to_optimize =0 ;list_for_each_entry (folio, folio_list, lru) {
@@ -664,8 +654,6 @@ static void __hugetlb_vmemmap_optimize_fint ret; ret =__hugetlb_vmemmap_optimize_folio (h, folio, &vmemmap_pages, flags); -/* only need to synchronize_rcu() once for each batch */ - flags &= ~VMEMMAP_SYNCHRONIZE_RCU ;/* * Pages to be freed may have been accumulated. If we
Greg Kroah-Hartman
maintainerPATCH 562/583 mm/hugetlb_vmemmap: fix __hugetlb_vmemmap_optimize_folios()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
[ Upstream commit 6e25c6a9c7eff141ecfc966ecdb7801db2a8810e ]
__hugetlb_vmemmap_optimize_folios() uses incorrect arguments when handling bootmem HugeTLB folios.
The section number passed to register_page_bootmem_memmap() is derived from the vmemmap virtual address of folio->page instead of the folio PFN, so the bootmem memmap metadata can be registered against the wrong section. The helper is also given HUGETLB_VMEMMAP_RESERVE_SIZE even though it expects a page count, not a size in bytes. In addition, the write-protect range is based on pages_per_huge_page(h), which does not cover the full HugeTLB vmemmap area and can leave part of the shared tail vmemmap mapping writable.
Fix the section lookup to use folio_pfn(folio), use HUGETLB_VMEMMAP_RESERVE_PAGES when registering the reserved memmap pages, and use hugetlb_vmemmap_size(h) for the write-protect range.
--- mm/hugetlb_vmemmap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
mm/hugetlb_vmemmap.c +3 -3
@@ -615,12 +615,12 @@ static void __hugetlb_vmemmap_optimize_f* mirrored tail page structs RO. */ spfn = (unsigned long )&folio->page; - epfn = spfn +pages_per_huge_page (h); + epfn = spfn +hugetlb_vmemmap_size (h);vmemmap_wrprotect_hvo (spfn, epfn,folio_nid (folio),HUGETLB_VMEMMAP_RESERVE_SIZE ); -register_page_bootmem_memmap (pfn_to_section_nr (spfn), +register_page_bootmem_memmap (pfn_to_section_nr (folio_pfn (folio)), &folio->page, -HUGETLB_VMEMMAP_RESERVE_SIZE ); +HUGETLB_VMEMMAP_RESERVE_PAGES );continue ; }
Greg Kroah-Hartman
maintainerPATCH 563/583 Docs/ABI/damon: fix typo in intervals_goal sysfs path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Song Hu <[email protected]>
commit 1b492fc82368399123413c937f13da6ed312ee4d upstream.
Patch series "Docs/ABI/damon: sysfs ABI document fixes and additions", v2.
This series fixes typos and fills in missing entries in the DAMON sysfs ABI document (Documentation/ABI/testing/sysfs-kernel-mm-damon).
Patch 1 fixes a path typo, "intrvals_goal" -> "intervals_goal", in four
What: entries; the documented path points to a non-existent directory, so it is Cc'ed to stable.
Patch 2 fixes two further typos ("WDate:", "manimum").
Patches 3 and 4 add ABI entries that exist in the kernel and are already described in usage.rst but are missing from the canonical ABI document:
the 'update_tuned_intervals' state command (patch 3) and the 'tried_regions/<R>/probes/<P>/hits' file (patch 4).
This patch (of 4):
The ABI document spells the DAMON sysfs directory as "intrvals_goal" (missing 'e') in four What: entries, but the kernel creates it as "intervals_goal" (mm/damon/sysfs.c). Following the documented path therefore yields a non-existent directory.
--- Documentation/ABI/testing/sysfs-kernel-mm-damon | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
Documentation/ABI/testing/sysfs-kernel-mm-damon +4 -4
@@ -105,7 +105,7 @@ Description: Writing a value to this fil DAMON context in microseconds as the value. Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/access_bp +What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/access_bp Date: Feb 2025 Contact: SeongJae Park <[email protected]> Description: Writing a value to this file sets the monitoring intervals
@@ -113,7 +113,7 @@ Description: Writing a value to this fil the given time interval (aggrs in same directory), in bp (1/10,000). Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/aggrs +What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/aggrs Date: Feb 2025 Contact: SeongJae Park <[email protected]> Description: Writing a value to this file sets the time interval to achieve
@@ -121,14 +121,14 @@ Description: Writing a value to this fil access events ratio (access_bp in same directory) within. Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/min_sample_us +What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/min_sample_us Date: Feb 2025 Contact: SeongJae Park <[email protected]> Description: Writing a value to this file sets the minimum value of auto-tuned sampling interval in microseconds. Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/max_sample_us +What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/max_sample_us Date: Feb 2025 Contact: SeongJae Park <[email protected]> Description: Writing a value to this file sets the maximum value of
Greg Kroah-Hartman
maintainerPATCH 564/583 power: supply: ab8500_fg: Remove redundant dev_err()/dev_err_probe()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pan Chuang <[email protected]>
[ Upstream commit aa5f4decedfb4fc5cd0fe49ab256ad4304d192e4 ]
The devm_request_threaded_irq() and devm_request_irq() now automatically log detailed error messages on failure. This eliminates the need for driver-specific dev_err() and dev_err_probe() calls that previously printed generic messages.
Stable-dep-of: 75b1e88d3425 ("power: supply: ab8500_fg: fix use-after-free on remove")
--- drivers/power/supply/ab8500_fg.c | 2 -- 1 file changed, 2 deletions(-)
drivers/power/supply/ab8500_fg.c +0 -2
@@ -3178,8 +3178,6 @@ static int ab8500_fg_probe(struct platfo ab8500_fg_irq[i].name, di);if (ret !=0 ) { -dev_err (dev,"failed to request %s IRQ %d: %d\n" , - ab8500_fg_irq[i].name, irq, ret);destroy_workqueue (di->fg_wq);return ret; }
Greg Kroah-Hartman
maintainerPATCH 565/583 power: supply: ab8500_fg: fix use-after-free on remove
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit 75b1e88d34254f4fb7753345e21bfee47abddd7f ]
ab8500_fg_remove() destroys the driver workqueue while the threaded interrupt handlers are still armed; they are devm-managed and freed only after ->remove() returns, so a handler that fires in that window queues work on the freed workqueue.
Tear the workqueue down through devm instead, registering its cleanup after the power supply and before the interrupt requests. devm then frees the interrupts first, so the handlers can no longer queue work, before disabling the delayed and plain work items and destroying the workqueue. Disabling the items, rather than cancelling them, keeps them disabled so no producer (including the power-supply external_power_changed callback) can requeue them.
Found by an in-house static analysis tool.
--- drivers/power/supply/ab8500_fg.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-)
drivers/power/supply/ab8500_fg.c +21 -9
@@ -3054,6 +3054,20 @@ static void ab8500_fg_unbind(struct deviflush_workqueue (di->fg_wq); } +/* Disable, not cancel: works stay disabled so nothing can re-arm them. */ +static void ab8500_fg_destroy_workqueue (void *data) +{ +struct ab8500_fg *di = data; + +disable_work_sync (&di->fg_acc_cur_work); +disable_work_sync (&di->fg_work); +disable_delayed_work_sync (&di->fg_reinit_work); +disable_delayed_work_sync (&di->fg_low_bat_work); +disable_delayed_work_sync (&di->fg_check_hw_failure_work); +disable_delayed_work_sync (&di->fg_periodic_work); +destroy_workqueue (di->fg_wq); +} +static const struct component_ops ab8500_fg_component_ops = { .bind = ab8500_fg_bind, .unbind = ab8500_fg_unbind,
@@ -3155,6 +3169,11 @@ static int ab8500_fg_probe(struct platforeturn PTR_ERR (di->fg_psy); } +/* Registered after fg_psy, before the IRQs: devm frees IRQ -> workqueue -> fg_psy. */ + ret =devm_add_action_or_reset (dev, ab8500_fg_destroy_workqueue, di); +if (ret) +return ret; + di->fg_samples =SEC_TO_SAMPLE (di->bm->fg_params->init_timer);/*
@@ -3167,20 +3186,16 @@ static int ab8500_fg_probe(struct platfo/* Register primary interrupt handlers */ for (i =0 ; i <ARRAY_SIZE (ab8500_fg_irq); i++) { irq =platform_get_irq_byname (pdev, ab8500_fg_irq[i].name); -if (irq <0 ) { -destroy_workqueue (di->fg_wq); +if (irq <0 )return irq; - } ret =devm_request_threaded_irq (dev, irq,NULL , ab8500_fg_irq[i].isr,IRQF_SHARED |IRQF_NO_SUSPEND |IRQF_ONESHOT , ab8500_fg_irq[i].name, di); -if (ret !=0 ) { -destroy_workqueue (di->fg_wq); +if (ret !=0 )return ret; - }dev_dbg (dev,"Requested %s IRQ %d: %d\n" , ab8500_fg_irq[i].name, irq, ret); }
@@ -3194,7 +3209,6 @@ static int ab8500_fg_probe(struct platfo ret =ab8500_fg_sysfs_init (di);if (ret) {dev_err (dev,"failed to create sysfs entry\n" ); -destroy_workqueue (di->fg_wq);return ret; }
@@ -3202,7 +3216,6 @@ static int ab8500_fg_probe(struct platfoif (ret) {dev_err (dev,"failed to create FG psy\n" );ab8500_fg_sysfs_exit (di); -destroy_workqueue (di->fg_wq);return ret; }
@@ -3222,7 +3235,6 @@ static void ab8500_fg_remove(struct plat {struct ab8500_fg *di =platform_get_drvdata (pdev); -destroy_workqueue (di->fg_wq);component_del (&pdev->dev, &ab8500_fg_component_ops);list_del (&di->node);ab8500_fg_sysfs_exit (di);
Greg Kroah-Hartman
maintainerPATCH 566/583 iommu/arm-smmu-v3: Add HAFT support for SVA
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Robin Murphy <[email protected]>
[ Upstream commit f99d3b4bb095c4966cf58154077e8bb69ee4827f ]
Since table access flags cannot be software-managed, if process pagetables are using HAFT then SVA must require the SMMU to support and enable it too, otherwise page aging is liable to get out of whack.
For unbinding, we can't disable HAFT atomically with HA as might be desired, but luckily we can get away with just not disabling HA either.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c | 14 ++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 9 ++++++++- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c +14 -0
@@ -104,6 +104,17 @@ void arm_smmu_make_sva_cd(struct arm_smm target->data[0 ] &=cpu_to_le64 (~(CTXDESC_CD_0_S |CTXDESC_CD_0_R )); } +/* +* Enable Hardware Access and Dirty updates (DBM) if supported. This is +* safe to enable by default, as PTE_WRITE and PTE_DBM share the same bit, +* while the EPD0 config can't get as far as fetching any PTEs anyway. +*/ +if (master->smmu->features &ARM_SMMU_FEAT_HA ) + target->data[0 ] |=cpu_to_le64 (CTXDESC_CD_0_TCR_HA ); +if (master->smmu->features &ARM_SMMU_FEAT_HD ) + target->data[0 ] |=cpu_to_le64 (CTXDESC_CD_0_TCR_HD ); +if (master->smmu->features &ARM_SMMU_FEAT_HAFT &&system_supports_haft ()) + target->data[1 ] |=cpu_to_le64 (CTXDESC_CD_1_HAFT );/* * MAIR value is pretty much constant and global, so we can just get it
@@ -223,6 +234,9 @@ bool arm_smmu_sva_supported(struct arm_sif (system_supports_bbml2_noabort ()) feat_mask |=ARM_SMMU_FEAT_BBML2 ; +if (system_supports_haft ()) + feat_mask |=ARM_SMMU_FEAT_HAFT ; +if ((smmu->features & feat_mask) != feat_mask)return false;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8 -1
@@ -4379,10 +4379,14 @@ static void arm_smmu_device_iidr_probe(sstatic void arm_smmu_get_httu (struct arm_smmu_device *smmu,u32 reg) { -u32 fw_features = smmu->features & (ARM_SMMU_FEAT_HA |ARM_SMMU_FEAT_HD ); +u32 fw_features = smmu->features & (ARM_SMMU_FEAT_HA |ARM_SMMU_FEAT_HD | +ARM_SMMU_FEAT_HAFT );u32 hw_features =0 ;switch (FIELD_GET (IDR0_HTTU , reg)) { +case IDR0_HTTU_ACCESS_DIRTY_HAFT : + hw_features |=ARM_SMMU_FEAT_HAFT ; + fallthrough;case IDR0_HTTU_ACCESS_DIRTY : hw_features |=ARM_SMMU_FEAT_HD ; fallthrough;
@@ -4692,6 +4696,9 @@ static int arm_smmu_device_acpi_probe(st smmu->features |=ARM_SMMU_FEAT_COHERENCY ;switch (FIELD_GET (ACPI_IORT_SMMU_V3_HTTU_OVERRIDE , iort_smmu->flags)) { +case IDR0_HTTU_ACCESS_DIRTY_HAFT : + smmu->features |=ARM_SMMU_FEAT_HAFT ; + fallthrough;case IDR0_HTTU_ACCESS_DIRTY : smmu->features |=ARM_SMMU_FEAT_HD ; fallthrough;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -0
@@ -40,6 +40,7 @@ struct arm_vsmmu;#define IDR0_HTTU GENMASK(7, 6)#define IDR0_HTTU_ACCESS 1#define IDR0_HTTU_ACCESS_DIRTY 2 +#define IDR0_HTTU_ACCESS_DIRTY_HAFT 3#define IDR0_COHACC (1 << 4)#define IDR0_TTF GENMASK(3, 2)#define IDR0_TTF_AARCH64 2
@@ -370,6 +371,7 @@ static inline unsigned int arm_smmu_cdta#define CTXDESC_CD_0_ASET (1UL << 47)#define CTXDESC_CD_0_ASID GENMASK_ULL(63, 48) +#define CTXDESC_CD_1_HAFT (1UL << 3)#define CTXDESC_CD_1_TTB0_MASK GENMASK_ULL(51, 4)/*
@@ -769,6 +771,7 @@ struct arm_smmu_device {#define ARM_SMMU_FEAT_HD (1 << 22)#define ARM_SMMU_FEAT_S2FWB (1 << 23)#define ARM_SMMU_FEAT_BBML2 (1 << 24) +#define ARM_SMMU_FEAT_HAFT (1 << 25)u32 features;#define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
Greg Kroah-Hartman
maintainerPATCH 567/583 mm/damon/tests/core-kunit: catch test failure in test_merge_regions_of()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit b640708929aa956235866bf7717d89018c661083 upstream.
KUNIT_EXPECT_EQ() does not abort the execution of test code when the expectation is not met. But damon_test_merge_regions_of() code after its initial KUNIT_EXPECT_EQ() call assumes the expectation is met. It does a per-region test with a hard-coded number of regions that is correct only if the expectation was met. As a result, __nth_region_of() could return NULL, and the test code can dereference NULL pointers. Fix the issue by catching the expectation failure and skip the per-region tests.
The user impact on realistic setups should be negligible, as it is a unit test.
The issue was discovered [1] by Sashiko.
--- mm/damon/tests/core-kunit.h | 3 +++ 1 file changed, 3 insertions(+)
mm/damon/tests/core-kunit.h +3 -0
@@ -269,11 +269,14 @@ static void damon_test_merge_regions_of(damon_merge_regions_of (t,9 ,9999 );/* 0-112, 114-130, 130-156, 156-170 */ KUNIT_EXPECT_EQ (test,damon_nr_regions (t),5u ); +if (damon_nr_regions (t) !=5 ) + goto out;for (i =0 ; i <5 ; i++) { r =__nth_region_of (t, i);KUNIT_EXPECT_EQ (test, r->ar.start, saddrs[i]);KUNIT_EXPECT_EQ (test, r->ar.end, eaddrs[i]); } +out:damon_free_target (t); }
Greg Kroah-Hartman
maintainerPATCH 568/583 mm/damon/vaddr: drop last same folio access check optimization
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 831846078caa14b7d00b2ccca8b8fe522afe3204 upstream.
The optimization can race when multiple kdamonds are running. Meanwhile, the impact of the optimization is quite doubtful. Just remove it.
The user impact of the issue should be quite trivial. After all, the race can happen only when the user intentionally setup DAMON in the way. Even if it happens, it would be rare and only degrade the best-effort monitoring results. No critical consequences like kernel panic or memory corruption happen.
The race possibility was discovered [1] by Sashiko.
--- mm/damon/vaddr.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-)
mm/damon/vaddr.c +6 -28
@@ -431,8 +431,6 @@ static void damon_va_prepare_access_chec }struct damon_young_walk_private { -/* size of the folio for the access checked virtual memory address */ -unsigned long *folio_sz;bool young; };
@@ -468,7 +466,6 @@ static int damon_young_pmd_entry(pmd_t *mmu_notifier_test_young (walk->mm, addr)) priv->young = true; - *priv->folio_sz =HPAGE_PMD_SIZE ;folio_put (folio); huge_out:spin_unlock (ptl);
@@ -490,7 +487,6 @@ regular_page:if (pte_young (ptent) || !folio_test_idle (folio) ||mmu_notifier_test_young (walk->mm, addr)) priv->young = true; - *priv->folio_sz =folio_size (folio);folio_put (folio); out:pte_unmap_unlock (pte, ptl);
@@ -519,7 +515,6 @@ static int damon_young_hugetlb_entry(pteif (pte_young (entry) || !folio_test_idle (folio) ||mmu_notifier_test_young (walk->mm, addr)) priv->young = true; - *priv->folio_sz =huge_page_size (h);folio_put (folio);
@@ -537,11 +532,9 @@ static const struct mm_walk_ops damon_yo .walk_lock = PGWALK_RDLOCK, }; -static bool damon_va_young (struct mm_struct *mm,unsigned long addr, -unsigned long *folio_sz) +static bool damon_va_young (struct mm_struct *mm,unsigned long addr) {struct damon_young_walk_private arg = { - .folio_sz = folio_sz, .young = false, };
@@ -558,29 +551,18 @@ static bool damon_va_young(struct mm_str* r the region to be checked */ static void __damon_va_check_access (struct mm_struct *mm, -struct damon_region *r,bool same_target, +struct damon_region *r,struct damon_attrs *attrs) { -static unsigned long last_addr; -static unsigned long last_folio_sz =PAGE_SIZE ; -static bool last_accessed; +bool accessed;if (!mm) {damon_update_region_access_rate (r, false, attrs);return ; } -/* If the region is in the last checked page, reuse the result */ -if (same_target && (ALIGN_DOWN (last_addr, last_folio_sz) == -ALIGN_DOWN (r->sampling_addr, last_folio_sz))) { -damon_update_region_access_rate (r, last_accessed, attrs); -return ; - } - - last_accessed =damon_va_young (mm, r->sampling_addr, &last_folio_sz); -damon_update_region_access_rate (r, last_accessed, attrs); - - last_addr = r->sampling_addr; + accessed =damon_va_young (mm, r->sampling_addr); +damon_update_region_access_rate (r, accessed, attrs); }static unsigned int damon_va_check_accesses (struct damon_ctx *ctx)
@@ -589,16 +571,12 @@ static unsigned int damon_va_check_accesstruct mm_struct *mm;struct damon_region *r;unsigned int max_nr_accesses =0 ; -bool same_target;damon_for_each_target (t, ctx) { mm =damon_get_mm (t); - same_target = false;damon_for_each_region (r, t) { -__damon_va_check_access (mm, r, same_target, - &ctx->attrs); +__damon_va_check_access (mm, r, &ctx->attrs); max_nr_accesses =max (r->nr_accesses, max_nr_accesses); - same_target = true; }if (mm)mmput (mm);
Greg Kroah-Hartman
maintainerPATCH 569/583 mm/damon/paddr: drop last same folio access check reuse optimization
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit f23f0aa62b2f32c2b12f95959fc4603ef81678b2 upstream.
It can race when multiple kdamonds are being used. The problem from the race is doubtful, but the gain from the optimization is also doubtful. Simply drop the optimization in favor of code simplicity.
The user impact is doubtfully trivial. After all, this kind of interference can happen only by intentional user setup. Even if it happens, it will be rare, and the consequence is degradation of the best-effort monitoring results. No critical consequences like kernel panic or memory corruption happen.
The race was discovered [1] by Sashiko.
--- mm/damon/paddr.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-)
mm/damon/paddr.c +4 -16
@@ -67,7 +67,7 @@ static void damon_pa_prepare_access_chec } } -static bool damon_pa_young (phys_addr_t paddr,unsigned long *folio_sz) +static bool damon_pa_young (phys_addr_t paddr) {struct folio *folio =damon_get_folio (PHYS_PFN (paddr));bool accessed;
@@ -76,7 +76,6 @@ static bool damon_pa_young(phys_addr_t preturn false; accessed =damon_folio_young (folio); - *folio_sz =folio_size (folio);folio_put (folio);return accessed; }
@@ -84,23 +83,12 @@ static bool damon_pa_young(phys_addr_t pstatic void __damon_pa_check_access (struct damon_region *r,struct damon_attrs *attrs,unsigned long addr_unit) { -static phys_addr_t last_addr; -static unsigned long last_folio_sz =PAGE_SIZE ; -static bool last_accessed; +bool accessed;phys_addr_t sampling_addr =damon_pa_phys_addr ( r->sampling_addr, addr_unit); -/* If the region is in the last checked page, reuse the result */ -if (ALIGN_DOWN (last_addr, last_folio_sz) == -ALIGN_DOWN (sampling_addr, last_folio_sz)) { -damon_update_region_access_rate (r, last_accessed, attrs); -return ; - } - - last_accessed =damon_pa_young (sampling_addr, &last_folio_sz); -damon_update_region_access_rate (r, last_accessed, attrs); - - last_addr = sampling_addr; + accessed =damon_pa_young (sampling_addr); +damon_update_region_access_rate (r, accessed, attrs); }static unsigned int damon_pa_check_accesses (struct damon_ctx *ctx)
Greg Kroah-Hartman
maintainerPATCH 570/583 mm/damon/ops-common: use nr_accesses moving sum for quota score
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 6c25083f7ae7e2660d766169e5b4d3e96010503f upstream.
Since commit 42f994b71404 ("mm/damon/core: implement scheme-specific apply interval"), DAMOS scheme can be applied at any time. At that time, nr_accesses may not be fully aggregated. But the quota prioritization score is calculated using the not fully aggregated count. As a result, the performance of DAMOS could be degraded. Fix by using damon_nr_accesses_mvsum() instead.
The user impact of the issue is suboptimum DAMOS performance under certain setups. Nonetheless, the bug was there from the beginning of the setup availability. In other words, the suboptimum performance is the baseline of the setup and hence it didn't cause regression. Also the extent of the suboptimality was not big enough to be found from users and testers. Still, this is a clear bug that is better to be fixed, and can be easily fixed.
--- mm/damon/ops-common.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
mm/damon/ops-common.c +3 -2
@@ -101,8 +101,9 @@ int damon_hot_score(struct damon_ctx *c,unsigned int age_weight = s->quota.weight_age;int hotness; - freq_subscore = r->nr_accesses *DAMON_MAX_SUBSCORE / -damon_max_nr_accesses (&c->attrs); + freq_subscore =mult_frac (r->nr_accesses_bp /10000 , +DAMON_MAX_SUBSCORE , +damon_max_nr_accesses (&c->attrs)); age_in_sec = (unsigned long )r->age * c->attrs.aggr_interval /1000000 ; for (age_in_log = 0; age_in_log < DAMON_MAX_AGE_IN_LOG && age_in_sec;
Greg Kroah-Hartman
maintainerPATCH 571/583 mm/damon/core: skip aging from repeated aggressive merging
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 0250dbe08c730d003ef9f484da56ae09a1ea0c4c upstream.
The number of DAMON regions could temporarily exceed the user-defined maximum number of regions limit for corner cases. For example, users could lower the limit via runtime parameters update. For such a case, kdamond_merge_regions() repeats merging regions in the case doubling the merge threshold. The repeated merge operation could update the age of regions multiple times. This corrupts the monitoring results. Fix the issue by asking the merge operation to skip aging for the corner case.
The user impact is degradation of the monitoring quality. The impact should be mild, since the degradation is only temporal, and it is not common to happen in realistic setups.
The issue was discovered [1,2] by Sashiko.
--- mm/damon/core.c | 21 +++++++++++++-------- mm/damon/tests/core-kunit.h | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-)
mm/damon/core.c +13 -8
@@ -2364,17 +2364,19 @@ static void damon_merge_two_regions(stru* sz_limit size upper limit of each region */ static void damon_merge_regions_of (struct damon_target *t,unsigned int thres, -unsigned long sz_limit) +unsigned long sz_limit,bool count_age) {struct damon_region *r, *prev =NULL , *next;damon_for_each_region_safe (r, next, t) { -if (abs (r->nr_accesses - r->last_nr_accesses) > thres) - r->age =0 ; -else if ((r->nr_accesses ==0 ) != (r->last_nr_accesses ==0 )) - r->age =0 ; -else - r->age++; +if (count_age) { +if (abs (r->nr_accesses - r->last_nr_accesses) > thres) + r->age =0 ; +else if ((r->nr_accesses ==0 ) != (r->last_nr_accesses ==0 )) + r->age =0 ; +else + r->age++; + } if (prev && prev->ar.end == r->ar.start && abs(prev->nr_accesses - r->nr_accesses) <= thres &&
@@ -2408,15 +2410,18 @@ static void kdamond_merge_regions(structstruct damon_target *t;unsigned int nr_regions;unsigned int max_thres; +bool count_age = true; max_thres = c->attrs.aggr_interval / (c->attrs.sample_interval ? c->attrs.sample_interval :1 );do { nr_regions =0 ;damon_for_each_target (t, c) { -damon_merge_regions_of (t, threshold, sz_limit); +damon_merge_regions_of (t, threshold, sz_limit, + count_age); nr_regions +=damon_nr_regions (t); } + count_age = false; threshold =max (1 , threshold *2 ); }while (nr_regions > c->attrs.max_nr_regions && threshold /2 < max_thres);
mm/damon/tests/core-kunit.h +1 -1
@@ -266,7 +266,7 @@ static void damon_test_merge_regions_of(damon_add_region (r, t); } -damon_merge_regions_of (t,9 ,9999 ); +damon_merge_regions_of (t,9 ,9999 , true);/* 0-112, 114-130, 130-156, 156-170 */ KUNIT_EXPECT_EQ (test,damon_nr_regions (t),5u );if (damon_nr_regions (t) !=5 )
Greg Kroah-Hartman
maintainerPATCH 572/583 mm/damon/sysfs: read addr_unit only once in damon_sysfs_apply_inputs()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit ab4d9358e32316fa39b5f1f5360292135978c3d9 upstream.
damon_sysfs_apply_inputs() reads addr_unit twice. It could race with addr_unit_store(). As a result, the min_region_sz could wrongly be set up. Read it once.
The user impact is trivial. Sane users ain't update the parameter in parallel. Even if it happens, the DAMON core layer handles the wrong min_region_sz (!is_power_of_2()). Even if somehow the race ended up making a min_region_sz that is different from the user's intention but still valid, only monitoring itself runs differently than expected. No critical consequences like kernel panic or memory corruption happen.
The issue was discovered [1] by Sashiko.
--- mm/damon/sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
mm/damon/sysfs.c +2 -2
@@ -1445,11 +1445,11 @@ static int damon_sysfs_apply_inputs(stru err =damon_select_ops (ctx, sys_ctx->ops_id);if (err)return err; - ctx->addr_unit = sys_ctx->addr_unit; + ctx->addr_unit =READ_ONCE (sys_ctx->addr_unit);/* addr_unit is respected by only DAMON_OPS_PADDR */ if (sys_ctx->ops_id ==DAMON_OPS_PADDR ) ctx->min_sz_region =max ( -DAMON_MIN_REGION / sys_ctx->addr_unit,1 ); +DAMON_MIN_REGION / ctx->addr_unit,1 ); err =damon_sysfs_set_attrs (ctx, sys_ctx->attrs);if (err)return err;
Greg Kroah-Hartman
maintainerPATCH 573/583 mm/damon/core-kunit: handle region split failure in filter_out()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 3423fe70395210e2f0cd795270292f6a27cd57b5 upstream.
damos_test_filter_out() test checks if damos_filter_match() of an address filter splits the region as expected under a given condition. But, the test continued regardless of the split successes. As a result, the later part of the test could dereference invalid pointers that returned from damon_next_region(). Further, it could corrupt memory from damon_destroy_region().
The consequent user impact (memory corruption) is quite bad. The realistic user impact would be limited, though. It would affect only test run setups.
Fix it by exiting early for the number of regions test failure.
The issue was discovered [1] by Sashiko.
--- mm/damon/tests/core-kunit.h | 5 +++++ 1 file changed, 5 insertions(+)
mm/damon/tests/core-kunit.h +5 -0
@@ -596,6 +596,8 @@ static void damos_test_filter_out(structKUNIT_EXPECT_EQ (test, r->ar.start,DAMON_MIN_REGION *1 );KUNIT_EXPECT_EQ (test, r->ar.end,DAMON_MIN_REGION *2 );KUNIT_EXPECT_EQ (test,damon_nr_regions (t),2 ); +if (damon_nr_regions (t) !=2 ) + goto out; r2 =damon_next_region (r);KUNIT_EXPECT_EQ (test, r2->ar.start,DAMON_MIN_REGION *2 );KUNIT_EXPECT_EQ (test, r2->ar.end,DAMON_MIN_REGION *4 );
@@ -610,11 +612,14 @@ static void damos_test_filter_out(structKUNIT_EXPECT_EQ (test, r->ar.start,DAMON_MIN_REGION *2 );KUNIT_EXPECT_EQ (test, r->ar.end,DAMON_MIN_REGION *6 );KUNIT_EXPECT_EQ (test,damon_nr_regions (t),2 ); +if (damon_nr_regions (t) !=2 ) + goto out; r2 =damon_next_region (r);KUNIT_EXPECT_EQ (test, r2->ar.start,DAMON_MIN_REGION *6 );KUNIT_EXPECT_EQ (test, r2->ar.end,DAMON_MIN_REGION *8 );damon_destroy_region (r2, t); +out:damon_free_target (t);damos_free_filter (f); }
Greg Kroah-Hartman
maintainerPATCH 574/583 mm/damon/core: initialize damos->last_applied
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit af5f76aeb9c9aa9c77d5e1d94e0ee4618c805239 upstream.
Patch series "mm/damon: fix uninitialized DAMOS field and kunit exec expectation bugs".
Fix a few Sashiko-found unurgent bugs. Patch 1 fixes use of uninitialized damos->last_applied field. Patches 2-7 fix DAMON kunit tests that do invalid memory access under test failures.
The bugs are better to be fixed and eventually merged into stable@ kernel. That said, the fixes are arguably not urgent. Patch 1 only introduces negligible DAMOS efficiency degradation in occasional cases. Kunit fixes could introduce quite bad consequences but those are test code that affect only test run setups.
This patch (of 7):
Multiple DAMON regions could exist across a folio. If they fulfill the condition to apply a DAMOS scheme, the scheme could be applied multiple times to the folio. To avoid this, each DAMOS scheme stores the folio that the scheme was applied to last time in the damos->last_applied field and skips repeatedly applying the same scheme to the same folio.
The field is being used without initialization, though. Hence, the mechanism could wrongly skip applying a scheme to a folio at the very first time of DAMOS run.
The user impact is trivial. DAMON might unexpectedly skip applying DAMOS action for one folio for the first time per scheme. In the DAMON's best-effort world, this is never a real problem. No critical consequences such as kernel panic or memory corruption happen.
It is a clear bug, though, and the fix is straightforward. Fix the issue by initializing the field in DAMOS scheme creation function, damon_new_scheme().
The issue was discovered [1] by Sashiko.
--- mm/damon/core.c | 1 + 1 file changed, 1 insertion(+)
mm/damon/core.c +1 -0
@@ -412,6 +412,7 @@ struct damos *damon_new_scheme(struct daINIT_LIST_HEAD (&scheme->filters);INIT_LIST_HEAD (&scheme->ops_filters); scheme->stat = (struct damos_stat ){}; + scheme->last_applied =NULL ;INIT_LIST_HEAD (&scheme->list); scheme->quota = *(damos_quota_init (quota));
Greg Kroah-Hartman
maintainerPATCH 575/583 mm: thp: introduce folio_split_queue_lock and its variants
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
[ Upstream commit ad7c7f4576a5977b4ec4ac5dd090ab3f81ca7c6f ]
In future memcg removal, the binding between a folio and a memcg may change, making the split lock within the memcg unstable when held.
A new approach is required to reparent the split queue to its parent. This patch starts introducing a unified way to acquire the split lock for future work.
It's a code-only refactoring with no functional changes.
Stable-dep-of: c299a2285d9d ("mm/huge_memory: use folio's memcg inside __folio_split()")
--- include/linux/memcontrol.h | 10 +++ mm/huge_memory.c | 119 +++++++++++++++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 35 deletions(-)
include/linux/memcontrol.h +10 -0
@@ -1674,6 +1674,11 @@ int alloc_shrinker_info(struct mem_cgrouvoid free_shrinker_info (struct mem_cgroup *memcg);void set_shrinker_bit (struct mem_cgroup *memcg,int nid,int shrinker_id);void reparent_shrinker_deferred (struct mem_cgroup *memcg); + +static inline int shrinker_id (struct shrinker *shrinker) +{ +return shrinker->id; +}#else #define mem_cgroup_sockets_enabled 0
@@ -1705,6 +1710,11 @@ static inline void set_shrinker_bit(stru int nid, int shrinker_id) { } + +static inline int shrinker_id (struct shrinker *shrinker) +{ +return -1 ; +}#endif #ifdef CONFIG_MEMCG
mm/huge_memory.c +84 -35
213 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 576/583 mm: thp: use folio_batch to handle THP splitting in deferred_split_scan()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
[ Upstream commit 776bde7caf80f6af72b087cafe7d9f607b14716d ]
The maintenance of the folio->_deferred_list is intricate because it's reused in a local list.
Here are some peculiarities:
1) When a folio is removed from its split queue and added to a local on-stack list in deferred_split_scan(), the ->split_queue_len isn't updated, leading to an inconsistency between it and the actual number of folios in the split queue.
2) When the folio is split via split_folio() later, it's removed from the local list while holding the split queue lock. At this time, the lock is not needed as it is not protecting anything.
3) To handle the race condition with a third-party freeing or migrating the preceding folio, we must ensure there's always one safe (with raised refcount) folio before by delaying its folio_put(). More details can be found in commit e66f3185fa04 ("mm/thp: fix deferred split queue not partially_mapped"). It's rather tricky.
We can use the folio_batch infrastructure to handle this clearly. In this case, ->split_queue_len will be consistent with the real number of folios in the split queue. If list_empty(&folio->_deferred_list) returns false, it's clear the folio must be in its split queue (not in a local list anymore).
In the future, we will reparent LRU folios during memcg offline to eliminate dying memory cgroups, which requires reparenting the split queue to its parent first. So this patch prepares for using folio_split_queue_lock_irqsave() as the memcg may change then.
Stable-dep-of: c299a2285d9d ("mm/huge_memory: use folio's memcg inside __folio_split()")
--- mm/huge_memory.c | 134 +++++++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 66 deletions(-)
mm/huge_memory.c +68 -66
@@ -3688,19 +3688,21 @@ static int __folio_split(struct folio *fbool is_anon =folio_test_anon (folio);struct address_space *mapping =NULL ;struct anon_vma *anon_vma =NULL ; -int order =folio_order (folio); +int old_order =folio_order (folio);struct folio *new_folio, *next; +int extra_pins;int nr_shmem_dropped =0 ; -int remap_flags =0 ; -int extra_pins, ret; -pgoff_t end; -bool is_hzp; +enum ttu_flags ttu_flags =0 ; +pgoff_t end =0 ; +int ret;VM_WARN_ON_ONCE_FOLIO (!folio_test_locked (folio), folio);VM_WARN_ON_ONCE_FOLIO (!folio_test_large (folio), folio); -if (folio !=page_folio (split_at) || folio !=page_folio (lock_at)) -return -EINVAL ; +if (folio !=page_folio (split_at) || folio !=page_folio (lock_at)) { + ret = -EINVAL ; + goto out; + }/* * Folios that just got truncated cannot get split. Signal to the
@@ -3712,14 +3714,12 @@ static int __folio_split(struct folio *fif (!is_anon && !folio->mapping)return -EBUSY ; -if (new_order >=folio_order (folio)) -return -EINVAL ; - -if (!folio_split_supported (folio, new_order, uniform_split,/* warn = */ true)) -return -EINVAL ; +if (new_order >= old_order) { + ret = -EINVAL ; + goto out; + } - is_hzp =is_huge_zero_folio (folio); -if (is_hzp) { +if (is_huge_zero_folio (folio)) {pr_warn_ratelimited ("Called split_huge_page for huge zero page\n" );return -EBUSY ; }
@@ -3727,6 +3727,13 @@ static int __folio_split(struct folio *fif (folio_test_writeback (folio))return -EBUSY ; + ret =folio_split_supported (folio, new_order, uniform_split, +/* warns = */ false) ?0 : -EINVAL ; +if (ret) { +VM_WARN_ONCE (ret == -EINVAL ,"Tried to split an unsplittable folio" ); + goto out; + } +if (is_anon) {/* * The caller does not necessarily hold an mmap_lock that would
@@ -3819,21 +3826,22 @@ static int __folio_split(struct folio *fstruct lruvec *lruvec;int expected_refs; -if (folio_order (folio) >1 && - !list_empty (&folio->_deferred_list)) { - ds_queue->split_queue_len--; +if (folio_order (folio) >1 ) { +if (!list_empty (&folio->_deferred_list)) { + ds_queue->split_queue_len--; +/* +* Reinitialize page_deferred_list after removing the +* page from the split_queue, otherwise a subsequent +* split will see list corruption when checking the +* page_deferred_list. +*/ +list_del_init (&folio->_deferred_list); + }if (folio_test_partially_mapped (folio)) {folio_clear_partially_mapped (folio);mod_mthp_stat (folio_order (folio),MTHP_STAT_NR_ANON_PARTIALLY_MAPPED ,-1 ); } -/* -* Reinitialize page_deferred_list after removing the -* page from the split_queue, otherwise a subsequent -* split will see list corruption when checking the -* page_deferred_list. -*/ -list_del_init (&folio->_deferred_list); }split_queue_unlock (ds_queue);if (mapping) {
@@ -3943,8 +3951,8 @@ fail:shmem_uncharge (mapping->host, nr_shmem_dropped);if (!ret && is_anon) - remap_flags =RMP_USE_SHARED_ZEROPAGE ; -remap_page (folio,1 << order, remap_flags); + ttu_flags = (enum ttu_flags )RMP_USE_SHARED_ZEROPAGE ; +remap_page (folio,1 << old_order, ttu_flags);/* * Drop the mapping while the inode is still pinned. @folio stays
@@ -3987,9 +3995,9 @@ out_unlock:i_mmap_unlock_read (mapping); out:xas_destroy (&xas); -if (order ==HPAGE_PMD_ORDER ) +if (old_order ==HPAGE_PMD_ORDER )count_vm_event (!ret ?THP_SPLIT_PAGE :THP_SPLIT_PAGE_FAILED ); -count_mthp_stat (order, !ret ?MTHP_STAT_SPLIT :MTHP_STAT_SPLIT_FAILED ); +count_mthp_stat (old_order, !ret ?MTHP_STAT_SPLIT :MTHP_STAT_SPLIT_FAILED );return ret; }
@@ -4232,35 +4240,40 @@ static unsigned long deferred_split_scan {struct deferred_split *ds_queue;unsigned long flags; -LIST_HEAD (list); -struct folio *folio, *next, *prev =NULL ; -int split =0 , removed =0 ; +struct folio *folio, *next; +int split =0 , i; +struct folio_batch fbatch; + +folio_batch_init (&fbatch); +retry: ds_queue =split_queue_lock_irqsave (sc->nid, sc->memcg, &flags);/* Take pin on all head pages to avoid freeing them under us */ list_for_each_entry_safe (folio, next, &ds_queue->split_queue, _deferred_list) {if (folio_try_get (folio)) { -list_move (&folio->_deferred_list, &list); - }else { +folio_batch_add (&fbatch, folio); + }else if (folio_test_partially_mapped (folio)) {/* We lost race with folio_put() */ -if (folio_test_partially_mapped (folio)) { -folio_clear_partially_mapped (folio); -mod_mthp_stat (folio_order (folio), -MTHP_STAT_NR_ANON_PARTIALLY_MAPPED ,-1 ); - } -list_del_init (&folio->_deferred_list); - ds_queue->split_queue_len--; +folio_clear_partially_mapped (folio); +mod_mthp_stat (folio_order (folio), +MTHP_STAT_NR_ANON_PARTIALLY_MAPPED ,-1 ); } +list_del_init (&folio->_deferred_list); + ds_queue->split_queue_len--;if (!--sc->nr_to_scan)break ; +if (!folio_batch_space (&fbatch)) +break ; }split_queue_unlock_irqrestore (ds_queue, flags); -list_for_each_entry_safe (folio, next, &list, _deferred_list) { +for (i =0 ; i <folio_batch_count (&fbatch); i++) {bool did_split = false;bool underused = false; +struct deferred_split *fqueue; + folio = fbatch.folios[i];if (!folio_test_partially_mapped (folio)) {/* * See try_to_map_unused_to_zeropage(): we cannot
@@ -4283,39 +4296,28 @@ static unsigned long deferred_split_scan }folio_unlock (folio); next: +if (did_split || !folio_test_partially_mapped (folio)) +continue ;/* -* split_folio() removes folio from list on success. * Only add back to the queue if folio is partially mapped. * If thp_underused returns false, or if split_folio fails * in the case it was underused, then consider it used and * don't add it back to split_queue. */ -if (did_split) { - ;/* folio already removed from list */ - }else if (!folio_test_partially_mapped (folio)) { -list_del_init (&folio->_deferred_list); - removed++; - }else { -/* -* That unlocked list_del_init() above would be unsafe, -* unless its folio is separated from any earlier folios -* left on the list (which may be concurrently unqueued) -* by one safe folio with refcount still raised. -*/ -swap (folio, prev); - } -if (folio) -folio_put (folio); + fqueue =folio_split_queue_lock_irqsave (folio, &flags); +if (list_empty (&folio->_deferred_list)) { +list_add_tail (&folio->_deferred_list, &fqueue->split_queue); + fqueue->split_queue_len++; + } +split_queue_unlock_irqrestore (fqueue, flags); + } +folios_put (&fbatch); + +if (sc->nr_to_scan && !list_empty (&ds_queue->split_queue)) { +cond_resched (); + goto retry; } -spin_lock_irqsave (&ds_queue->split_queue_lock, flags); -list_splice_tail (&list, &ds_queue->split_queue); - ds_queue->split_queue_len -= removed; -spin_unlock_irqrestore (&ds_queue->split_queue_lock, flags); - -if (prev) -folio_put (prev); -/* * Stop shrinker if we didn't split any page, but the queue is empty. * This can happen if pages were freed under us.
Greg Kroah-Hartman
maintainerPATCH 577/583 mm/huge_memory: change folio_split_supported() to folio_check_splittable()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zi Yan <[email protected]>
[ Upstream commit bdd0d69a32c2aa6437d23e35acc705758b835a75 ]
Patch series "Improve folio split related functions", v4.
This patchset improves several folio split related functions to avoid future misuse. The changes are:
1. Consolidated folio splittable checks by moving truncated folio check, huge zero folio check, and writeback folio check into folio_split_supported(). Changed the function return type. Renamed it to folio_check_splittable() for clarification.
2. Replaced can_split_folio() with open coded folio_expected_ref_count() and folio_ref_count() and introduced folio_cache_ref_count().
3. Changed min_order_for_split() to always return an order.
4. Fixed folio split stats counting.
Motivation
==========
This is based on Wei's observation[1] and solves several potential issues:
1. Dereferencing NULL folio->mapping in try_folio_split_to_order() if it is called on truncated folios.
2. Not handling of negative return value of min_order_for_split() in mm/memory-failure.c
There is no bug in the current code.
This patch (of 4):
folio_split_supported() used in try_folio_split_to_order() requires folio->mapping to be non NULL, but current try_folio_split_to_order() does not check it. There is no issue in the current code, since try_folio_split_to_order() is only used in truncate_inode_partial_folio(), where folio->mapping is not NULL.
To prevent future misuse, move folio->mapping NULL check (i.e., folio is truncated) into folio_split_supported(). Since folio->mapping NULL check returns -EBUSY and folio_split_supported() == false means -EINVAL, change folio_split_supported() return type from bool to int and return error numbers accordingly. Rename folio_split_supported() to folio_check_splittable() to match the return type change.
While at it, move is_huge_zero_folio() check and folio_test_writeback() check into folio_check_splittable() and add kernel-doc.
Remove all warnings inside folio_check_splittable() and give warnings in __folio_split() instead, so that bool warns parameter can be removed.
Stable-dep-of: c299a2285d9d ("mm/huge_memory: use folio's memcg inside __folio_split()")
--- include/linux/huge_mm.h | 6 ++-- mm/huge_memory.c | 72 ++++++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 35 deletions(-)
include/linux/huge_mm.h +3 -3
@@ -369,8 +369,8 @@ int split_huge_page_to_list_to_order(str unsigned int new_order);int min_order_for_split (struct folio *folio);int split_folio_to_list (struct folio *folio,struct list_head *list); -bool folio_split_supported (struct folio *folio,unsigned int new_order, -bool uniform_split,bool warns); +int folio_check_splittable (struct folio *folio,unsigned int new_order, +bool uniform_split);int folio_split (struct folio *folio,unsigned int new_order,struct page *page,struct list_head *list);/*
@@ -390,7 +390,7 @@ int folio_split(struct folio *folio, unsstatic inline int try_folio_split_to_order (struct folio *folio,struct page *page,unsigned int new_order) { -if (!folio_split_supported (folio, new_order, false,/* warns= */ false)) +if (folio_check_splittable (folio, new_order, false))return split_huge_page_to_list_to_order (&folio->page,NULL , new_order);return folio_split (folio, new_order, page,NULL );
mm/huge_memory.c +40 -32
107 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 578/583 mm/huge_memory: replace can_split_folio() with direct refcount calculation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zi Yan <[email protected]>
[ Upstream commit 5842bcbfc316738cbfcbdb4def5a7592aa03ebf2 ]
can_split_folio() is just a refcount comparison, making sure only the split caller holds an extra pin. Open code it with folio_expected_ref_count() != folio_ref_count() - 1. For the extra_pins used by folio_ref_freeze(), add folio_cache_ref_count() to calculate it. Also replace folio_expected_ref_count() with folio_cache_ref_count() used by folio_ref_unfreeze(), since they are returning the same values when a folio is frozen and folio_cache_ref_count() does not have unnecessary folio_mapcount() in its implementation.
Stable-dep-of: c299a2285d9d ("mm/huge_memory: use folio's memcg inside __folio_split()")
--- include/linux/huge_mm.h | 1 - mm/huge_memory.c | 35 ++++++++++++----------------------- mm/vmscan.c | 3 ++- 3 files changed, 14 insertions(+), 25 deletions(-)
include/linux/huge_mm.h +0 -1
@@ -364,7 +364,6 @@ unsigned long thp_get_unmapped_area_vmfl unsigned long len, unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags); -bool can_split_folio (struct folio *folio,int caller_pins,int *pextra_pins);int split_huge_page_to_list_to_order (struct page *page,struct list_head *list,unsigned int new_order);int min_order_for_split (struct folio *folio);
mm/huge_memory.c +12 -23
@@ -3328,23 +3328,6 @@ static void lru_add_split_folio(struct f } } -/* Racy check whether the huge page can be split */ -bool can_split_folio (struct folio *folio,int caller_pins,int *pextra_pins) -{ -int extra_pins; - -/* Additional pins from page cache */ -if (folio_test_anon (folio)) - extra_pins =folio_test_swapcache (folio) ? -folio_nr_pages (folio) :0 ; -else - extra_pins =folio_nr_pages (folio); -if (pextra_pins) - *pextra_pins = extra_pins; -return folio_mapcount (folio) ==folio_ref_count (folio) - extra_pins - - caller_pins; -} -static bool page_range_has_hwpoisoned (struct page *page,long nr_pages) {for (; nr_pages; page++, nr_pages--)
@@ -3717,7 +3700,6 @@ static int __folio_split(struct folio *fstruct anon_vma *anon_vma =NULL ;int old_order =folio_order (folio);struct folio *new_folio, *next; -int extra_pins;int nr_shmem_dropped =0 ;enum ttu_flags ttu_flags =0 ;pgoff_t end =0 ;
@@ -3805,7 +3787,7 @@ static int __folio_split(struct folio *f* Racy check if we can split the page, before unmap_folio() will * split PMDs */ -if (!can_split_folio (folio,1 , &extra_pins)) { +if (folio_expected_ref_count (folio) !=folio_ref_count (folio) -1 ) { ret = -EAGAIN ; goto out_unlock; }
@@ -3829,7 +3811,10 @@ static int __folio_split(struct folio *f/* Prevent deferred_split_scan() touching ->_refcount */ ds_queue =folio_split_queue_lock (folio); -if (folio_ref_freeze (folio,1 + extra_pins)) { +if (folio_ref_freeze (folio, + (folio_test_anon (folio) && + !folio_test_swapcache (folio) ? +0 :folio_nr_pages (folio)) +1 )) {struct swap_cluster_info *ci =NULL ;struct lruvec *lruvec;int expected_refs;
@@ -3898,7 +3883,9 @@ static int __folio_split(struct folio *f next =folio_next (new_folio); - expected_refs =folio_expected_ref_count (new_folio) +1 ; + expected_refs = (folio_test_anon (new_folio) && + !folio_test_swapcache (new_folio) ? +0 :folio_nr_pages (new_folio)) +1 ;folio_ref_unfreeze (new_folio, expected_refs);lru_add_split_folio (folio, new_folio, lruvec, list);
@@ -3938,7 +3925,9 @@ static int __folio_split(struct folio *f* Otherwise, a parallel folio_try_get() can grab @folio * and its caller can see stale page cache entries. */ - expected_refs =folio_expected_ref_count (folio) +1 ; + expected_refs = (folio_test_anon (folio) && + !folio_test_swapcache (folio) ? +0 :folio_nr_pages (folio)) +1 ;folio_ref_unfreeze (folio, expected_refs);unlock_page_lruvec (lruvec);
@@ -4467,7 +4456,7 @@ static int split_huge_pages_pid(int pid,* can be split or not. So skip the check here. */ if (!folio_test_private (folio) && - !can_split_folio (folio,0 ,NULL )) +folio_expected_ref_count (folio) !=folio_ref_count (folio)) goto next;if (!folio_trylock (folio))
mm/vmscan.c +2 -1
@@ -1312,7 +1312,8 @@ retry: goto keep_locked;if (folio_test_large (folio)) {/* cannot split folio, skip it */ -if (!can_split_folio (folio,1 ,NULL )) +if (folio_expected_ref_count (folio) != +folio_ref_count (folio) -1 ) goto activate_locked;/* * Split partially mapped folios right away.
Greg Kroah-Hartman
maintainerPATCH 579/583 migrate: replace RMP_ flags with TTU_ flags
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Matthew Wilcox (Oracle)" <[email protected]>
[ Upstream commit 832d95b5314eea558cf4cc9ca40db10122ce8f63 ]
Instead of translating between RMP_ and TTU_ flags, remove the RMP_ flags and just use the TTU_ flag space; there's plenty available.
Possibly we should rename these to RMAP_ flags, and maybe even pass them in through rmap_walk_arg, but that can be done later.
Stable-dep-of: c299a2285d9d ("mm/huge_memory: use folio's memcg inside __folio_split()")
--- include/linux/rmap.h | 9 +++------ mm/huge_memory.c | 6 +++--- mm/migrate.c | 12 ++++++------ 3 files changed, 12 insertions(+), 15 deletions(-)
include/linux/rmap.h +3 -6
@@ -92,6 +92,7 @@ struct anon_vma_chain { };enum ttu_flags { +TTU_USE_SHARED_ZEROPAGE =0x2 ,/* for unused pages of large folios */ TTU_SPLIT_HUGE_PMD =0x4 ,/* split huge PMD if any */ TTU_IGNORE_MLOCK =0x8 ,/* ignore mlock */ TTU_SYNC =0x10 ,/* avoid racy checks with PVMW_SYNC */
@@ -1000,12 +1001,8 @@ int mapping_wrprotect_range(struct addreint pfn_mkclean_range (unsigned long pfn,unsigned long nr_pages,pgoff_t pgoff,struct vm_area_struct *vma); -enum rmp_flags { -RMP_LOCKED =1 <<0 , -RMP_USE_SHARED_ZEROPAGE =1 <<1 , -}; - -void remove_migration_ptes (struct folio *src,struct folio *dst,int flags); +void remove_migration_ptes (struct folio *src,struct folio *dst, +enum ttu_flags flags);/* * rmap_walk_control: To control rmap traversing for specific needs
mm/huge_memory.c +3 -3
@@ -3298,7 +3298,7 @@ static void remap_page(struct folio *folif (!folio_test_anon (folio))return ;for (;;) { -remove_migration_ptes (folio, folio,RMP_LOCKED | flags); +remove_migration_ptes (folio, folio,TTU_RMAP_LOCKED | flags); i +=folio_nr_pages (folio);if (i >= nr)break ;
@@ -3702,8 +3702,8 @@ static int __folio_split(struct folio *fstruct folio *new_folio, *next;int nr_shmem_dropped =0 ;enum ttu_flags ttu_flags =0 ; -pgoff_t end =0 ;int ret; +pgoff_t end =0 ;VM_WARN_ON_ONCE_FOLIO (!folio_test_locked (folio), folio);VM_WARN_ON_ONCE_FOLIO (!folio_test_large (folio), folio);
@@ -3948,7 +3948,7 @@ fail:shmem_uncharge (mapping->host, nr_shmem_dropped);if (!ret && is_anon) - ttu_flags = (enum ttu_flags )RMP_USE_SHARED_ZEROPAGE ; + ttu_flags =TTU_USE_SHARED_ZEROPAGE ;remap_page (folio,1 << old_order, ttu_flags);/*
mm/migrate.c +6 -6
@@ -451,11 +451,12 @@ static bool remove_migration_pte(struct* Get rid of all migration entries and replace them by * references to the indicated page. */ -void remove_migration_ptes (struct folio *src,struct folio *dst,int flags) +void remove_migration_ptes (struct folio *src,struct folio *dst, +enum ttu_flags flags) {struct rmap_walk_arg rmap_walk_arg = { .folio = src, - .map_unused_to_zeropage = flags &RMP_USE_SHARED_ZEROPAGE , + .map_unused_to_zeropage = flags &TTU_USE_SHARED_ZEROPAGE , };struct rmap_walk_control rwc = {
@@ -463,9 +464,9 @@ void remove_migration_ptes(struct folio .arg = &rmap_walk_arg, }; -VM_BUG_ON_FOLIO ((flags &RMP_USE_SHARED_ZEROPAGE ) && (src != dst), src); +VM_BUG_ON_FOLIO ((flags &TTU_USE_SHARED_ZEROPAGE ) && (src != dst), src); -if (flags &RMP_LOCKED ) +if (flags &TTU_RMAP_LOCKED )rmap_walk_locked (dst, &rwc);else rmap_walk (dst, &rwc);
@@ -1530,8 +1531,7 @@ static int unmap_and_move_huge_page(new_ rc =move_to_new_folio (dst, src, mode);if (page_was_mapped) -remove_migration_ptes (src, !rc ? dst : src, - ttu ?RMP_LOCKED :0 ); +remove_migration_ptes (src, !rc ? dst : src, ttu);if (ttu &TTU_RMAP_LOCKED )i_mmap_unlock_write (mapping);
Greg Kroah-Hartman
maintainerPATCH 580/583 mm/huge_memory: use folios memcg inside __folio_split()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zi Yan <[email protected]>
[ Upstream commit c299a2285d9d8bda4da024455de65e3d00de6f17 ]
Patch series "Honor XA_FLAGS_ACCOUNT in xas_split_alloc() and charge to folio's memcg", v3.
__GFP_ACCOUNT is needed for xarray node allocation accounting when XA_FLAGS_ACCOUNT is set. Commit 7b785645e8f13 ("mm: fix page cache convergence regression") fixed a workingset regression with it. xas_split_alloc() does not have it and needs to be fixed.
In addition, based on Sashiko's review[1] and Johannes' confirmation[2], to charge the right memcg, folio's memcg needs to be active during folio split. Add that before adding __GFP_ACCOUNT.
There is no workingset convergence regression related to missing __GFP_ACCOUNT in xas_split_alloc() and the impact to userspace should be minor.
This patch (of 2):
During a pagecache folio split, an xarray node allocation can happen and needs to charge at folio's memcg instead of folio split invoker's memcg, because for example folio split can happen during reclaim and reclaim's active memcg might not be folio's memcg. Switch to folio's memcg at the beginning and switch back afterwards.
--- mm/huge_memory.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
mm/huge_memory.c +16 -4
@@ -3696,34 +3696,42 @@ static int __folio_split(struct folio *fXA_STATE (xas, &folio->mapping->i_pages, folio->index);struct folio *end_folio =folio_next (folio);bool is_anon =folio_test_anon (folio); +struct mem_cgroup *memcg, *old_memcg;struct address_space *mapping =NULL ;struct anon_vma *anon_vma =NULL ;int old_order =folio_order (folio);struct folio *new_folio, *next;int nr_shmem_dropped =0 ;enum ttu_flags ttu_flags =0 ; -int ret;pgoff_t end =0 ; +int ret;VM_WARN_ON_ONCE_FOLIO (!folio_test_locked (folio), folio);VM_WARN_ON_ONCE_FOLIO (!folio_test_large (folio), folio);if (folio !=page_folio (split_at) || folio !=page_folio (lock_at)) { ret = -EINVAL ; - goto out; + goto out_no_memcg; }if (new_order >= old_order) { ret = -EINVAL ; - goto out; + goto out_no_memcg; } ret =folio_check_splittable (folio, new_order, uniform_split);if (ret) {VM_WARN_ONCE (ret == -EINVAL ,"Tried to split an unsplittable folio" ); - goto out; + goto out_no_memcg; } +/* +* switch to folio's memcg as xarray node allocation can happen and +* needs to charge to it. +*/ + memcg =get_mem_cgroup_from_folio (folio); + old_memcg =set_active_memcg (memcg); +if (is_anon) {/* * The caller does not necessarily hold an mmap_lock that would
@@ -3991,6 +3999,10 @@ out_unlock:if (mapping)i_mmap_unlock_read (mapping); out: +/* restore to caller's old_memcg */ +set_active_memcg (old_memcg); +mem_cgroup_put (memcg); +out_no_memcg:xas_destroy (&xas);if (old_order ==HPAGE_PMD_ORDER )count_vm_event (!ret ?THP_SPLIT_PAGE :THP_SPLIT_PAGE_FAILED );
Greg Kroah-Hartman
maintainerPATCH 581/583 pidfs: protect PIDFD_GET_* ioctls() via ifdef
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 75ddaa4ddc86d31edb15e50152adf4ddee77a6ba upstream.
We originally protected PIDFD_GET_<ns-type>_NAMESPACE ioctls() through ifdefs and recent rework made it possible to drop them. There was an oversight though. When the relevant namespace is turned off ns->ops will be NULL so even though opening a file descriptor is perfectly legitimate it would fail during inode eviction when the file was closed.
The simple fix would be to check ns->ops for NULL and continue allow to retrieve namespace fds from pidfds but we don't allow retrieving them when the relevant namespace type is turned off. So keep the simplification but add the ifdefs back in.
--- fs/pidfs.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
fs/pidfs.c +18 -0
@@ -511,14 +511,18 @@ static long pidfd_ioctl(struct file *filswitch (cmd) {/* Namespaces that hang of nsproxy. */ case PIDFD_GET_CGROUP_NAMESPACE : +#ifdef CONFIG_CGROUPS if (!ns_ref_get (nsp->cgroup_ns))break ; ns_common =to_ns_common (nsp->cgroup_ns); +#endif break ;case PIDFD_GET_IPC_NAMESPACE : +#ifdef CONFIG_IPC_NS if (!ns_ref_get (nsp->ipc_ns))break ; ns_common =to_ns_common (nsp->ipc_ns); +#endif break ;case PIDFD_GET_MNT_NAMESPACE :if (!ns_ref_get (nsp->mnt_ns))
@@ -526,32 +530,43 @@ static long pidfd_ioctl(struct file *fil ns_common =to_ns_common (nsp->mnt_ns);break ;case PIDFD_GET_NET_NAMESPACE : +#ifdef CONFIG_NET_NS if (!ns_ref_get (nsp->net_ns))break ; ns_common =to_ns_common (nsp->net_ns); +#endif break ;case PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE : +#ifdef CONFIG_PID_NS if (!ns_ref_get (nsp->pid_ns_for_children))break ; ns_common =to_ns_common (nsp->pid_ns_for_children); +#endif break ;case PIDFD_GET_TIME_NAMESPACE : +#ifdef CONFIG_TIME_NS if (!ns_ref_get (nsp->time_ns))break ; ns_common =to_ns_common (nsp->time_ns); +#endif break ;case PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE : +#ifdef CONFIG_TIME_NS if (!ns_ref_get (nsp->time_ns_for_children))break ; ns_common =to_ns_common (nsp->time_ns_for_children); +#endif break ;case PIDFD_GET_UTS_NAMESPACE : +#ifdef CONFIG_UTS_NS if (!ns_ref_get (nsp->uts_ns))break ; ns_common =to_ns_common (nsp->uts_ns); +#endif break ;/* Namespaces that don't hang of nsproxy. */ case PIDFD_GET_USER_NAMESPACE : +#ifdef CONFIG_USER_NS scoped_guard (rcu) {struct user_namespace *user_ns;
@@ -560,8 +575,10 @@ static long pidfd_ioctl(struct file *filbreak ; ns_common =to_ns_common (user_ns); } +#endif break ;case PIDFD_GET_PID_NAMESPACE : +#ifdef CONFIG_PID_NS scoped_guard (rcu) {struct pid_namespace *pid_ns;
@@ -570,6 +587,7 @@ static long pidfd_ioctl(struct file *filbreak ; ns_common = to_ns_common(pid_ns); } +#endif break ;default : error = -ENOIOCTLCMD ;
Greg Kroah-Hartman
maintainerPATCH 582/583 mm/hugetlb_vmemmap: fix incorrect vmemmap restore in rollback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
commit c7bde43f6daf70e05a64fbca7efdf6fa93e057dc upstream.
vmemmap_restore_pte() rebuilds restored vmemmap pages from a tail-page template derived from compound_head(). This is wrong when the current PTE already maps a page whose contents are not tail-page metadata.
In the rollback path of vmemmap_remap_free(), the first restored PTE is backed by vmemmap_head and contains head-page metadata. Reconstructing that page from a tail-page template overwrites the head-page state and corrupts the restored vmemmap page.
Fix this by copying the full page from the page currently mapped by the PTE. Also pass vmemmap_tail to the rollback walk so only PTEs backed by the shared tail page are restored, while the head PTE remains mapped to vmemmap_head. Add VM_WARN_ON_ONCE() checks for unexpected cases.
--- mm/hugetlb_vmemmap.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-)
mm/hugetlb_vmemmap.c +18 -18
@@ -205,6 +205,8 @@ static void vmemmap_remap_pte(pte_t *pte/* Remapping the head page requires r/w */ if (unlikely (walk->nr_walked ==0 && walk->vmemmap_head)) { +VM_WARN_ON_ONCE (!PageHead ((const struct page *)addr)); +list_del (&walk->vmemmap_head->lru);/*
@@ -216,6 +218,8 @@ static void vmemmap_remap_pte(pte_t *pte entry =mk_pte (walk->vmemmap_head,PAGE_KERNEL ); }else { +VM_WARN_ON_ONCE (!PageTail ((const struct page *)addr)); +/* * Remap the tail pages as read-only to catch illegal write * operation to the tail pages.
@@ -230,33 +234,28 @@ static void vmemmap_remap_pte(pte_t *ptestatic void vmemmap_restore_pte (pte_t *pte,unsigned long addr,struct vmemmap_remap_walk *walk) { -struct page *page; -struct page *from, *to; - - page = list_first_entry(walk->vmemmap_pages, struct page, lru); -list_del (&page->lru); +struct page *src =pte_page (ptep_get (pte)), *dst;/* -* Initialize tail pages in the newly allocated vmemmap page. -* -* There is folio-scope metadata that is encoded in the first few -* tail pages. -* -* Use the value last tail page in the page with the head page -* to initialize the rest of tail pages. +* When rolling back vmemmap_remap_free(), keep the copied head page +* mapping and restore only PTEs currently pointing at the shared tail +* page. */ - from =compound_head ((struct page *)addr) + -PAGE_SIZE /sizeof (struct page ) -1 ; - to =page_to_virt (page); -for (int i =0 ; i <PAGE_SIZE /sizeof (struct page ); i++, to++) - *to = *from; +if (walk->vmemmap_tail && walk->vmemmap_tail != src) +return ; + +VM_WARN_ON_ONCE (PageHead ((const struct page *)addr)); + + dst = list_first_entry(walk->vmemmap_pages, struct page, lru); +list_del (&dst->lru); +copy_page (page_to_virt (dst),page_to_virt (src));/* * Makes sure that preceding stores to the page contents become visible * before the set_pte_at() write. */ smp_wmb (); -set_pte_at (&init_mm, addr, pte,mk_pte (page,PAGE_KERNEL )); +set_pte_at (&init_mm, addr, pte,mk_pte (dst,PAGE_KERNEL )); }/**
@@ -322,6 +321,7 @@ static int vmemmap_remap_free(unsigned l*/ walk = (struct vmemmap_remap_walk ) { .remap_pte = vmemmap_restore_pte, + .vmemmap_tail = vmemmap_tail, .vmemmap_pages = vmemmap_pages, .flags =0 , };
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.18.51-rc1-g4cfd5b1359b0 #1 SMP PREEMPT_DYNAMIC Wed Sep 9 15:50:25 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
CIP testing did not find any problems here:
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-6.18.y
Best regards,
Pavel
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Fri, 11 Sep 2026 13:40:31 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.51-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian
Build tested in our x86,arm64,loongarch,riscv config successfully without error.
Best Regards
Wentao Guan
Log:
Linux version 6.18.51-rc1-g4cfd5b1359b0 (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC Linux version 6.18.51-rc1-g4cfd5b1359b0 (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #2 SMP PREEMPT_DYNAMIC Wed Sep 9 23:55:33 CST 2026 Linux version 6.18.51-rc1-g4cfd5b1359b0 (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC Linux version 6.18.51-rc1-g4cfd5b1359b0 (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #3 SMP PREEMPT_DYNAMIC Thu Sep 10 00:10:35 CST 2026 Linux version 6.18.51-rc1+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT
Linux version 6.18.51-rc1+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #4 SMP PREEMPT Thu Sep 10 00:25:26 CST 2026 Linux version 6.18.51-rc1-g4cfd5b1359b0 (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin18) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC
Linux version 6.18.51-rc1-g4cfd5b1359b0 (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin18) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #1 SMP PREEMPT_DYNAMIC Wed Sep 9 23:33:53 CST 2026
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Fri, 11 Sep 2026 13:40:31 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.51-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Hi Greg,
Quoted 20 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Fri, 11 Sep 2026 13:40:31 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.51-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
-------------
Pseudo-Shortlog of commits:
...
Quoted 9 lines
Peter Ujfalusi <[email protected]>
ASoC:tegra210_i2s: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC:tegra210_i2s: sort the Tegra264 register default tableJon Hunter <[email protected]>
ASoC: tegra: Fix the MIXER enable default value
Quoted 8 lines
Jon Hunter <[email protected]>
ASoC: tegra: Sort ADMAIF register defaultsJon Hunter <[email protected]>
ASoC: tegra: Sort MBDRC register defaultsPeter Ujfalusi <[email protected]>
ASoC:tegra210_mixer: sort the register default table
These are causing some audio regressions for Tegra ...
Test results for next:
builds: 11 pass, 0 fail
boots: 26 pass, 0 fail, aborted 1
tests: 139 pass, 15 fail, aborted 1Linux version: 6.18.51-rc1-g4cfd5b1359b0
Boards tested: tegra20-ventana
tegra30-cardhu-a04
tegra124-jetson-tk1
tegra210-p2371-2180
tegra210-p3450-0000
tegra186-p2771-0000
tegra186-p3509-0000+p3636-0001
tegra194-p2972-0000
tegra194-p3509-0000+p3668-0000
tegra234-p3737-0000+p3701-0000
tegra234-p3768-0000+p3767-0005Test failures: tegra186-p2771-0000: tegra-audio-loopback-testsuite.sh
tegra186-p2771-0000: tegra-audio-mixer-testsuite.sh
tegra194-p2972-0000: tegra-audio-loopback-testsuite.sh
tegra194-p2972-0000: tegra-audio-loopback-testsuite.sh (rt565x loopback)
tegra194-p2972-0000: tegra-audio-mixer-testsuite.sh
tegra194-p3509-0000+p3668-0000: tegra-audio-loopback-testsuite.sh
tegra194-p3509-0000+p3668-0000: tegra-audio-mixer-testsuite.sh
tegra210-p2371-2180: tegra-audio-loopback-testsuite.sh
tegra210-p2371-2180: tegra-audio-mixer-testsuite.sh
tegra210-p3450-0000: tegra-audio-loopback-testsuite.sh
tegra210-p3450-0000: tegra-audio-mixer-testsuite.shThis tells me that there is some other dependency missing. My intention was to only backport these for v7.2 because audio is broken in that release for Tegra. Sorry if I screwed up an fixes tags here.
I see this is pulling in a bunch of other related patches, but I am not sure if these should be pulled in either. Adding Peter if he has any comments.
Quoted 78 lines
Peter Ujfalusi <[email protected]>
ASoC: tas2783-sdw: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: tas2783-sdw: drop duplicatereg_defaultentryPeter Ujfalusi <[email protected]>
ASoC: tas2780: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: tas2764: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: tas2552: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: sti-sas: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: sgtl5000: sort the register default tableWenTao Liang <[email protected]>
ASoC: samsung:aries_audio_probe: doubleof_node_putdue to direct assignment withoutof_node_getPeter Ujfalusi <[email protected]>
ASoC: rt721-sdca-sdw: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt715: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt715-sdca: sort the register default tablesPeter Ujfalusi <[email protected]>
ASoC: rt715-sdca: drop duplicatereg_defaultentriesPeter Ujfalusi <[email protected]>
ASoC: rt712-sdca-sdw: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt712-sdca-dmic: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt711: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt711-sdca: sort the register default tablesPeter Ujfalusi <[email protected]>
ASoC: rt700: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt700: drop duplicatereg_defaultentryPeter Ujfalusi <[email protected]>
ASoC: rt298: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt286: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt274: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt1318: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt1318-sdw: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt1316-sdw: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: rt1017-sdca-sdw: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: pm4125-sdw: sort the register default table
Quoted 8 lines
Peter Ujfalusi <[email protected]>
ASoC: pcm512x: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: ml26124: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC: max9860: sort the register default table
Jon
Signature · 1 line
nvpublic
Greg Kroah-Hartman
maintainerQuoted 76 lines · Jon Hunter
Hi Greg,
Quoted 20 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Fri, 11 Sep 2026 13:40:31 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.51-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
-------------
Pseudo-Shortlog of commits:...
Quoted 9 lines
Peter Ujfalusi <[email protected]>
ASoC:tegra210_i2s: sort the register default tablePeter Ujfalusi <[email protected]>
ASoC:tegra210_i2s: sort the Tegra264 register default tableJon Hunter <[email protected]>
ASoC: tegra: Fix the MIXER enable default valueQuoted 8 lines
Jon Hunter <[email protected]>
ASoC: tegra: Sort ADMAIF register defaultsJon Hunter <[email protected]>
ASoC: tegra: Sort MBDRC register defaultsPeter Ujfalusi <[email protected]>
ASoC:tegra210_mixer: sort the register default tableThese are causing some audio regressions for Tegra ...
Test results for next:
builds: 11 pass, 0 fail boots: 26 pass, 0 fail, aborted 1 tests: 139 pass, 15 fail, aborted 1Linux version: 6.18.51-rc1-g4cfd5b1359b0
Boards tested: tegra20-ventanategra30-cardhu-a04 tegra124-jetson-tk1 tegra210-p2371-2180 tegra210-p3450-0000 tegra186-p2771-0000 tegra186-p3509-0000+p3636-0001 tegra194-p2972-0000 tegra194-p3509-0000+p3668-0000 tegra234-p3737-0000+p3701-0000 tegra234-p3768-0000+p3767-0005Test failures: tegra186-p2771-0000:
tegra-audio-loopback-testsuite.shtegra186-p2771-0000: tegra-audio-mixer-testsuite.sh tegra194-p2972-0000: tegra-audio-loopback-testsuite.sh tegra194-p2972-0000: tegra-audio-loopback-testsuite.sh (rt565x loopback) tegra194-p2972-0000: tegra-audio-mixer-testsuite.sh tegra194-p3509-0000+p3668-0000: tegra-audio-loopback-testsuite.sh tegra194-p3509-0000+p3668-0000: tegra-audio-mixer-testsuite.sh tegra210-p2371-2180: tegra-audio-loopback-testsuite.sh tegra210-p2371-2180: tegra-audio-mixer-testsuite.sh tegra210-p3450-0000: tegra-audio-loopback-testsuite.sh tegra210-p3450-0000: tegra-audio-mixer-testsuite.sh
Quoted 8 lines
This tells me that there is some other dependency missing. My intention was to only backport these for v7.2 because audio is broken in that release for Tegra. Sorry if I screwed up an fixes tags here.
I see this is pulling in a bunch of other related patches, but I am not sure if these should be pulled in either. Adding Peter if he has any comments.
All of them had specific Fixes: tags on them, which I followed, are they incorrect?
I can drop all of the "sort the register table" patches if you want from all branches, just let me know.
thanks,
greg k-h
On 10/09/2026 07:12, Greg Kroah-Hartman wrote:
...
Quoted 11 lines
Quoted 8 lines
This tells me that there is some other dependency missing. My intention was to only backport these for v7.2 because audio is broken in that release for Tegra. Sorry if I screwed up an fixes tags here.
I see this is pulling in a bunch of other related patches, but I am not sure if these should be pulled in either. Adding Peter if he has any comments.
All of them had specific Fixes: tags on them, which I followed, are they incorrect?
No but there are some other fixes which are also needed ...
53dd1c1de851 ASoC: tegra: Fix the I2S enable default value 5442b8093a2f ASoC: tegra: Fix the MIXER enable default value
Sasha's review also caught the above.
Quoted 2 lines
I can drop all of the "sort the register table" patches if you want from all branches, just let me know.
Yes, I think that is best to drop them unless Peter thinks otherwise.
Jon
Signature · 1 line
nvpublic
Sasha Levin
maintainerQuoted 4 lines
Quoted 2 lines
I can drop all of the "sort the register table" patches if you want from all branches, just let me know.
Yes, I think that is best to drop them unless Peter thinks otherwise.
Dropped both again from 6.18: "ASoC: tegra: Fix the MIXER enable default value" and its "sort the register table" dependency, "ASoC: tegra210_mixer: sort the register default table".
Signature · 2 lines
Thanks, Sasha
Sasha Levin
maintainerQuoted 5 lines
These are causing some audio regressions for Tegra ...
Test failures: tegra186-p2771-0000:
tegra-audio-loopback-testsuite.shtegra186-p2771-0000: tegra-audio-mixer-testsuite.sh[...]
Dropped all six Tegra "sort register defaults" patches from every branch where they were queued:
- "ASoC: tegra: Sort ADMAIF register defaults" -- 6.18
- "ASoC: tegra: Fix the MIXER enable default value" -- 6.18, 6.12, 6.6, 6.1
- "ASoC: tegra210_i2s: sort the register default table" -- 6.18, 6.12,
6.6, 6.1, 5.15, 5.10
- "ASoC: tegra210_i2s: sort the Tegra264 register default table" -- 6.18
- "ASoC: tegra: Sort MBDRC register defaults" -- 6.18, 6.12, 6.6, 6.1
- "ASoC: tegra210_mixer: sort the register default table" -- 6.18, 6.12,
6.6, 6.1Quoted 2 lines
My intention was to only backport these for v7.2 because audio is broken in that release for Tegra.
Makes sense - and while digging into the I2S case specifically, we found the root cause: your companion fix 53dd1c1de851 ("ASoC: tegra: Fix the I2S enable default value") is queued in no stable branch. Once the I2S sort patch makes TEGRA210_I2S_ENABLE reachable via regcache's bsearch, the wrong recorded default is left in place with nothing to correct it or re-enable I2S at runtime_resume.
Quoted 2 lines
I see this is pulling in a bunch of other related patches, but I am not sure if these should be pulled in either.
We also acted on that wider concern. We reviewed the whole "sort the register default table" family (all the non-Tegra codecs in this series: adau1761, cx2072x, fsl_easrc, max9860, ml26124, pcm512x, pm4125-sdw, the rt* SoundWire codecs, sgtl5000, sti-sas, tas2552/2764/ 2780/2783-sdw) and are dropping it from 6.18 and older across the board. These patches accompany regcache commit 4b05ccb17f92 ("regcache: Sort the local copy of an unsorted reg_defaults array"), which none of those branches have, so sorting the driver-side table is itself what changes bsearch reachability in regcache_lookup_reg(), the same mechanism behind the I2S regression above, but with no reported bug to fix on these branches. Risk with no benefit, so out they go.
Thanks for flagging this.
Signature · 2 lines
Thanks, Sasha
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Fri, 11 Sep 2026 13:40:31 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.18.51-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.18.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Am 09.09.2026 um 15:34 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.18.51 release. There are 583 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697v2 server. No dmesg oddities or regressions found.
Beste Grüße,
Peter Schneider
Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge, enjoy the air and behold the view. Climb it so you can see the world, not so the world can see you. -- David McCullough Jr. OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244 Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc https://keys.mailvelope.com/pks/lookup?op=get&[email protected] https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Greg Kroah-Hartman
maintainerPATCH 583/583 cifs: add fscache_resize_cookie() to cifs_setsize()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Sorenson <[email protected]>
commit fa724e235cfdb0fb0bb427d0f9dfe864ae27403e upstream.
Several code paths update the VFS inode size by calling netfs_resize_file() and cifs_setsize(), but omit the corresponding fscache_resize_cookie() call, leaving the fscache cookie out of sync with the actual file size:
- cifs_file_set_size() in inode.c: server-side truncation via setattr
- cifs_do_truncate() in file.c: truncates to zero on O_TRUNC open
- smb2_duplicate_extents() in smb2ops.c: file clone extending EOF
- smb3_simple_falloc() in smb2ops.c: two branches that extend EOF via write-range and SMB2_set_eof respectively
Since every caller of cifs_setsize() must resize the fscache cookie, add the call to cifs_setsize() itself, consistent with how truncate_pagecache() is already consolidated there.
--- fs/smb/client/inode.c | 1 + 1 file changed, 1 insertion(+)
fs/smb/client/inode.c +1 -0
@@ -3039,6 +3039,7 @@ void cifs_setsize(struct inode *inode, linode_set_mtime_to_ts (inode,inode_set_ctime_current (inode));truncate_pagecache (inode, offset);netfs_wait_for_outstanding_io (inode); +fscache_resize_cookie (cifs_inode_cookie (inode), offset); }void cifs_resize_file_locked (struct inode *inode,loff_t offset)