--- /dev/null
+From 2ddc3e7caadf352b6e0d11fc08bacbde92c1b530 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 21:00:23 +0200
+Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 4e3d1b2c48ca6c55f1e9ca7f8dccc76f120f276c ]
+
+FUSE_NOTIFY_RETRIEVE must be limited to uptodate folios; !uptodate folios
+can contain uninitialized data.
+Since FUSE_NOTIFY_RETRIEVE is intended to only return data that is already
+in the page cache and not wait for data from the FUSE daemon, treat
+!uptodate folios as if they weren't present.
+
+This only has security impact on systems that don't enable automatic
+zero-initialization of all page allocations via
+CONFIG_INIT_ON_ALLOC_DEFAULT_ON or init_on_alloc=1.
+
+Cc: stable@kernel.org
+Fixes: 2d45ba381a74 ("fuse: add retrieve request")
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[adjusted for stable: page instead of folio]
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dev.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
+index 9c8a7fdb34dd1a..fc93beee0719d9 100644
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -1728,6 +1728,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
+ page = find_get_page(mapping, index);
+ if (!page)
+ break;
++ if (!PageUptodate(page)) {
++ put_page(page);
++ break;
++ }
+
+ this_num = min_t(unsigned, num, PAGE_SIZE - offset);
+ ap->pages[ap->num_pages] = page;
+--
+2.53.0
+
--- /dev/null
+From ecf771d6c0b4b04f30c11c6dda7b9717ed31d4dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:44 +0800
+Subject: net/sched: act_pedit: check static offsets a priori
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit e1201bc781c28766720e78a5e099ffa568be4d74 ]
+
+Static key offsets should always be on 32 bit boundaries. Validate them on
+create/update time for static offsets and move the datapath validation
+for runtime offsets only.
+
+iproute2 already errors out if a given offset and data size cannot be
+packed to a 32 bit boundary. This change will make sure users which
+create/update pedit instances directly via netlink also error out,
+instead of finding out when packets are traversing.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 957ce9017c3f73..95ae885ecba168 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -239,8 +239,16 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ memcpy(nparms->tcfp_keys, parm->keys, ksize);
+
+ for (i = 0; i < nparms->tcfp_nkeys; ++i) {
++ u32 offmask = nparms->tcfp_keys[i].offmask;
+ u32 cur = nparms->tcfp_keys[i].off;
+
++ /* The AT option can be added to static offsets in the datapath */
++ if (!offmask && cur % 4) {
++ NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
++ ret = -EINVAL;
++ goto put_chain;
++ }
++
+ /* sanitize the shift value for any later use */
+ nparms->tcfp_keys[i].shift = min_t(size_t,
+ BITS_PER_TYPE(int) - 1,
+@@ -249,7 +257,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ /* The AT option can read a single byte, we can bound the actual
+ * value with uchar max.
+ */
+- cur += (0xff & nparms->tcfp_keys[i].offmask) >> nparms->tcfp_keys[i].shift;
++ cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+
+ /* Each key touches 4 bytes starting from the computed offset */
+ nparms->tcfp_off_max_hint =
+@@ -383,12 +391,12 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+- offset += (*d & tkey->offmask) >> tkey->shift;
+- }
+
+- if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
+- goto bad;
++ offset += (*d & tkey->offmask) >> tkey->shift;
++ if (offset % 4) {
++ pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ goto bad;
++ }
+ }
+
+ if (!offset_valid(skb, hoffset + offset)) {
+--
+2.53.0
+
--- /dev/null
+From 9bfe0e032f451c11caaa42d6e909747ba0e38d00 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:52 +0800
+Subject: net/sched: act_pedit: free pedit keys on bail from offset check
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb ]
+
+Ido Schimmel reports a memleak on a syzkaller instance:
+ BUG: memory leak
+ unreferenced object 0xffff88803d45e400 (size 1024):
+ comm "syz-executor292", pid 563, jiffies 4295025223 (age 51.781s)
+ hex dump (first 32 bytes):
+ 28 bd 70 00 fb db df 25 02 00 14 1f ff 02 00 02 (.p....%........
+ 00 32 00 00 1f 00 00 00 ac 14 14 3e 08 00 07 00 .2.........>....
+ backtrace:
+ [<ffffffff81bd0f2c>] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
+ [<ffffffff81bd0f2c>] slab_post_alloc_hook mm/slab.h:772 [inline]
+ [<ffffffff81bd0f2c>] slab_alloc_node mm/slub.c:3452 [inline]
+ [<ffffffff81bd0f2c>] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491
+ [<ffffffff81a865d9>] __do_kmalloc_node mm/slab_common.c:966 [inline]
+ [<ffffffff81a865d9>] __kmalloc+0x59/0x1a0 mm/slab_common.c:980
+ [<ffffffff83aa85c3>] kmalloc include/linux/slab.h:584 [inline]
+ [<ffffffff83aa85c3>] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245
+ [<ffffffff83a90623>] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394
+ [<ffffffff83a90e58>] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459
+ [<ffffffff83a96258>] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985
+ [<ffffffff83a96997>] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044
+ [<ffffffff83920a8d>] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395
+ [<ffffffff83b24305>] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575
+ [<ffffffff83901806>] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413
+ [<ffffffff83b21cae>] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
+ [<ffffffff83b21cae>] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365
+ [<ffffffff83b2293f>] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942
+ [<ffffffff8380c39f>] sock_sendmsg_nosec net/socket.c:724 [inline]
+ [<ffffffff8380c39f>] sock_sendmsg net/socket.c:747 [inline]
+ [<ffffffff8380c39f>] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503
+ [<ffffffff838156d2>] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557
+ [<ffffffff8381594f>] __sys_sendmsg+0x11f/0x200 net/socket.c:2586
+ [<ffffffff83815ab0>] __do_sys_sendmsg net/socket.c:2595 [inline]
+ [<ffffffff83815ab0>] __se_sys_sendmsg net/socket.c:2593 [inline]
+ [<ffffffff83815ab0>] __x64_sys_sendmsg+0x80/0xc0 net/socket.c:2593
+
+The recently added static offset check missed a free to the key buffer when
+bailing out on error.
+
+Fixes: e1201bc781c2 ("net/sched: act_pedit: check static offsets a priori")
+Reported-by: Ido Schimmel <idosch@idosch.org>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Tested-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 35fa94ba0edf8f..0601deea04d725 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -250,7 +250,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ if (!offmask && cur % 4) {
+ NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
+ ret = -EINVAL;
+- goto put_chain;
++ goto out_free_keys;
+ }
+
+ /* sanitize the shift value for any later use */
+@@ -275,6 +275,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+
+ return ret;
+
++out_free_keys:
++ kfree(nparms->tcfp_keys);
+ put_chain:
+ if (goto_ch)
+ tcf_chain_put_by_act(goto_ch);
+--
+2.53.0
+
--- /dev/null
+From d2a0396da8a211854254a031e458c4293eee28ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:48 +0800
+Subject: net/sched: act_pedit: Parse L3 Header for L4 offset
+
+From: Max Tottenham <mtottenh@akamai.com>
+
+[ Upstream commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b ]
+
+Instead of relying on skb->transport_header being set correctly, opt
+instead to parse the L3 header length out of the L3 headers for both
+IPv4/IPv6 when the Extended Layer Op for tcp/udp is used. This fixes a
+bug if GRO is disabled, when GRO is disabled skb->transport_header is
+set by __netif_receive_skb_core() to point to the L3 header, it's later
+fixed by the upper protocol layers, but act_pedit will receive the SKB
+before the fixups are completed. The existing behavior causes the
+following to edit the L3 header if GRO is disabled instead of the UDP
+header:
+
+ tc filter add dev eth0 ingress protocol ip flower ip_proto udp \
+ dst_ip 192.168.1.3 action pedit ex munge udp set dport 18053
+
+Also re-introduce a rate-limited warning if we were unable to extract
+the header offset when using the 'ex' interface.
+
+Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to
+the conventional network headers")
+Signed-off-by: Max Tottenham <mtottenh@akamai.com>
+Reviewed-by: Josh Hunt <johunt@akamai.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202305261541.N165u9TZ-lkp@intel.com/
+Reviewed-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b)
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 48 ++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 43 insertions(+), 5 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index ecad6fc39dc3d7..df31b2b7b42253 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -13,7 +13,10 @@
+ #include <linux/rtnetlink.h>
+ #include <linux/module.h>
+ #include <linux/init.h>
++#include <linux/ip.h>
++#include <linux/ipv6.h>
+ #include <linux/slab.h>
++#include <net/ipv6.h>
+ #include <net/netlink.h>
+ #include <net/pkt_sched.h>
+ #include <linux/tc_act/tc_pedit.h>
+@@ -313,28 +316,58 @@ static bool offset_valid(struct sk_buff *skb, int offset)
+ return true;
+ }
+
+-static void pedit_skb_hdr_offset(struct sk_buff *skb,
++static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
++{
++ const int noff = skb_network_offset(skb);
++ int ret = -EINVAL;
++ struct iphdr _iph;
++
++ switch (skb->protocol) {
++ case htons(ETH_P_IP): {
++ const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph);
++
++ if (!iph)
++ goto out;
++ *hoffset = noff + iph->ihl * 4;
++ ret = 0;
++ break;
++ }
++ case htons(ETH_P_IPV6):
++ ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL;
++ break;
++ }
++out:
++ return ret;
++}
++
++static int pedit_skb_hdr_offset(struct sk_buff *skb,
+ enum pedit_header_type htype, int *hoffset)
+ {
++ int ret = -EINVAL;
+ /* 'htype' is validated in the netlink parsing */
+ switch (htype) {
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
+- if (skb_mac_header_was_set(skb))
++ if (skb_mac_header_was_set(skb)) {
+ *hoffset = skb_mac_offset(skb);
++ ret = 0;
++ }
+ break;
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
+ *hoffset = skb_network_offset(skb);
++ ret = 0;
+ break;
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
++ ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP);
++ break;
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
+- if (skb_transport_header_was_set(skb))
+- *hoffset = skb_transport_offset(skb);
++ ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP);
+ break;
+ default:
+ break;
+ }
++ return ret;
+ }
+
+ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+@@ -369,6 +402,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ int hoffset = 0;
+ u32 *ptr, hdata;
+ u32 val;
++ int rc;
+
+ if (tkey_ex) {
+ htype = tkey_ex->htype;
+@@ -377,7 +411,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ tkey_ex++;
+ }
+
+- pedit_skb_hdr_offset(skb, htype, &hoffset);
++ rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
++ if (rc) {
++ pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype);
++ goto bad;
++ }
+
+ if (tkey->offmask) {
+ u8 *d, _d;
+--
+2.53.0
+
--- /dev/null
+From c744ad742f2186b1c0b2998bc32348f5ad2289f4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:46 +0800
+Subject: net/sched: act_pedit: rate limit datapath messages
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a ]
+
+Unbounded info messages in the pedit datapath can flood the printk
+ring buffer quite easily depending on the action created.
+As these messages are informational, usually printing some, not all,
+is enough to bring attention to the real issue.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 95ae885ecba168..ecad6fc39dc3d7 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -383,8 +383,8 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ u8 *d, _d;
+
+ if (!offset_valid(skb, hoffset + tkey->at)) {
+- pr_info("tc action pedit 'at' offset %d out of bounds\n",
+- hoffset + tkey->at);
++ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
++ hoffset + tkey->at);
+ goto bad;
+ }
+ d = skb_header_pointer(skb, hoffset + tkey->at,
+@@ -394,14 +394,13 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ offset += (*d & tkey->offmask) >> tkey->shift;
+ if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ pr_info_ratelimited("tc action pedit offset must be on 32 bit boundaries\n");
+ goto bad;
+ }
+ }
+
+ if (!offset_valid(skb, hoffset + offset)) {
+- pr_info("tc action pedit offset %d out of bounds\n",
+- hoffset + offset);
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
+ goto bad;
+ }
+
+@@ -418,8 +417,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ val = (*ptr + tkey->val) & ~tkey->mask;
+ break;
+ default:
+- pr_info("tc action pedit bad command (%d)\n",
+- cmd);
++ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+--
+2.53.0
+
--- /dev/null
+From 1d610bf3d0adb7044c162bf713cf65e638d5c2d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:42 +0800
+Subject: net/sched: act_pedit: remove extra check for key type
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 577140180ba28d0d37bc898c7bd6702c83aa106f ]
+
+The netlink parsing already validates the key 'htype'.
+Remove the datapath check as it's redundant.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 29 +++++++----------------------
+ 1 file changed, 7 insertions(+), 22 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 84152d3a492469..957ce9017c3f73 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -305,37 +305,28 @@ static bool offset_valid(struct sk_buff *skb, int offset)
+ return true;
+ }
+
+-static int pedit_skb_hdr_offset(struct sk_buff *skb,
+- enum pedit_header_type htype, int *hoffset)
++static void pedit_skb_hdr_offset(struct sk_buff *skb,
++ enum pedit_header_type htype, int *hoffset)
+ {
+- int ret = -EINVAL;
+-
++ /* 'htype' is validated in the netlink parsing */
+ switch (htype) {
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
+- if (skb_mac_header_was_set(skb)) {
++ if (skb_mac_header_was_set(skb))
+ *hoffset = skb_mac_offset(skb);
+- ret = 0;
+- }
+ break;
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
+ *hoffset = skb_network_offset(skb);
+- ret = 0;
+ break;
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
+- if (skb_transport_header_was_set(skb)) {
++ if (skb_transport_header_was_set(skb))
+ *hoffset = skb_transport_offset(skb);
+- ret = 0;
+- }
+ break;
+ default:
+- ret = -EINVAL;
+ break;
+ }
+-
+- return ret;
+ }
+
+ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+@@ -367,10 +358,9 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
+ int offset = tkey->off;
++ int hoffset = 0;
+ u32 *ptr, hdata;
+- int hoffset;
+ u32 val;
+- int rc;
+
+ if (tkey_ex) {
+ htype = tkey_ex->htype;
+@@ -379,12 +369,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ tkey_ex++;
+ }
+
+- rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
+- if (rc) {
+- pr_info("tc action pedit bad header type specified (0x%x)\n",
+- htype);
+- goto bad;
+- }
++ pedit_skb_hdr_offset(skb, htype, &hoffset);
+
+ if (tkey->offmask) {
+ u8 *d, _d;
+--
+2.53.0
+
--- /dev/null
+From fd434c1137b7e44d2b52fc703df732947fe45e1b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:36 +0800
+Subject: net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 5036034572b79daa6d6600338e8e8229e2a44b09 ]
+
+Transform two checks in the 'ex' key parsing into netlink policies
+removing extra if checks.
+
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index a44101b2f44191..510a3b5b8c0c1d 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -31,8 +31,9 @@ static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
+ };
+
+ static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
+- [TCA_PEDIT_KEY_EX_HTYPE] = { .type = NLA_U16 },
+- [TCA_PEDIT_KEY_EX_CMD] = { .type = NLA_U16 },
++ [TCA_PEDIT_KEY_EX_HTYPE] =
++ NLA_POLICY_MAX(NLA_U16, TCA_PEDIT_HDR_TYPE_MAX),
++ [TCA_PEDIT_KEY_EX_CMD] = NLA_POLICY_MAX(NLA_U16, TCA_PEDIT_CMD_MAX),
+ };
+
+ static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
+@@ -82,12 +83,6 @@ static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
+ k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
+ k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
+
+- if (k->htype > TCA_PEDIT_HDR_TYPE_MAX ||
+- k->cmd > TCA_PEDIT_CMD_MAX) {
+- err = -EINVAL;
+- goto err_out;
+- }
+-
+ k++;
+ }
+
+--
+2.53.0
+
--- /dev/null
+From 05750b74ef98d989e2d907e8cf4b4c25fc24b160 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:50 +0800
+Subject: net/sched: fix pedit partial COW leading to page cache corruption
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+
+[ Upstream commit 899ee91156e57784090c5565e4f31bd7dbffbc5a ]
+
+tcf_pedit_act() computes the COW range for skb_ensure_writable()
+once before the key loop using tcfp_off_max_hint, but the hint does
+not account for the runtime header offset added by typed keys. This
+can leave part of the write region un-COW'd.
+
+Fix by moving skb_ensure_writable() inside the per-key loop where
+the actual write offset is known, and add overflow checking on the
+offset arithmetic. For negative offsets (e.g. Ethernet header edits
+at ingress), use skb_cow() to COW the headroom instead. Guard
+offset_valid() against INT_MIN, where negation is undefined.
+
+Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable")
+Reported-by: Yiming Qian <yimingqian591@gmail.com>
+Reported-by: Keenan Dong <keenanat2000@gmail.com>
+Reported-by: Han Guidong <2045gemini@gmail.com>
+Reported-by: Zhang Cen <rollkingzzc@gmail.com>
+Reviewed-by: Han Guidong <2045gemini@gmail.com>
+Tested-by: Han Guidong <2045gemini@gmail.com>
+Reviewed-by: Davide Caratti <dcaratti@redhat.com>
+Tested-by: Davide Caratti <dcaratti@redhat.com>
+Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Tested-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Reviewed-by: Victor Nogueira <victor@mojatatu.com>
+Tested-by: Victor Nogueira <victor@mojatatu.com>
+Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Signed-off-by: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com
+[rename include file from linux/unaligned.h to asm/unaligned.h]
+Conflicts:
+ include/net/tc_act/tc_pedit.h
+ net/sched/act_pedit.c
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/tc_act/tc_pedit.h | 1 -
+ net/sched/act_pedit.c | 77 +++++++++++++++++++----------------
+ 2 files changed, 41 insertions(+), 37 deletions(-)
+
+diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
+index 83fe3993178180..a26d4cd3b8d6f3 100644
+--- a/include/net/tc_act/tc_pedit.h
++++ b/include/net/tc_act/tc_pedit.h
+@@ -14,7 +14,6 @@ struct tcf_pedit_key_ex {
+ struct tcf_pedit_parms {
+ struct tc_pedit_key *tcfp_keys;
+ struct tcf_pedit_key_ex *tcfp_keys_ex;
+- u32 tcfp_off_max_hint;
+ unsigned char tcfp_nkeys;
+ unsigned char tcfp_flags;
+ struct rcu_head rcu;
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index df31b2b7b42253..35fa94ba0edf8f 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -17,6 +17,8 @@
+ #include <linux/ipv6.h>
+ #include <linux/slab.h>
+ #include <net/ipv6.h>
++#include <linux/overflow.h>
++#include <asm/unaligned.h>
+ #include <net/netlink.h>
+ #include <net/pkt_sched.h>
+ #include <linux/tc_act/tc_pedit.h>
+@@ -229,7 +231,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ goto out_release;
+ }
+
+- nparms->tcfp_off_max_hint = 0;
+ nparms->tcfp_flags = parm->flags;
+ nparms->tcfp_nkeys = parm->nkeys;
+
+@@ -257,14 +258,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ BITS_PER_TYPE(int) - 1,
+ nparms->tcfp_keys[i].shift);
+
+- /* The AT option can read a single byte, we can bound the actual
+- * value with uchar max.
+- */
+- cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+-
+- /* Each key touches 4 bytes starting from the computed offset */
+- nparms->tcfp_off_max_hint =
+- max(nparms->tcfp_off_max_hint, cur + 4);
+ }
+
+ p = to_pedit(*a);
+@@ -305,15 +298,12 @@ static void tcf_pedit_cleanup(struct tc_action *a)
+ call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
+ }
+
+-static bool offset_valid(struct sk_buff *skb, int offset)
++static bool offset_valid(struct sk_buff *skb, int offset, int len)
+ {
+- if (offset > 0 && offset > skb->len)
+- return false;
+-
+- if (offset < 0 && -offset > skb_headroom(skb))
++ if (offset < -(int)skb_headroom(skb))
+ return false;
+
+- return true;
++ return offset <= (int)skb->len - len;
+ }
+
+ static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
+@@ -379,18 +369,10 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ struct tcf_pedit_key_ex *tkey_ex;
+ struct tcf_pedit_parms *parms;
+ struct tc_pedit_key *tkey;
+- u32 max_offset;
+ int i;
+
+ parms = rcu_dereference_bh(p->parms);
+
+- max_offset = (skb_transport_header_was_set(skb) ?
+- skb_transport_offset(skb) :
+- skb_network_offset(skb)) +
+- parms->tcfp_off_max_hint;
+- if (skb_ensure_writable(skb, min(skb->len, max_offset)))
+- goto done;
+-
+ tcf_lastuse_update(&p->tcf_tm);
+ tcf_action_update_bstats(&p->common, skb);
+
+@@ -398,10 +380,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ tkey_ex = parms->tcfp_keys_ex;
+
+ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
++ int write_offset, write_len;
+ int offset = tkey->off;
+ int hoffset = 0;
+- u32 *ptr, hdata;
+- u32 val;
++ u32 cur_val, val;
++ u32 *ptr;
+ int rc;
+
+ if (tkey_ex) {
+@@ -419,13 +402,15 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ if (tkey->offmask) {
+ u8 *d, _d;
++ int at_offset;
+
+- if (!offset_valid(skb, hoffset + tkey->at)) {
++ if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) ||
++ !offset_valid(skb, at_offset, sizeof(_d))) {
+ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
+ hoffset + tkey->at);
+ goto bad;
+ }
+- d = skb_header_pointer(skb, hoffset + tkey->at,
++ d = skb_header_pointer(skb, at_offset,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+@@ -437,31 +422,51 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ }
+ }
+
+- if (!offset_valid(skb, hoffset + offset)) {
+- pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
++ if (check_add_overflow(hoffset, offset, &write_offset)) {
++ pr_info_ratelimited("tc action pedit offset overflow\n");
+ goto bad;
+ }
+
+- ptr = skb_header_pointer(skb, hoffset + offset,
+- sizeof(hdata), &hdata);
+- if (!ptr)
++ if (!offset_valid(skb, write_offset, sizeof(*ptr))) {
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n",
++ write_offset);
+ goto bad;
++ }
++
++ if (write_offset < 0) {
++ if (skb_cow(skb, -write_offset))
++ goto bad;
++ if (write_offset + (int)sizeof(*ptr) > 0) {
++ if (skb_ensure_writable(skb,
++ min_t(int, skb->len,
++ write_offset + (int)sizeof(*ptr))))
++ goto bad;
++ }
++ } else {
++ if (check_add_overflow(write_offset, (int)sizeof(*ptr),
++ &write_len))
++ goto bad;
++ if (skb_ensure_writable(skb, min_t(int, skb->len,
++ write_len)))
++ goto bad;
++ }
++
++ ptr = (u32 *)(skb->data + write_offset);
++ cur_val = get_unaligned(ptr);
+ /* just do it, baby */
+ switch (cmd) {
+ case TCA_PEDIT_KEY_EX_CMD_SET:
+ val = tkey->val;
+ break;
+ case TCA_PEDIT_KEY_EX_CMD_ADD:
+- val = (*ptr + tkey->val) & ~tkey->mask;
++ val = (cur_val + tkey->val) & ~tkey->mask;
+ break;
+ default:
+ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+- *ptr = ((*ptr & tkey->mask) ^ val);
+- if (ptr == &hdata)
+- skb_store_bits(skb, hoffset + offset, ptr, 4);
++ put_unaligned((cur_val & tkey->mask) ^ val, ptr);
+ }
+
+ goto done;
+--
+2.53.0
+
--- /dev/null
+From c0d15083891cb8d52d3a11c00a76a98bcbf66da0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:40 +0800
+Subject: net/sched: simplify tcf_pedit_act
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 95b069382351826c0ae37938070aa82dbeaf288d ]
+
+Remove the check for a negative number of keys as
+this cannot ever happen
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 137 +++++++++++++++++++++---------------------
+ 1 file changed, 67 insertions(+), 70 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 0fbffebfbdc9d8..84152d3a492469 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -341,8 +341,12 @@ static int pedit_skb_hdr_offset(struct sk_buff *skb,
+ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ struct tcf_result *res)
+ {
++ enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
++ enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
+ struct tcf_pedit *p = to_pedit(a);
++ struct tcf_pedit_key_ex *tkey_ex;
+ struct tcf_pedit_parms *parms;
++ struct tc_pedit_key *tkey;
+ u32 max_offset;
+ int i;
+
+@@ -358,88 +362,81 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ tcf_lastuse_update(&p->tcf_tm);
+ tcf_action_update_bstats(&p->common, skb);
+
+- if (parms->tcfp_nkeys > 0) {
+- struct tc_pedit_key *tkey = parms->tcfp_keys;
+- struct tcf_pedit_key_ex *tkey_ex = parms->tcfp_keys_ex;
+- enum pedit_header_type htype =
+- TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
+- enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
+-
+- for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
+- u32 *ptr, hdata;
+- int offset = tkey->off;
+- int hoffset;
+- u32 val;
+- int rc;
+-
+- if (tkey_ex) {
+- htype = tkey_ex->htype;
+- cmd = tkey_ex->cmd;
+-
+- tkey_ex++;
+- }
++ tkey = parms->tcfp_keys;
++ tkey_ex = parms->tcfp_keys_ex;
+
+- rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
+- if (rc) {
+- pr_info("tc action pedit bad header type specified (0x%x)\n",
+- htype);
+- goto bad;
+- }
++ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
++ int offset = tkey->off;
++ u32 *ptr, hdata;
++ int hoffset;
++ u32 val;
++ int rc;
+
+- if (tkey->offmask) {
+- u8 *d, _d;
+-
+- if (!offset_valid(skb, hoffset + tkey->at)) {
+- pr_info("tc action pedit 'at' offset %d out of bounds\n",
+- hoffset + tkey->at);
+- goto bad;
+- }
+- d = skb_header_pointer(skb, hoffset + tkey->at,
+- sizeof(_d), &_d);
+- if (!d)
+- goto bad;
+- offset += (*d & tkey->offmask) >> tkey->shift;
+- }
++ if (tkey_ex) {
++ htype = tkey_ex->htype;
++ cmd = tkey_ex->cmd;
+
+- if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
+- goto bad;
+- }
++ tkey_ex++;
++ }
+
+- if (!offset_valid(skb, hoffset + offset)) {
+- pr_info("tc action pedit offset %d out of bounds\n",
+- hoffset + offset);
+- goto bad;
+- }
++ rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
++ if (rc) {
++ pr_info("tc action pedit bad header type specified (0x%x)\n",
++ htype);
++ goto bad;
++ }
+
+- ptr = skb_header_pointer(skb, hoffset + offset,
+- sizeof(hdata), &hdata);
+- if (!ptr)
+- goto bad;
+- /* just do it, baby */
+- switch (cmd) {
+- case TCA_PEDIT_KEY_EX_CMD_SET:
+- val = tkey->val;
+- break;
+- case TCA_PEDIT_KEY_EX_CMD_ADD:
+- val = (*ptr + tkey->val) & ~tkey->mask;
+- break;
+- default:
+- pr_info("tc action pedit bad command (%d)\n",
+- cmd);
++ if (tkey->offmask) {
++ u8 *d, _d;
++
++ if (!offset_valid(skb, hoffset + tkey->at)) {
++ pr_info("tc action pedit 'at' offset %d out of bounds\n",
++ hoffset + tkey->at);
+ goto bad;
+ }
++ d = skb_header_pointer(skb, hoffset + tkey->at,
++ sizeof(_d), &_d);
++ if (!d)
++ goto bad;
++ offset += (*d & tkey->offmask) >> tkey->shift;
++ }
+
+- *ptr = ((*ptr & tkey->mask) ^ val);
+- if (ptr == &hdata)
+- skb_store_bits(skb, hoffset + offset, ptr, 4);
++ if (offset % 4) {
++ pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ goto bad;
+ }
+
+- goto done;
+- } else {
+- WARN(1, "pedit BUG: index %d\n", p->tcf_index);
++ if (!offset_valid(skb, hoffset + offset)) {
++ pr_info("tc action pedit offset %d out of bounds\n",
++ hoffset + offset);
++ goto bad;
++ }
++
++ ptr = skb_header_pointer(skb, hoffset + offset,
++ sizeof(hdata), &hdata);
++ if (!ptr)
++ goto bad;
++ /* just do it, baby */
++ switch (cmd) {
++ case TCA_PEDIT_KEY_EX_CMD_SET:
++ val = tkey->val;
++ break;
++ case TCA_PEDIT_KEY_EX_CMD_ADD:
++ val = (*ptr + tkey->val) & ~tkey->mask;
++ break;
++ default:
++ pr_info("tc action pedit bad command (%d)\n",
++ cmd);
++ goto bad;
++ }
++
++ *ptr = ((*ptr & tkey->mask) ^ val);
++ if (ptr == &hdata)
++ skb_store_bits(skb, hoffset + offset, ptr, 4);
+ }
+
++ goto done;
++
+ bad:
+ spin_lock(&p->tcf_lock);
+ p->tcf_qstats.overlimits++;
+--
+2.53.0
+
--- /dev/null
+From 85521f7ed7d2b93d4ab27bc01d3fc74285cdbc21 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 15:53:38 +0800
+Subject: net/sched: transition act_pedit to rcu and percpu stats
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 52cf89f78c01bf39973f3e70d366921d70faff7a ]
+
+The software pedit action didn't get the same love as some of the
+other actions and it's still using spinlocks and shared stats in the
+datapath.
+Transition the action to rcu and percpu stats as this improves the
+action's performance dramatically on multiple cpu deployments.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Conflicts:
+ net/sched/act_pedit.c
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/tc_act/tc_pedit.h | 81 +++++++++++++++----
+ net/sched/act_pedit.c | 148 ++++++++++++++++++++--------------
+ 2 files changed, 153 insertions(+), 76 deletions(-)
+
+diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
+index 3e02709a1df656..83fe3993178180 100644
+--- a/include/net/tc_act/tc_pedit.h
++++ b/include/net/tc_act/tc_pedit.h
+@@ -4,22 +4,29 @@
+
+ #include <net/act_api.h>
+ #include <linux/tc_act/tc_pedit.h>
++#include <linux/types.h>
+
+ struct tcf_pedit_key_ex {
+ enum pedit_header_type htype;
+ enum pedit_cmd cmd;
+ };
+
+-struct tcf_pedit {
+- struct tc_action common;
+- unsigned char tcfp_nkeys;
+- unsigned char tcfp_flags;
+- u32 tcfp_off_max_hint;
++struct tcf_pedit_parms {
+ struct tc_pedit_key *tcfp_keys;
+ struct tcf_pedit_key_ex *tcfp_keys_ex;
++ u32 tcfp_off_max_hint;
++ unsigned char tcfp_nkeys;
++ unsigned char tcfp_flags;
++ struct rcu_head rcu;
++};
++
++struct tcf_pedit {
++ struct tc_action common;
++ struct tcf_pedit_parms __rcu *parms;
+ };
+
+ #define to_pedit(a) ((struct tcf_pedit *)a)
++#define to_pedit_parms(a) (rcu_dereference(to_pedit(a)->parms))
+
+ static inline bool is_tcf_pedit(const struct tc_action *a)
+ {
+@@ -32,37 +39,81 @@ static inline bool is_tcf_pedit(const struct tc_action *a)
+
+ static inline int tcf_pedit_nkeys(const struct tc_action *a)
+ {
+- return to_pedit(a)->tcfp_nkeys;
++ struct tcf_pedit_parms *parms;
++ int nkeys;
++
++ rcu_read_lock();
++ parms = to_pedit_parms(a);
++ nkeys = parms->tcfp_nkeys;
++ rcu_read_unlock();
++
++ return nkeys;
+ }
+
+ static inline u32 tcf_pedit_htype(const struct tc_action *a, int index)
+ {
+- if (to_pedit(a)->tcfp_keys_ex)
+- return to_pedit(a)->tcfp_keys_ex[index].htype;
++ u32 htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
++ struct tcf_pedit_parms *parms;
++
++ rcu_read_lock();
++ parms = to_pedit_parms(a);
++ if (parms->tcfp_keys_ex)
++ htype = parms->tcfp_keys_ex[index].htype;
++ rcu_read_unlock();
+
+- return TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
++ return htype;
+ }
+
+ static inline u32 tcf_pedit_cmd(const struct tc_action *a, int index)
+ {
+- if (to_pedit(a)->tcfp_keys_ex)
+- return to_pedit(a)->tcfp_keys_ex[index].cmd;
++ struct tcf_pedit_parms *parms;
++ u32 cmd = __PEDIT_CMD_MAX;
+
+- return __PEDIT_CMD_MAX;
++ rcu_read_lock();
++ parms = to_pedit_parms(a);
++ if (parms->tcfp_keys_ex)
++ cmd = parms->tcfp_keys_ex[index].cmd;
++ rcu_read_unlock();
++
++ return cmd;
+ }
+
+ static inline u32 tcf_pedit_mask(const struct tc_action *a, int index)
+ {
+- return to_pedit(a)->tcfp_keys[index].mask;
++ struct tcf_pedit_parms *parms;
++ u32 mask;
++
++ rcu_read_lock();
++ parms = to_pedit_parms(a);
++ mask = parms->tcfp_keys[index].mask;
++ rcu_read_unlock();
++
++ return mask;
+ }
+
+ static inline u32 tcf_pedit_val(const struct tc_action *a, int index)
+ {
+- return to_pedit(a)->tcfp_keys[index].val;
++ struct tcf_pedit_parms *parms;
++ u32 val;
++
++ rcu_read_lock();
++ parms = to_pedit_parms(a);
++ val = parms->tcfp_keys[index].val;
++ rcu_read_unlock();
++
++ return val;
+ }
+
+ static inline u32 tcf_pedit_offset(const struct tc_action *a, int index)
+ {
+- return to_pedit(a)->tcfp_keys[index].off;
++ struct tcf_pedit_parms *parms;
++ u32 off;
++
++ rcu_read_lock();
++ parms = to_pedit_parms(a);
++ off = parms->tcfp_keys[index].off;
++ rcu_read_unlock();
++
++ return off;
+ }
+ #endif /* __NET_TC_PED_H */
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 510a3b5b8c0c1d..0fbffebfbdc9d8 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -130,6 +130,17 @@ static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
+ return -EINVAL;
+ }
+
++static void tcf_pedit_cleanup_rcu(struct rcu_head *head)
++{
++ struct tcf_pedit_parms *parms =
++ container_of(head, struct tcf_pedit_parms, rcu);
++
++ kfree(parms->tcfp_keys_ex);
++ kfree(parms->tcfp_keys);
++
++ kfree(parms);
++}
++
+ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ struct nlattr *est, struct tc_action **a,
+ int ovr, int bind, bool rtnl_held,
+@@ -137,10 +148,9 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ struct netlink_ext_ack *extack)
+ {
+ struct tc_action_net *tn = net_generic(net, pedit_net_id);
+- struct nlattr *tb[TCA_PEDIT_MAX + 1];
+ struct tcf_chain *goto_ch = NULL;
+- struct tc_pedit_key *keys = NULL;
+- struct tcf_pedit_key_ex *keys_ex;
++ struct tcf_pedit_parms *oparms, *nparms;
++ struct nlattr *tb[TCA_PEDIT_MAX + 1];
+ struct tc_pedit *parm;
+ struct nlattr *pattr;
+ struct tcf_pedit *p;
+@@ -177,18 +187,25 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ return -EINVAL;
+ }
+
+- keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
+- if (IS_ERR(keys_ex))
+- return PTR_ERR(keys_ex);
++ nparms = kzalloc(sizeof(*nparms), GFP_KERNEL);
++ if (!nparms)
++ return -ENOMEM;
++
++ nparms->tcfp_keys_ex =
++ tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
++ if (IS_ERR(nparms->tcfp_keys_ex)) {
++ ret = PTR_ERR(nparms->tcfp_keys_ex);
++ goto out_free;
++ }
+
+ index = parm->index;
+ err = tcf_idr_check_alloc(tn, &index, a, bind);
+ if (!err) {
+- ret = tcf_idr_create(tn, index, est, a,
+- &act_pedit_ops, bind, false, flags);
++ ret = tcf_idr_create_from_flags(tn, index, est, a,
++ &act_pedit_ops, bind, flags);
+ if (ret) {
+ tcf_idr_cleanup(tn, index);
+- goto out_free;
++ goto out_free_ex;
+ }
+ ret = ACT_P_CREATED;
+ } else if (err > 0) {
+@@ -200,7 +217,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ }
+ } else {
+ ret = err;
+- goto out_free;
++ goto out_free_ex;
+ }
+
+ err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
+@@ -208,48 +225,50 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ ret = err;
+ goto out_release;
+ }
+- p = to_pedit(*a);
+- spin_lock_bh(&p->tcf_lock);
+
+- if (ret == ACT_P_CREATED ||
+- (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys)) {
+- keys = kmalloc(ksize, GFP_ATOMIC);
+- if (!keys) {
+- spin_unlock_bh(&p->tcf_lock);
+- ret = -ENOMEM;
+- goto put_chain;
+- }
+- kfree(p->tcfp_keys);
+- p->tcfp_keys = keys;
+- p->tcfp_nkeys = parm->nkeys;
++ nparms->tcfp_off_max_hint = 0;
++ nparms->tcfp_flags = parm->flags;
++ nparms->tcfp_nkeys = parm->nkeys;
++
++ nparms->tcfp_keys = kmalloc(ksize, GFP_KERNEL);
++ if (!nparms->tcfp_keys) {
++ ret = -ENOMEM;
++ goto put_chain;
+ }
+- memcpy(p->tcfp_keys, parm->keys, ksize);
+- p->tcfp_off_max_hint = 0;
+- for (i = 0; i < p->tcfp_nkeys; ++i) {
+- u32 cur = p->tcfp_keys[i].off;
++
++ memcpy(nparms->tcfp_keys, parm->keys, ksize);
++
++ for (i = 0; i < nparms->tcfp_nkeys; ++i) {
++ u32 cur = nparms->tcfp_keys[i].off;
+
+ /* sanitize the shift value for any later use */
+- p->tcfp_keys[i].shift = min_t(size_t, BITS_PER_TYPE(int) - 1,
+- p->tcfp_keys[i].shift);
++ nparms->tcfp_keys[i].shift = min_t(size_t,
++ BITS_PER_TYPE(int) - 1,
++ nparms->tcfp_keys[i].shift);
+
+ /* The AT option can read a single byte, we can bound the actual
+ * value with uchar max.
+ */
+- cur += (0xff & p->tcfp_keys[i].offmask) >> p->tcfp_keys[i].shift;
++ cur += (0xff & nparms->tcfp_keys[i].offmask) >> nparms->tcfp_keys[i].shift;
+
+ /* Each key touches 4 bytes starting from the computed offset */
+- p->tcfp_off_max_hint = max(p->tcfp_off_max_hint, cur + 4);
++ nparms->tcfp_off_max_hint =
++ max(nparms->tcfp_off_max_hint, cur + 4);
+ }
+
+- p->tcfp_flags = parm->flags;
++ p = to_pedit(*a);
++
++ spin_lock_bh(&p->tcf_lock);
+ goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
++ oparms = rcu_replace_pointer(p->parms, nparms, 1);
++ spin_unlock_bh(&p->tcf_lock);
+
+- kfree(p->tcfp_keys_ex);
+- p->tcfp_keys_ex = keys_ex;
++ if (oparms)
++ call_rcu(&oparms->rcu, tcf_pedit_cleanup_rcu);
+
+- spin_unlock_bh(&p->tcf_lock);
+ if (goto_ch)
+ tcf_chain_put_by_act(goto_ch);
++
+ return ret;
+
+ put_chain:
+@@ -257,19 +276,22 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ tcf_chain_put_by_act(goto_ch);
+ out_release:
+ tcf_idr_release(*a, bind);
++out_free_ex:
++ kfree(nparms->tcfp_keys_ex);
+ out_free:
+- kfree(keys_ex);
++ kfree(nparms);
+ return ret;
+-
+ }
+
+ static void tcf_pedit_cleanup(struct tc_action *a)
+ {
+ struct tcf_pedit *p = to_pedit(a);
+- struct tc_pedit_key *keys = p->tcfp_keys;
++ struct tcf_pedit_parms *parms;
+
+- kfree(keys);
+- kfree(p->tcfp_keys_ex);
++ parms = rcu_dereference_protected(p->parms, 1);
++
++ if (parms)
++ call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
+ }
+
+ static bool offset_valid(struct sk_buff *skb, int offset)
+@@ -320,28 +342,30 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ struct tcf_result *res)
+ {
+ struct tcf_pedit *p = to_pedit(a);
++ struct tcf_pedit_parms *parms;
+ u32 max_offset;
+ int i;
+
+- spin_lock(&p->tcf_lock);
++ parms = rcu_dereference_bh(p->parms);
+
+ max_offset = (skb_transport_header_was_set(skb) ?
+ skb_transport_offset(skb) :
+ skb_network_offset(skb)) +
+- p->tcfp_off_max_hint;
++ parms->tcfp_off_max_hint;
+ if (skb_ensure_writable(skb, min(skb->len, max_offset)))
+- goto unlock;
++ goto done;
+
+ tcf_lastuse_update(&p->tcf_tm);
++ tcf_action_update_bstats(&p->common, skb);
+
+- if (p->tcfp_nkeys > 0) {
+- struct tc_pedit_key *tkey = p->tcfp_keys;
+- struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
++ if (parms->tcfp_nkeys > 0) {
++ struct tc_pedit_key *tkey = parms->tcfp_keys;
++ struct tcf_pedit_key_ex *tkey_ex = parms->tcfp_keys_ex;
+ enum pedit_header_type htype =
+ TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
+ enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
+
+- for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
++ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
+ u32 *ptr, hdata;
+ int offset = tkey->off;
+ int hoffset;
+@@ -417,11 +441,10 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ }
+
+ bad:
++ spin_lock(&p->tcf_lock);
+ p->tcf_qstats.overlimits++;
+-done:
+- bstats_update(&p->tcf_bstats, skb);
+-unlock:
+ spin_unlock(&p->tcf_lock);
++done:
+ return p->tcf_action;
+ }
+
+@@ -440,30 +463,33 @@ static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
+ {
+ unsigned char *b = skb_tail_pointer(skb);
+ struct tcf_pedit *p = to_pedit(a);
++ struct tcf_pedit_parms *parms;
+ struct tc_pedit *opt;
+ struct tcf_t t;
+ int s;
+
+- s = struct_size(opt, keys, p->tcfp_nkeys);
++ spin_lock_bh(&p->tcf_lock);
++ parms = rcu_dereference_protected(p->parms, 1);
++ s = struct_size(opt, keys, parms->tcfp_nkeys);
+
+- /* netlink spinlocks held above us - must use ATOMIC */
+ opt = kzalloc(s, GFP_ATOMIC);
+- if (unlikely(!opt))
++ if (unlikely(!opt)) {
++ spin_unlock_bh(&p->tcf_lock);
+ return -ENOBUFS;
++ }
+
+- spin_lock_bh(&p->tcf_lock);
+- memcpy(opt->keys, p->tcfp_keys, flex_array_size(opt, keys, p->tcfp_nkeys));
++ memcpy(opt->keys, parms->tcfp_keys,
++ flex_array_size(opt, keys, parms->tcfp_nkeys));
+ opt->index = p->tcf_index;
+- opt->nkeys = p->tcfp_nkeys;
+- opt->flags = p->tcfp_flags;
++ opt->nkeys = parms->tcfp_nkeys;
++ opt->flags = parms->tcfp_flags;
+ opt->action = p->tcf_action;
+ opt->refcnt = refcount_read(&p->tcf_refcnt) - ref;
+ opt->bindcnt = atomic_read(&p->tcf_bindcnt) - bind;
+
+- if (p->tcfp_keys_ex) {
+- if (tcf_pedit_key_ex_dump(skb,
+- p->tcfp_keys_ex,
+- p->tcfp_nkeys))
++ if (parms->tcfp_keys_ex) {
++ if (tcf_pedit_key_ex_dump(skb, parms->tcfp_keys_ex,
++ parms->tcfp_nkeys))
+ goto nla_put_failure;
+
+ if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
+--
+2.53.0
+
--- /dev/null
+net-sched-act_pedit-use-nla_policy-for-parsing-ex-ke.patch
+net-sched-transition-act_pedit-to-rcu-and-percpu-sta.patch
+net-sched-simplify-tcf_pedit_act.patch
+net-sched-act_pedit-remove-extra-check-for-key-type.patch
+net-sched-act_pedit-check-static-offsets-a-priori.patch
+net-sched-act_pedit-rate-limit-datapath-messages.patch
+net-sched-act_pedit-parse-l3-header-for-l4-offset.patch
+net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
+net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch
+fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
--- /dev/null
+From 317cdc5119de09e0fe86e671be2e301c5f10c43b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 21:00:23 +0200
+Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 4e3d1b2c48ca6c55f1e9ca7f8dccc76f120f276c ]
+
+FUSE_NOTIFY_RETRIEVE must be limited to uptodate folios; !uptodate folios
+can contain uninitialized data.
+Since FUSE_NOTIFY_RETRIEVE is intended to only return data that is already
+in the page cache and not wait for data from the FUSE daemon, treat
+!uptodate folios as if they weren't present.
+
+This only has security impact on systems that don't enable automatic
+zero-initialization of all page allocations via
+CONFIG_INIT_ON_ALLOC_DEFAULT_ON or init_on_alloc=1.
+
+Cc: stable@kernel.org
+Fixes: 2d45ba381a74 ("fuse: add retrieve request")
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[adjusted for stable: page instead of folio]
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dev.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
+index 231b1c14b76778..e5a051a624e735 100644
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -1724,6 +1724,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
+ page = find_get_page(mapping, index);
+ if (!page)
+ break;
++ if (!PageUptodate(page)) {
++ put_page(page);
++ break;
++ }
+
+ this_num = min_t(unsigned, num, PAGE_SIZE - offset);
+ ap->pages[ap->num_pages] = page;
+--
+2.53.0
+
--- /dev/null
+From 6277e99e50f9ff6fe0e7a003dd63c46696987aca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:55:04 +0800
+Subject: net/sched: act_pedit: check static offsets a priori
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit e1201bc781c28766720e78a5e099ffa568be4d74 ]
+
+Static key offsets should always be on 32 bit boundaries. Validate them on
+create/update time for static offsets and move the datapath validation
+for runtime offsets only.
+
+iproute2 already errors out if a given offset and data size cannot be
+packed to a 32 bit boundary. This change will make sure users which
+create/update pedit instances directly via netlink also error out,
+instead of finding out when packets are traversing.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit e1201bc781c28766720e78a5e099ffa568be4d74)
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index d800e0285d5c25..999ac09e33d5dc 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -250,8 +250,16 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ memcpy(nparms->tcfp_keys, parm->keys, ksize);
+
+ for (i = 0; i < nparms->tcfp_nkeys; ++i) {
++ u32 offmask = nparms->tcfp_keys[i].offmask;
+ u32 cur = nparms->tcfp_keys[i].off;
+
++ /* The AT option can be added to static offsets in the datapath */
++ if (!offmask && cur % 4) {
++ NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
++ ret = -EINVAL;
++ goto put_chain;
++ }
++
+ /* sanitize the shift value for any later use */
+ nparms->tcfp_keys[i].shift = min_t(size_t,
+ BITS_PER_TYPE(int) - 1,
+@@ -260,7 +268,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ /* The AT option can read a single byte, we can bound the actual
+ * value with uchar max.
+ */
+- cur += (0xff & nparms->tcfp_keys[i].offmask) >> nparms->tcfp_keys[i].shift;
++ cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+
+ /* Each key touches 4 bytes starting from the computed offset */
+ nparms->tcfp_off_max_hint =
+@@ -429,12 +437,12 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+- offset += (*d & tkey->offmask) >> tkey->shift;
+- }
+
+- if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
+- goto bad;
++ offset += (*d & tkey->offmask) >> tkey->shift;
++ if (offset % 4) {
++ pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ goto bad;
++ }
+ }
+
+ if (!offset_valid(skb, hoffset + offset)) {
+--
+2.53.0
+
--- /dev/null
+From e7db67a6bc4fbe88ab7855e54bc66a0622c5bae6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:55:10 +0800
+Subject: net/sched: act_pedit: free pedit keys on bail from offset check
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb ]
+
+Ido Schimmel reports a memleak on a syzkaller instance:
+ BUG: memory leak
+ unreferenced object 0xffff88803d45e400 (size 1024):
+ comm "syz-executor292", pid 563, jiffies 4295025223 (age 51.781s)
+ hex dump (first 32 bytes):
+ 28 bd 70 00 fb db df 25 02 00 14 1f ff 02 00 02 (.p....%........
+ 00 32 00 00 1f 00 00 00 ac 14 14 3e 08 00 07 00 .2.........>....
+ backtrace:
+ [<ffffffff81bd0f2c>] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
+ [<ffffffff81bd0f2c>] slab_post_alloc_hook mm/slab.h:772 [inline]
+ [<ffffffff81bd0f2c>] slab_alloc_node mm/slub.c:3452 [inline]
+ [<ffffffff81bd0f2c>] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491
+ [<ffffffff81a865d9>] __do_kmalloc_node mm/slab_common.c:966 [inline]
+ [<ffffffff81a865d9>] __kmalloc+0x59/0x1a0 mm/slab_common.c:980
+ [<ffffffff83aa85c3>] kmalloc include/linux/slab.h:584 [inline]
+ [<ffffffff83aa85c3>] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245
+ [<ffffffff83a90623>] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394
+ [<ffffffff83a90e58>] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459
+ [<ffffffff83a96258>] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985
+ [<ffffffff83a96997>] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044
+ [<ffffffff83920a8d>] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395
+ [<ffffffff83b24305>] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575
+ [<ffffffff83901806>] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413
+ [<ffffffff83b21cae>] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
+ [<ffffffff83b21cae>] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365
+ [<ffffffff83b2293f>] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942
+ [<ffffffff8380c39f>] sock_sendmsg_nosec net/socket.c:724 [inline]
+ [<ffffffff8380c39f>] sock_sendmsg net/socket.c:747 [inline]
+ [<ffffffff8380c39f>] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503
+ [<ffffffff838156d2>] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557
+ [<ffffffff8381594f>] __sys_sendmsg+0x11f/0x200 net/socket.c:2586
+ [<ffffffff83815ab0>] __do_sys_sendmsg net/socket.c:2595 [inline]
+ [<ffffffff83815ab0>] __se_sys_sendmsg net/socket.c:2593 [inline]
+ [<ffffffff83815ab0>] __x64_sys_sendmsg+0x80/0xc0 net/socket.c:2593
+
+The recently added static offset check missed a free to the key buffer when
+bailing out on error.
+
+Fixes: e1201bc781c2 ("net/sched: act_pedit: check static offsets a priori")
+Reported-by: Ido Schimmel <idosch@idosch.org>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Tested-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index f5b3b6e78b7a6b..efcbca97bf2eda 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -258,7 +258,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ if (!offmask && cur % 4) {
+ NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
+ ret = -EINVAL;
+- goto put_chain;
++ goto out_free_keys;
+ }
+
+ /* sanitize the shift value for any later use */
+@@ -283,6 +283,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+
+ return ret;
+
++out_free_keys:
++ kfree(nparms->tcfp_keys);
+ put_chain:
+ if (goto_ch)
+ tcf_chain_put_by_act(goto_ch);
+--
+2.53.0
+
--- /dev/null
+From 0e6bd4abb28768262b9720f63bc9c010c10d2bfb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:55:06 +0800
+Subject: net/sched: act_pedit: rate limit datapath messages
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a ]
+
+Unbounded info messages in the pedit datapath can flood the printk
+ring buffer quite easily depending on the action created.
+As these messages are informational, usually printing some, not all,
+is enough to bring attention to the real issue.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a)
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 999ac09e33d5dc..345fd5645e1dd2 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -429,8 +429,8 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ u8 *d, _d;
+
+ if (!offset_valid(skb, hoffset + tkey->at)) {
+- pr_info("tc action pedit 'at' offset %d out of bounds\n",
+- hoffset + tkey->at);
++ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
++ hoffset + tkey->at);
+ goto bad;
+ }
+ d = skb_header_pointer(skb, hoffset + tkey->at,
+@@ -440,14 +440,13 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ offset += (*d & tkey->offmask) >> tkey->shift;
+ if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ pr_info_ratelimited("tc action pedit offset must be on 32 bit boundaries\n");
+ goto bad;
+ }
+ }
+
+ if (!offset_valid(skb, hoffset + offset)) {
+- pr_info("tc action pedit offset %d out of bounds\n",
+- hoffset + offset);
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
+ goto bad;
+ }
+
+@@ -464,8 +463,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ val = (*ptr + tkey->val) & ~tkey->mask;
+ break;
+ default:
+- pr_info("tc action pedit bad command (%d)\n",
+- cmd);
++ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+--
+2.53.0
+
--- /dev/null
+From 74bfeffa765a0b4b52535c236dbb6e06e1899b23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:55:08 +0800
+Subject: net/sched: fix pedit partial COW leading to page cache corruption
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+
+[ Upstream commit 899ee91156e57784090c5565e4f31bd7dbffbc5a ]
+
+tcf_pedit_act() computes the COW range for skb_ensure_writable()
+once before the key loop using tcfp_off_max_hint, but the hint does
+not account for the runtime header offset added by typed keys. This
+can leave part of the write region un-COW'd.
+
+Fix by moving skb_ensure_writable() inside the per-key loop where
+the actual write offset is known, and add overflow checking on the
+offset arithmetic. For negative offsets (e.g. Ethernet header edits
+at ingress), use skb_cow() to COW the headroom instead. Guard
+offset_valid() against INT_MIN, where negation is undefined.
+
+Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable")
+Reported-by: Yiming Qian <yimingqian591@gmail.com>
+Reported-by: Keenan Dong <keenanat2000@gmail.com>
+Reported-by: Han Guidong <2045gemini@gmail.com>
+Reported-by: Zhang Cen <rollkingzzc@gmail.com>
+Reviewed-by: Han Guidong <2045gemini@gmail.com>
+Tested-by: Han Guidong <2045gemini@gmail.com>
+Reviewed-by: Davide Caratti <dcaratti@redhat.com>
+Tested-by: Davide Caratti <dcaratti@redhat.com>
+Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Tested-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Reviewed-by: Victor Nogueira <victor@mojatatu.com>
+Tested-by: Victor Nogueira <victor@mojatatu.com>
+Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Signed-off-by: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com
+[rename include file from linux/unaligned.h to asm/unaligned.h]
+Conflicts:
+ include/net/tc_act/tc_pedit.h
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/tc_act/tc_pedit.h | 1 -
+ net/sched/act_pedit.c | 77 +++++++++++++++++++----------------
+ 2 files changed, 41 insertions(+), 37 deletions(-)
+
+diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
+index 83fe3993178180..a26d4cd3b8d6f3 100644
+--- a/include/net/tc_act/tc_pedit.h
++++ b/include/net/tc_act/tc_pedit.h
+@@ -14,7 +14,6 @@ struct tcf_pedit_key_ex {
+ struct tcf_pedit_parms {
+ struct tc_pedit_key *tcfp_keys;
+ struct tcf_pedit_key_ex *tcfp_keys_ex;
+- u32 tcfp_off_max_hint;
+ unsigned char tcfp_nkeys;
+ unsigned char tcfp_flags;
+ struct rcu_head rcu;
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 345fd5645e1dd2..f5b3b6e78b7a6b 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -16,6 +16,8 @@
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
+ #include <linux/slab.h>
++#include <linux/overflow.h>
++#include <asm/unaligned.h>
+ #include <net/ipv6.h>
+ #include <net/netlink.h>
+ #include <net/pkt_sched.h>
+@@ -237,7 +239,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ goto out_free_ex;
+ }
+
+- nparms->tcfp_off_max_hint = 0;
+ nparms->tcfp_flags = parm->flags;
+ nparms->tcfp_nkeys = parm->nkeys;
+
+@@ -265,14 +266,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ BITS_PER_TYPE(int) - 1,
+ nparms->tcfp_keys[i].shift);
+
+- /* The AT option can read a single byte, we can bound the actual
+- * value with uchar max.
+- */
+- cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+-
+- /* Each key touches 4 bytes starting from the computed offset */
+- nparms->tcfp_off_max_hint =
+- max(nparms->tcfp_off_max_hint, cur + 4);
+ }
+
+ p = to_pedit(*a);
+@@ -313,15 +306,12 @@ static void tcf_pedit_cleanup(struct tc_action *a)
+ call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
+ }
+
+-static bool offset_valid(struct sk_buff *skb, int offset)
++static bool offset_valid(struct sk_buff *skb, int offset, int len)
+ {
+- if (offset > 0 && offset > skb->len)
+- return false;
+-
+- if (offset < 0 && -offset > skb_headroom(skb))
++ if (offset < -(int)skb_headroom(skb))
+ return false;
+
+- return true;
++ return offset <= (int)skb->len - len;
+ }
+
+ static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
+@@ -387,18 +377,10 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ struct tcf_pedit_key_ex *tkey_ex;
+ struct tcf_pedit_parms *parms;
+ struct tc_pedit_key *tkey;
+- u32 max_offset;
+ int i;
+
+ parms = rcu_dereference_bh(p->parms);
+
+- max_offset = (skb_transport_header_was_set(skb) ?
+- skb_transport_offset(skb) :
+- skb_network_offset(skb)) +
+- parms->tcfp_off_max_hint;
+- if (skb_ensure_writable(skb, min(skb->len, max_offset)))
+- goto done;
+-
+ tcf_lastuse_update(&p->tcf_tm);
+ tcf_action_update_bstats(&p->common, skb);
+
+@@ -406,10 +388,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ tkey_ex = parms->tcfp_keys_ex;
+
+ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
++ int write_offset, write_len;
+ int offset = tkey->off;
+ int hoffset = 0;
+- u32 *ptr, hdata;
+- u32 val;
++ u32 cur_val, val;
++ u32 *ptr;
+ int rc;
+
+ if (tkey_ex) {
+@@ -427,13 +410,15 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ if (tkey->offmask) {
+ u8 *d, _d;
++ int at_offset;
+
+- if (!offset_valid(skb, hoffset + tkey->at)) {
++ if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) ||
++ !offset_valid(skb, at_offset, sizeof(_d))) {
+ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
+ hoffset + tkey->at);
+ goto bad;
+ }
+- d = skb_header_pointer(skb, hoffset + tkey->at,
++ d = skb_header_pointer(skb, at_offset,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+@@ -445,31 +430,51 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ }
+ }
+
+- if (!offset_valid(skb, hoffset + offset)) {
+- pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
++ if (check_add_overflow(hoffset, offset, &write_offset)) {
++ pr_info_ratelimited("tc action pedit offset overflow\n");
+ goto bad;
+ }
+
+- ptr = skb_header_pointer(skb, hoffset + offset,
+- sizeof(hdata), &hdata);
+- if (!ptr)
++ if (!offset_valid(skb, write_offset, sizeof(*ptr))) {
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n",
++ write_offset);
+ goto bad;
++ }
++
++ if (write_offset < 0) {
++ if (skb_cow(skb, -write_offset))
++ goto bad;
++ if (write_offset + (int)sizeof(*ptr) > 0) {
++ if (skb_ensure_writable(skb,
++ min_t(int, skb->len,
++ write_offset + (int)sizeof(*ptr))))
++ goto bad;
++ }
++ } else {
++ if (check_add_overflow(write_offset, (int)sizeof(*ptr),
++ &write_len))
++ goto bad;
++ if (skb_ensure_writable(skb, min_t(int, skb->len,
++ write_len)))
++ goto bad;
++ }
++
++ ptr = (u32 *)(skb->data + write_offset);
++ cur_val = get_unaligned(ptr);
+ /* just do it, baby */
+ switch (cmd) {
+ case TCA_PEDIT_KEY_EX_CMD_SET:
+ val = tkey->val;
+ break;
+ case TCA_PEDIT_KEY_EX_CMD_ADD:
+- val = (*ptr + tkey->val) & ~tkey->mask;
++ val = (cur_val + tkey->val) & ~tkey->mask;
+ break;
+ default:
+ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+- *ptr = ((*ptr & tkey->mask) ^ val);
+- if (ptr == &hdata)
+- skb_store_bits(skb, hoffset + offset, ptr, 4);
++ put_unaligned((cur_val & tkey->mask) ^ val, ptr);
+ }
+
+ goto done;
+--
+2.53.0
+
--- /dev/null
+fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
+net-sched-act_pedit-check-static-offsets-a-priori.patch
+net-sched-act_pedit-rate-limit-datapath-messages.patch
+net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
+net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch
--- /dev/null
+From ad1a051b51afd0fc2ed5b2abe21cff701777e4fa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 21:00:23 +0200
+Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 4e3d1b2c48ca6c55f1e9ca7f8dccc76f120f276c ]
+
+FUSE_NOTIFY_RETRIEVE must be limited to uptodate folios; !uptodate folios
+can contain uninitialized data.
+Since FUSE_NOTIFY_RETRIEVE is intended to only return data that is already
+in the page cache and not wait for data from the FUSE daemon, treat
+!uptodate folios as if they weren't present.
+
+This only has security impact on systems that don't enable automatic
+zero-initialization of all page allocations via
+CONFIG_INIT_ON_ALLOC_DEFAULT_ON or init_on_alloc=1.
+
+Cc: stable@kernel.org
+Fixes: 2d45ba381a74 ("fuse: add retrieve request")
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[adjusted for stable: page instead of folio]
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dev.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
+index 58ec5bac6ce9d1..abee0cd67182f3 100644
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -1718,6 +1718,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
+ page = find_get_page(mapping, index);
+ if (!page)
+ break;
++ if (!PageUptodate(page)) {
++ put_page(page);
++ break;
++ }
+
+ this_num = min_t(unsigned, num, PAGE_SIZE - offset);
+ ap->pages[ap->num_pages] = page;
+--
+2.53.0
+
--- /dev/null
+From 9e83a3ccc3afcc78eed4e7ad29c6d0fb50e3199d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 13:51:00 +0300
+Subject: KVM: VMX: Make vmread_error_trampoline() uncallable from C code
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sean Christopherson <seanjc@google.com>
+
+[ Upstream commit 0b5e7a16a0a79a3742f0df9e45bca46f01b40e6a ]
+
+Declare vmread_error_trampoline() as an opaque symbol so that it cannot
+be called from C code, at least not without some serious fudging. The
+trampoline always passes parameters on the stack so that the inline
+VMREAD sequence doesn't need to clobber registers. regparm(0) was
+originally added to document the stack behavior, but it ended up being
+confusing because regparm(0) is a nop for 64-bit targets.
+
+Opportunustically wrap the trampoline and its declaration in #ifdeffery
+to make it even harder to invoke incorrectly, to document why it exists,
+and so that it's not left behind if/when CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+is true for all supported toolchains.
+
+No functional change intended.
+
+Cc: Uros Bizjak <ubizjak@gmail.com>
+Signed-off-by: Sean Christopherson <seanjc@google.com>
+Link: https://lore.kernel.org/r/20220928232015.745948-1-seanjc@google.com
+(cherry picked from commit 0b5e7a16a0a79a3742f0df9e45bca46f01b40e6a)
+Signed-off-by: Hanne-Lotta Mäenpää <hannelotta@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kvm/vmx/vmenter.S | 2 ++
+ arch/x86/kvm/vmx/vmx_ops.h | 18 ++++++++++++++++--
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S
+index b4f8937226c211..ea7be3a74b6d02 100644
+--- a/arch/x86/kvm/vmx/vmenter.S
++++ b/arch/x86/kvm/vmx/vmenter.S
+@@ -274,6 +274,7 @@ SYM_FUNC_END(__vmx_vcpu_run)
+
+ .section .text, "ax"
+
++#ifndef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+ /**
+ * vmread_error_trampoline - Trampoline from inline asm to vmread_error()
+ * @field: VMCS field encoding that failed
+@@ -322,6 +323,7 @@ SYM_FUNC_START(vmread_error_trampoline)
+
+ RET
+ SYM_FUNC_END(vmread_error_trampoline)
++#endif
+
+ SYM_FUNC_START(vmx_do_interrupt_nmi_irqoff)
+ /*
+diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h
+index 5edab28dfb2efe..d23705df6a5250 100644
+--- a/arch/x86/kvm/vmx/vmx_ops.h
++++ b/arch/x86/kvm/vmx/vmx_ops.h
+@@ -11,14 +11,28 @@
+ #include "../x86.h"
+
+ void vmread_error(unsigned long field, bool fault);
+-__attribute__((regparm(0))) void vmread_error_trampoline(unsigned long field,
+- bool fault);
+ void vmwrite_error(unsigned long field, unsigned long value);
+ void vmclear_error(struct vmcs *vmcs, u64 phys_addr);
+ void vmptrld_error(struct vmcs *vmcs, u64 phys_addr);
+ void invvpid_error(unsigned long ext, u16 vpid, gva_t gva);
+ void invept_error(unsigned long ext, u64 eptp, gpa_t gpa);
+
++#ifndef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
++/*
++ * The VMREAD error trampoline _always_ uses the stack to pass parameters, even
++ * for 64-bit targets. Preserving all registers allows the VMREAD inline asm
++ * blob to avoid clobbering GPRs, which in turn allows the compiler to better
++ * optimize sequences of VMREADs.
++ *
++ * Declare the trampoline as an opaque label as it's not safe to call from C
++ * code; there is no way to tell the compiler to pass params on the stack for
++ * 64-bit targets.
++ *
++ * void vmread_error_trampoline(unsigned long field, bool fault);
++ */
++extern unsigned long vmread_error_trampoline;
++#endif
++
+ static __always_inline void vmcs_check16(unsigned long field)
+ {
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2000,
+--
+2.53.0
+
--- /dev/null
+From 3a6d2fa661f7656af8f9e56f62f0a78b28e4c633 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:42:48 +0800
+Subject: net/sched: act_pedit: check static offsets a priori
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit e1201bc781c28766720e78a5e099ffa568be4d74 ]
+
+Static key offsets should always be on 32 bit boundaries. Validate them on
+create/update time for static offsets and move the datapath validation
+for runtime offsets only.
+
+iproute2 already errors out if a given offset and data size cannot be
+packed to a 32 bit boundary. This change will make sure users which
+create/update pedit instances directly via netlink also error out,
+instead of finding out when packets are traversing.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit e1201bc781c28766720e78a5e099ffa568be4d74)
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index aee2e13f1db624..2bdc44efee779a 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -250,8 +250,16 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ memcpy(nparms->tcfp_keys, parm->keys, ksize);
+
+ for (i = 0; i < nparms->tcfp_nkeys; ++i) {
++ u32 offmask = nparms->tcfp_keys[i].offmask;
+ u32 cur = nparms->tcfp_keys[i].off;
+
++ /* The AT option can be added to static offsets in the datapath */
++ if (!offmask && cur % 4) {
++ NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
++ ret = -EINVAL;
++ goto put_chain;
++ }
++
+ /* sanitize the shift value for any later use */
+ nparms->tcfp_keys[i].shift = min_t(size_t,
+ BITS_PER_TYPE(int) - 1,
+@@ -260,7 +268,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ /* The AT option can read a single byte, we can bound the actual
+ * value with uchar max.
+ */
+- cur += (0xff & nparms->tcfp_keys[i].offmask) >> nparms->tcfp_keys[i].shift;
++ cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+
+ /* Each key touches 4 bytes starting from the computed offset */
+ nparms->tcfp_off_max_hint =
+@@ -429,12 +437,12 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+- offset += (*d & tkey->offmask) >> tkey->shift;
+- }
+
+- if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
+- goto bad;
++ offset += (*d & tkey->offmask) >> tkey->shift;
++ if (offset % 4) {
++ pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ goto bad;
++ }
+ }
+
+ if (!offset_valid(skb, hoffset + offset)) {
+--
+2.53.0
+
--- /dev/null
+From 2da6b49af1372a16ceba89f9472546d80088fc87 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:48:48 +0800
+Subject: net/sched: act_pedit: free pedit keys on bail from offset check
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb ]
+
+Ido Schimmel reports a memleak on a syzkaller instance:
+ BUG: memory leak
+ unreferenced object 0xffff88803d45e400 (size 1024):
+ comm "syz-executor292", pid 563, jiffies 4295025223 (age 51.781s)
+ hex dump (first 32 bytes):
+ 28 bd 70 00 fb db df 25 02 00 14 1f ff 02 00 02 (.p....%........
+ 00 32 00 00 1f 00 00 00 ac 14 14 3e 08 00 07 00 .2.........>....
+ backtrace:
+ [<ffffffff81bd0f2c>] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
+ [<ffffffff81bd0f2c>] slab_post_alloc_hook mm/slab.h:772 [inline]
+ [<ffffffff81bd0f2c>] slab_alloc_node mm/slub.c:3452 [inline]
+ [<ffffffff81bd0f2c>] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491
+ [<ffffffff81a865d9>] __do_kmalloc_node mm/slab_common.c:966 [inline]
+ [<ffffffff81a865d9>] __kmalloc+0x59/0x1a0 mm/slab_common.c:980
+ [<ffffffff83aa85c3>] kmalloc include/linux/slab.h:584 [inline]
+ [<ffffffff83aa85c3>] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245
+ [<ffffffff83a90623>] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394
+ [<ffffffff83a90e58>] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459
+ [<ffffffff83a96258>] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985
+ [<ffffffff83a96997>] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044
+ [<ffffffff83920a8d>] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395
+ [<ffffffff83b24305>] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575
+ [<ffffffff83901806>] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413
+ [<ffffffff83b21cae>] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
+ [<ffffffff83b21cae>] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365
+ [<ffffffff83b2293f>] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942
+ [<ffffffff8380c39f>] sock_sendmsg_nosec net/socket.c:724 [inline]
+ [<ffffffff8380c39f>] sock_sendmsg net/socket.c:747 [inline]
+ [<ffffffff8380c39f>] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503
+ [<ffffffff838156d2>] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557
+ [<ffffffff8381594f>] __sys_sendmsg+0x11f/0x200 net/socket.c:2586
+ [<ffffffff83815ab0>] __do_sys_sendmsg net/socket.c:2595 [inline]
+ [<ffffffff83815ab0>] __se_sys_sendmsg net/socket.c:2593 [inline]
+ [<ffffffff83815ab0>] __x64_sys_sendmsg+0x80/0xc0 net/socket.c:2593
+
+The recently added static offset check missed a free to the key buffer when
+bailing out on error.
+
+Fixes: e1201bc781c2 ("net/sched: act_pedit: check static offsets a priori")
+Reported-by: Ido Schimmel <idosch@idosch.org>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Tested-by: Ido Schimmel <idosch@nvidia.com>
+Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+(cherry picked from commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb)
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 1b076c4f2d1af5..09310b56b45beb 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -258,7 +258,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ if (!offmask && cur % 4) {
+ NL_SET_ERR_MSG_MOD(extack, "Offsets must be on 32bit boundaries");
+ ret = -EINVAL;
+- goto put_chain;
++ goto out_free_keys;
+ }
+
+ /* sanitize the shift value for any later use */
+@@ -283,6 +283,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+
+ return ret;
+
++out_free_keys:
++ kfree(nparms->tcfp_keys);
+ put_chain:
+ if (goto_ch)
+ tcf_chain_put_by_act(goto_ch);
+--
+2.53.0
+
--- /dev/null
+From d1f0ba56cadeaa4f2d6897dd6c6848bf998896a4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:42:49 +0800
+Subject: net/sched: act_pedit: rate limit datapath messages
+
+From: Pedro Tammela <pctammela@mojatatu.com>
+
+[ Upstream commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a ]
+
+Unbounded info messages in the pedit datapath can flood the printk
+ring buffer quite easily depending on the action created.
+As these messages are informational, usually printing some, not all,
+is enough to bring attention to the real issue.
+
+Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: Pedro Tammela <pctammela@mojatatu.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a)
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sched/act_pedit.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 2bdc44efee779a..7ba460fda1f4e9 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -429,8 +429,8 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ u8 *d, _d;
+
+ if (!offset_valid(skb, hoffset + tkey->at)) {
+- pr_info("tc action pedit 'at' offset %d out of bounds\n",
+- hoffset + tkey->at);
++ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
++ hoffset + tkey->at);
+ goto bad;
+ }
+ d = skb_header_pointer(skb, hoffset + tkey->at,
+@@ -440,14 +440,13 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ offset += (*d & tkey->offmask) >> tkey->shift;
+ if (offset % 4) {
+- pr_info("tc action pedit offset must be on 32 bit boundaries\n");
++ pr_info_ratelimited("tc action pedit offset must be on 32 bit boundaries\n");
+ goto bad;
+ }
+ }
+
+ if (!offset_valid(skb, hoffset + offset)) {
+- pr_info("tc action pedit offset %d out of bounds\n",
+- hoffset + offset);
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
+ goto bad;
+ }
+
+@@ -464,8 +463,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ val = (*ptr + tkey->val) & ~tkey->mask;
+ break;
+ default:
+- pr_info("tc action pedit bad command (%d)\n",
+- cmd);
++ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+--
+2.53.0
+
--- /dev/null
+From 97f407db528043146b47e25b45f6a11b997ddc10 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:42:50 +0800
+Subject: net/sched: fix pedit partial COW leading to page cache corruption
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+
+[ Upstream commit 899ee91156e57784090c5565e4f31bd7dbffbc5a ]
+
+tcf_pedit_act() computes the COW range for skb_ensure_writable()
+once before the key loop using tcfp_off_max_hint, but the hint does
+not account for the runtime header offset added by typed keys. This
+can leave part of the write region un-COW'd.
+
+Fix by moving skb_ensure_writable() inside the per-key loop where
+the actual write offset is known, and add overflow checking on the
+offset arithmetic. For negative offsets (e.g. Ethernet header edits
+at ingress), use skb_cow() to COW the headroom instead. Guard
+offset_valid() against INT_MIN, where negation is undefined.
+
+Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable")
+Reported-by: Yiming Qian <yimingqian591@gmail.com>
+Reported-by: Keenan Dong <keenanat2000@gmail.com>
+Reported-by: Han Guidong <2045gemini@gmail.com>
+Reported-by: Zhang Cen <rollkingzzc@gmail.com>
+Reviewed-by: Han Guidong <2045gemini@gmail.com>
+Tested-by: Han Guidong <2045gemini@gmail.com>
+Reviewed-by: Davide Caratti <dcaratti@redhat.com>
+Tested-by: Davide Caratti <dcaratti@redhat.com>
+Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Tested-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Reviewed-by: Victor Nogueira <victor@mojatatu.com>
+Tested-by: Victor Nogueira <victor@mojatatu.com>
+Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Signed-off-by: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com
+[rename include file from linux/unaligned.h to asm/unaligned.h]
+Conflicts:
+ include/net/tc_act/tc_pedit.h
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/tc_act/tc_pedit.h | 1 -
+ net/sched/act_pedit.c | 77 +++++++++++++++++++----------------
+ 2 files changed, 41 insertions(+), 37 deletions(-)
+
+diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
+index 83fe3993178180..a26d4cd3b8d6f3 100644
+--- a/include/net/tc_act/tc_pedit.h
++++ b/include/net/tc_act/tc_pedit.h
+@@ -14,7 +14,6 @@ struct tcf_pedit_key_ex {
+ struct tcf_pedit_parms {
+ struct tc_pedit_key *tcfp_keys;
+ struct tcf_pedit_key_ex *tcfp_keys_ex;
+- u32 tcfp_off_max_hint;
+ unsigned char tcfp_nkeys;
+ unsigned char tcfp_flags;
+ struct rcu_head rcu;
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 7ba460fda1f4e9..1b076c4f2d1af5 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -16,6 +16,8 @@
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
+ #include <linux/slab.h>
++#include <linux/overflow.h>
++#include <asm/unaligned.h>
+ #include <net/ipv6.h>
+ #include <net/netlink.h>
+ #include <net/pkt_sched.h>
+@@ -237,7 +239,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ goto out_free_ex;
+ }
+
+- nparms->tcfp_off_max_hint = 0;
+ nparms->tcfp_flags = parm->flags;
+ nparms->tcfp_nkeys = parm->nkeys;
+
+@@ -265,14 +266,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ BITS_PER_TYPE(int) - 1,
+ nparms->tcfp_keys[i].shift);
+
+- /* The AT option can read a single byte, we can bound the actual
+- * value with uchar max.
+- */
+- cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+-
+- /* Each key touches 4 bytes starting from the computed offset */
+- nparms->tcfp_off_max_hint =
+- max(nparms->tcfp_off_max_hint, cur + 4);
+ }
+
+ p = to_pedit(*a);
+@@ -313,15 +306,12 @@ static void tcf_pedit_cleanup(struct tc_action *a)
+ call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
+ }
+
+-static bool offset_valid(struct sk_buff *skb, int offset)
++static bool offset_valid(struct sk_buff *skb, int offset, int len)
+ {
+- if (offset > 0 && offset > skb->len)
+- return false;
+-
+- if (offset < 0 && -offset > skb_headroom(skb))
++ if (offset < -(int)skb_headroom(skb))
+ return false;
+
+- return true;
++ return offset <= (int)skb->len - len;
+ }
+
+ static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
+@@ -387,18 +377,10 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ struct tcf_pedit_key_ex *tkey_ex;
+ struct tcf_pedit_parms *parms;
+ struct tc_pedit_key *tkey;
+- u32 max_offset;
+ int i;
+
+ parms = rcu_dereference_bh(p->parms);
+
+- max_offset = (skb_transport_header_was_set(skb) ?
+- skb_transport_offset(skb) :
+- skb_network_offset(skb)) +
+- parms->tcfp_off_max_hint;
+- if (skb_ensure_writable(skb, min(skb->len, max_offset)))
+- goto done;
+-
+ tcf_lastuse_update(&p->tcf_tm);
+ tcf_action_update_bstats(&p->common, skb);
+
+@@ -406,10 +388,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ tkey_ex = parms->tcfp_keys_ex;
+
+ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
++ int write_offset, write_len;
+ int offset = tkey->off;
+ int hoffset = 0;
+- u32 *ptr, hdata;
+- u32 val;
++ u32 cur_val, val;
++ u32 *ptr;
+ int rc;
+
+ if (tkey_ex) {
+@@ -427,13 +410,15 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+
+ if (tkey->offmask) {
+ u8 *d, _d;
++ int at_offset;
+
+- if (!offset_valid(skb, hoffset + tkey->at)) {
++ if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) ||
++ !offset_valid(skb, at_offset, sizeof(_d))) {
+ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
+ hoffset + tkey->at);
+ goto bad;
+ }
+- d = skb_header_pointer(skb, hoffset + tkey->at,
++ d = skb_header_pointer(skb, at_offset,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+@@ -445,31 +430,51 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
+ }
+ }
+
+- if (!offset_valid(skb, hoffset + offset)) {
+- pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
++ if (check_add_overflow(hoffset, offset, &write_offset)) {
++ pr_info_ratelimited("tc action pedit offset overflow\n");
+ goto bad;
+ }
+
+- ptr = skb_header_pointer(skb, hoffset + offset,
+- sizeof(hdata), &hdata);
+- if (!ptr)
++ if (!offset_valid(skb, write_offset, sizeof(*ptr))) {
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n",
++ write_offset);
+ goto bad;
++ }
++
++ if (write_offset < 0) {
++ if (skb_cow(skb, -write_offset))
++ goto bad;
++ if (write_offset + (int)sizeof(*ptr) > 0) {
++ if (skb_ensure_writable(skb,
++ min_t(int, skb->len,
++ write_offset + (int)sizeof(*ptr))))
++ goto bad;
++ }
++ } else {
++ if (check_add_overflow(write_offset, (int)sizeof(*ptr),
++ &write_len))
++ goto bad;
++ if (skb_ensure_writable(skb, min_t(int, skb->len,
++ write_len)))
++ goto bad;
++ }
++
++ ptr = (u32 *)(skb->data + write_offset);
++ cur_val = get_unaligned(ptr);
+ /* just do it, baby */
+ switch (cmd) {
+ case TCA_PEDIT_KEY_EX_CMD_SET:
+ val = tkey->val;
+ break;
+ case TCA_PEDIT_KEY_EX_CMD_ADD:
+- val = (*ptr + tkey->val) & ~tkey->mask;
++ val = (cur_val + tkey->val) & ~tkey->mask;
+ break;
+ default:
+ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+- *ptr = ((*ptr & tkey->mask) ^ val);
+- if (ptr == &hdata)
+- skb_store_bits(skb, hoffset + offset, ptr, 4);
++ put_unaligned((cur_val & tkey->mask) ^ val, ptr);
+ }
+
+ goto done;
+--
+2.53.0
+
--- /dev/null
+From 0b8a74b3bed2508da5d3c54e740379bbca6b4116 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 09:40:31 +0800
+Subject: selftests/bpf: move SYS() macro into the test_progs.h
+
+From: Hangbin Liu <liuhangbin@gmail.com>
+
+commit b61987d37cbee3c44e80304598c60b163553926b upstream.
+
+A lot of tests defined SYS() macro to run system calls with goto label.
+Let's move this macro to test_progs.h and add configurable
+"goto_label" as the first arg.
+
+Suggested-by: Martin KaFai Lau <martin.lau@linux.dev>
+Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
+Link: https://lore.kernel.org/r/20230224061343.506571-2-liuhangbin@gmail.com
+Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
+Stable-dep-of: 967e8def1100 ("selftests/bpf: Fix bpf_nf selftest failure")
+[shung-hsi.yu: changes to several files are dropped because they don't exist
+yet: decap_sanity.c, fib_lookup.c, xdp_metadata.c, and xfrm_info.c. Addional
+changes are introdced to tc_redirect.c to patch SYS() macro usage not found
+in upstream. ]
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/bpf/prog_tests/empty_skb.c | 25 ++-
+ .../selftests/bpf/prog_tests/tc_redirect.c | 154 +++++++++---------
+ .../selftests/bpf/prog_tests/test_tunnel.c | 71 ++++----
+ .../selftests/bpf/prog_tests/xdp_bonding.c | 40 ++---
+ .../bpf/prog_tests/xdp_do_redirect.c | 30 ++--
+ .../selftests/bpf/prog_tests/xdp_synproxy.c | 41 ++---
+ tools/testing/selftests/bpf/test_progs.h | 15 ++
+ 7 files changed, 171 insertions(+), 205 deletions(-)
+
+diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
+index 329e34e5226e3a..33c8631fc1a7f0 100644
+--- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c
++++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c
+@@ -4,11 +4,6 @@
+ #include <net/if.h>
+ #include "empty_skb.skel.h"
+
+-#define SYS(cmd) ({ \
+- if (!ASSERT_OK(system(cmd), (cmd))) \
+- goto out; \
+-})
+-
+ void serial_test_empty_skb(void)
+ {
+ LIBBPF_OPTS(bpf_test_run_opts, tattr);
+@@ -97,18 +92,18 @@ void serial_test_empty_skb(void)
+ },
+ };
+
+- SYS("ip netns add empty_skb");
++ SYS(out, "ip netns add empty_skb");
+ tok = open_netns("empty_skb");
+- SYS("ip link add veth0 type veth peer veth1");
+- SYS("ip link set dev veth0 up");
+- SYS("ip link set dev veth1 up");
+- SYS("ip addr add 10.0.0.1/8 dev veth0");
+- SYS("ip addr add 10.0.0.2/8 dev veth1");
++ SYS(out, "ip link add veth0 type veth peer veth1");
++ SYS(out, "ip link set dev veth0 up");
++ SYS(out, "ip link set dev veth1 up");
++ SYS(out, "ip addr add 10.0.0.1/8 dev veth0");
++ SYS(out, "ip addr add 10.0.0.2/8 dev veth1");
+ veth_ifindex = if_nametoindex("veth0");
+
+- SYS("ip link add ipip0 type ipip local 10.0.0.1 remote 10.0.0.2");
+- SYS("ip link set ipip0 up");
+- SYS("ip addr add 192.168.1.1/16 dev ipip0");
++ SYS(out, "ip link add ipip0 type ipip local 10.0.0.1 remote 10.0.0.2");
++ SYS(out, "ip link set ipip0 up");
++ SYS(out, "ip addr add 192.168.1.1/16 dev ipip0");
+ ipip_ifindex = if_nametoindex("ipip0");
+
+ bpf_obj = empty_skb__open_and_load();
+@@ -150,5 +145,5 @@ void serial_test_empty_skb(void)
+ empty_skb__destroy(bpf_obj);
+ if (tok)
+ close_netns(tok);
+- system("ip netns del empty_skb");
++ SYS_NOFAIL("ip netns del empty_skb");
+ }
+diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
+index cb6a53b3e023c6..c01733b5c4beb0 100644
+--- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
++++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
+@@ -160,24 +160,16 @@ static int get_ifindex(const char *name)
+ return atoi(buf);
+ }
+
+-#define SYS(fmt, ...) \
+- ({ \
+- char cmd[1024]; \
+- snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
+- if (!ASSERT_OK(system(cmd), cmd)) \
+- goto fail; \
+- })
+-
+ static int netns_setup_links_and_routes(struct netns_setup_result *result)
+ {
+ struct nstoken *nstoken = NULL;
+ char veth_src_fwd_addr[IFADDR_STR_LEN+1] = {};
+
+- SYS("ip link add veth_src type veth peer name veth_src_fwd");
+- SYS("ip link add veth_dst type veth peer name veth_dst_fwd");
++ SYS(fail, "ip link add veth_src type veth peer name veth_src_fwd");
++ SYS(fail, "ip link add veth_dst type veth peer name veth_dst_fwd");
+
+- SYS("ip link set veth_dst_fwd address " MAC_DST_FWD);
+- SYS("ip link set veth_dst address " MAC_DST);
++ SYS(fail, "ip link set veth_dst_fwd address " MAC_DST_FWD);
++ SYS(fail, "ip link set veth_dst address " MAC_DST);
+
+ if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr))
+ goto fail;
+@@ -189,27 +181,27 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)
+ if (result->ifindex_veth_dst_fwd < 0)
+ goto fail;
+
+- SYS("ip link set veth_src netns " NS_SRC);
+- SYS("ip link set veth_src_fwd netns " NS_FWD);
+- SYS("ip link set veth_dst_fwd netns " NS_FWD);
+- SYS("ip link set veth_dst netns " NS_DST);
++ SYS(fail, "ip link set veth_src netns " NS_SRC);
++ SYS(fail, "ip link set veth_src_fwd netns " NS_FWD);
++ SYS(fail, "ip link set veth_dst_fwd netns " NS_FWD);
++ SYS(fail, "ip link set veth_dst netns " NS_DST);
+
+ /** setup in 'src' namespace */
+ nstoken = open_netns(NS_SRC);
+ if (!ASSERT_OK_PTR(nstoken, "setns src"))
+ goto fail;
+
+- SYS("ip addr add " IP4_SRC "/32 dev veth_src");
+- SYS("ip addr add " IP6_SRC "/128 dev veth_src nodad");
+- SYS("ip link set dev veth_src up");
++ SYS(fail, "ip addr add " IP4_SRC "/32 dev veth_src");
++ SYS(fail, "ip addr add " IP6_SRC "/128 dev veth_src nodad");
++ SYS(fail, "ip link set dev veth_src up");
+
+- SYS("ip route add " IP4_DST "/32 dev veth_src scope global");
+- SYS("ip route add " IP4_NET "/16 dev veth_src scope global");
+- SYS("ip route add " IP6_DST "/128 dev veth_src scope global");
++ SYS(fail, "ip route add " IP4_DST "/32 dev veth_src scope global");
++ SYS(fail, "ip route add " IP4_NET "/16 dev veth_src scope global");
++ SYS(fail, "ip route add " IP6_DST "/128 dev veth_src scope global");
+
+- SYS("ip neigh add " IP4_DST " dev veth_src lladdr %s",
++ SYS(fail, "ip neigh add " IP4_DST " dev veth_src lladdr %s",
+ veth_src_fwd_addr);
+- SYS("ip neigh add " IP6_DST " dev veth_src lladdr %s",
++ SYS(fail, "ip neigh add " IP6_DST " dev veth_src lladdr %s",
+ veth_src_fwd_addr);
+
+ close_netns(nstoken);
+@@ -223,15 +215,15 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)
+ * needs v4 one in order to start ARP probing. IP4_NET route is added
+ * to the endpoints so that the ARP processing will reply.
+ */
+- SYS("ip addr add " IP4_SLL "/32 dev veth_src_fwd");
+- SYS("ip addr add " IP4_DLL "/32 dev veth_dst_fwd");
+- SYS("ip link set dev veth_src_fwd up");
+- SYS("ip link set dev veth_dst_fwd up");
++ SYS(fail, "ip addr add " IP4_SLL "/32 dev veth_src_fwd");
++ SYS(fail, "ip addr add " IP4_DLL "/32 dev veth_dst_fwd");
++ SYS(fail, "ip link set dev veth_src_fwd up");
++ SYS(fail, "ip link set dev veth_dst_fwd up");
+
+- SYS("ip route add " IP4_SRC "/32 dev veth_src_fwd scope global");
+- SYS("ip route add " IP6_SRC "/128 dev veth_src_fwd scope global");
+- SYS("ip route add " IP4_DST "/32 dev veth_dst_fwd scope global");
+- SYS("ip route add " IP6_DST "/128 dev veth_dst_fwd scope global");
++ SYS(fail, "ip route add " IP4_SRC "/32 dev veth_src_fwd scope global");
++ SYS(fail, "ip route add " IP6_SRC "/128 dev veth_src_fwd scope global");
++ SYS(fail, "ip route add " IP4_DST "/32 dev veth_dst_fwd scope global");
++ SYS(fail, "ip route add " IP6_DST "/128 dev veth_dst_fwd scope global");
+
+ close_netns(nstoken);
+
+@@ -240,16 +232,16 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)
+ if (!ASSERT_OK_PTR(nstoken, "setns dst"))
+ goto fail;
+
+- SYS("ip addr add " IP4_DST "/32 dev veth_dst");
+- SYS("ip addr add " IP6_DST "/128 dev veth_dst nodad");
+- SYS("ip link set dev veth_dst up");
++ SYS(fail, "ip addr add " IP4_DST "/32 dev veth_dst");
++ SYS(fail, "ip addr add " IP6_DST "/128 dev veth_dst nodad");
++ SYS(fail, "ip link set dev veth_dst up");
+
+- SYS("ip route add " IP4_SRC "/32 dev veth_dst scope global");
+- SYS("ip route add " IP4_NET "/16 dev veth_dst scope global");
+- SYS("ip route add " IP6_SRC "/128 dev veth_dst scope global");
++ SYS(fail, "ip route add " IP4_SRC "/32 dev veth_dst scope global");
++ SYS(fail, "ip route add " IP4_NET "/16 dev veth_dst scope global");
++ SYS(fail, "ip route add " IP6_SRC "/128 dev veth_dst scope global");
+
+- SYS("ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD);
+- SYS("ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD);
++ SYS(fail, "ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD);
++ SYS(fail, "ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD);
+
+ close_netns(nstoken);
+
+@@ -262,16 +254,16 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)
+
+ static int netns_load_bpf(void)
+ {
+- SYS("tc qdisc add dev veth_src_fwd clsact");
+- SYS("tc filter add dev veth_src_fwd ingress bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_src_fwd clsact");
++ SYS(fail, "tc filter add dev veth_src_fwd ingress bpf da object-pinned "
+ SRC_PROG_PIN_FILE);
+- SYS("tc filter add dev veth_src_fwd egress bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_src_fwd egress bpf da object-pinned "
+ CHK_PROG_PIN_FILE);
+
+- SYS("tc qdisc add dev veth_dst_fwd clsact");
+- SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_dst_fwd clsact");
++ SYS(fail, "tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
+ DST_PROG_PIN_FILE);
+- SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_dst_fwd egress bpf da object-pinned "
+ CHK_PROG_PIN_FILE);
+
+ return 0;
+@@ -330,7 +322,7 @@ static void test_tcp(int family, const char *addr, __u16 port)
+
+ static int test_ping(int family, const char *addr)
+ {
+- SYS("ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr);
++ SYS(fail, "ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr);
+ return 0;
+ fail:
+ return -1;
+@@ -516,10 +508,10 @@ static int netns_load_dtime_bpf(struct test_tc_dtime *skel)
+ return -1;
+ PIN(egress_host);
+ PIN(ingress_host);
+- SYS("tc qdisc add dev veth_src clsact");
+- SYS("tc filter add dev veth_src ingress bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_src clsact");
++ SYS(fail, "tc filter add dev veth_src ingress bpf da object-pinned "
+ PIN_FNAME(ingress_host));
+- SYS("tc filter add dev veth_src egress bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_src egress bpf da object-pinned "
+ PIN_FNAME(egress_host));
+ close_netns(nstoken);
+
+@@ -529,10 +521,10 @@ static int netns_load_dtime_bpf(struct test_tc_dtime *skel)
+ return -1;
+ PIN(egress_host);
+ PIN(ingress_host);
+- SYS("tc qdisc add dev veth_dst clsact");
+- SYS("tc filter add dev veth_dst ingress bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_dst clsact");
++ SYS(fail, "tc filter add dev veth_dst ingress bpf da object-pinned "
+ PIN_FNAME(ingress_host));
+- SYS("tc filter add dev veth_dst egress bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_dst egress bpf da object-pinned "
+ PIN_FNAME(egress_host));
+ close_netns(nstoken);
+
+@@ -544,23 +536,23 @@ static int netns_load_dtime_bpf(struct test_tc_dtime *skel)
+ PIN(egress_fwdns_prio100);
+ PIN(ingress_fwdns_prio101);
+ PIN(egress_fwdns_prio101);
+- SYS("tc qdisc add dev veth_dst_fwd clsact");
+- SYS("tc filter add dev veth_dst_fwd ingress prio 100 bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_dst_fwd clsact");
++ SYS(fail, "tc filter add dev veth_dst_fwd ingress prio 100 bpf da object-pinned "
+ PIN_FNAME(ingress_fwdns_prio100));
+- SYS("tc filter add dev veth_dst_fwd ingress prio 101 bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_dst_fwd ingress prio 101 bpf da object-pinned "
+ PIN_FNAME(ingress_fwdns_prio101));
+- SYS("tc filter add dev veth_dst_fwd egress prio 100 bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_dst_fwd egress prio 100 bpf da object-pinned "
+ PIN_FNAME(egress_fwdns_prio100));
+- SYS("tc filter add dev veth_dst_fwd egress prio 101 bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_dst_fwd egress prio 101 bpf da object-pinned "
+ PIN_FNAME(egress_fwdns_prio101));
+- SYS("tc qdisc add dev veth_src_fwd clsact");
+- SYS("tc filter add dev veth_src_fwd ingress prio 100 bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_src_fwd clsact");
++ SYS(fail, "tc filter add dev veth_src_fwd ingress prio 100 bpf da object-pinned "
+ PIN_FNAME(ingress_fwdns_prio100));
+- SYS("tc filter add dev veth_src_fwd ingress prio 101 bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_src_fwd ingress prio 101 bpf da object-pinned "
+ PIN_FNAME(ingress_fwdns_prio101));
+- SYS("tc filter add dev veth_src_fwd egress prio 100 bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_src_fwd egress prio 100 bpf da object-pinned "
+ PIN_FNAME(egress_fwdns_prio100));
+- SYS("tc filter add dev veth_src_fwd egress prio 101 bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_src_fwd egress prio 101 bpf da object-pinned "
+ PIN_FNAME(egress_fwdns_prio101));
+ close_netns(nstoken);
+
+@@ -941,7 +933,7 @@ static int tun_open(char *name)
+ if (!ASSERT_OK(err, "ioctl TUNSETIFF"))
+ goto fail;
+
+- SYS("ip link set dev %s up", name);
++ SYS(fail, "ip link set dev %s up", name);
+
+ return fd;
+ fail:
+@@ -1061,34 +1053,34 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result)
+ * towards dst, and "tc_dst" to redirect packets
+ * and "tc_chk" on veth_dst_fwd to drop non-redirected packets.
+ */
+- SYS("tc qdisc add dev tun_fwd clsact");
+- SYS("tc filter add dev tun_fwd ingress bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev tun_fwd clsact");
++ SYS(fail, "tc filter add dev tun_fwd ingress bpf da object-pinned "
+ SRC_PROG_PIN_FILE);
+
+- SYS("tc qdisc add dev veth_dst_fwd clsact");
+- SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
++ SYS(fail, "tc qdisc add dev veth_dst_fwd clsact");
++ SYS(fail, "tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
+ DST_PROG_PIN_FILE);
+- SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned "
++ SYS(fail, "tc filter add dev veth_dst_fwd egress bpf da object-pinned "
+ CHK_PROG_PIN_FILE);
+
+ /* Setup route and neigh tables */
+- SYS("ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24");
+- SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24");
++ SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24");
++ SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24");
+
+- SYS("ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad");
+- SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad");
++ SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad");
++ SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad");
+
+- SYS("ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global");
+- SYS("ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD
++ SYS(fail, "ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global");
++ SYS(fail, "ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD
+ " dev tun_src scope global");
+- SYS("ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global");
+- SYS("ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global");
+- SYS("ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD
++ SYS(fail, "ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global");
++ SYS(fail, "ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global");
++ SYS(fail, "ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD
+ " dev tun_src scope global");
+- SYS("ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global");
++ SYS(fail, "ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global");
+
+- SYS("ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
+- SYS("ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
++ SYS(fail, "ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
++ SYS(fail, "ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
+
+ if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
+ goto fail;
+diff --git a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c
+index eea2741102673c..e0823c2472fe70 100644
+--- a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c
++++ b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c
+@@ -91,30 +91,15 @@
+
+ #define PING_ARGS "-i 0.01 -c 3 -w 10 -q"
+
+-#define SYS(fmt, ...) \
+- ({ \
+- char cmd[1024]; \
+- snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
+- if (!ASSERT_OK(system(cmd), cmd)) \
+- goto fail; \
+- })
+-
+-#define SYS_NOFAIL(fmt, ...) \
+- ({ \
+- char cmd[1024]; \
+- snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
+- system(cmd); \
+- })
+-
+ static int config_device(void)
+ {
+- SYS("ip netns add at_ns0");
+- SYS("ip link add veth0 address " MAC_VETH1 " type veth peer name veth1");
+- SYS("ip link set veth0 netns at_ns0");
+- SYS("ip addr add " IP4_ADDR1_VETH1 "/24 dev veth1");
+- SYS("ip link set dev veth1 up mtu 1500");
+- SYS("ip netns exec at_ns0 ip addr add " IP4_ADDR_VETH0 "/24 dev veth0");
+- SYS("ip netns exec at_ns0 ip link set dev veth0 up mtu 1500");
++ SYS(fail, "ip netns add at_ns0");
++ SYS(fail, "ip link add veth0 address " MAC_VETH1 " type veth peer name veth1");
++ SYS(fail, "ip link set veth0 netns at_ns0");
++ SYS(fail, "ip addr add " IP4_ADDR1_VETH1 "/24 dev veth1");
++ SYS(fail, "ip link set dev veth1 up mtu 1500");
++ SYS(fail, "ip netns exec at_ns0 ip addr add " IP4_ADDR_VETH0 "/24 dev veth0");
++ SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up mtu 1500");
+
+ return 0;
+ fail:
+@@ -132,23 +117,23 @@ static void cleanup(void)
+ static int add_vxlan_tunnel(void)
+ {
+ /* at_ns0 namespace */
+- SYS("ip netns exec at_ns0 ip link add dev %s type vxlan external gbp dstport 4789",
++ SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external gbp dstport 4789",
+ VXLAN_TUNL_DEV0);
+- SYS("ip netns exec at_ns0 ip link set dev %s address %s up",
++ SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",
+ VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);
+- SYS("ip netns exec at_ns0 ip addr add dev %s %s/24",
++ SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",
+ VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
+- SYS("ip netns exec at_ns0 ip neigh add %s lladdr %s dev %s",
++ SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev %s",
+ IP4_ADDR_TUNL_DEV1, MAC_TUNL_DEV1, VXLAN_TUNL_DEV0);
+- SYS("ip netns exec at_ns0 ip neigh add %s lladdr %s dev veth0",
++ SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev veth0",
+ IP4_ADDR2_VETH1, MAC_VETH1);
+
+ /* root namespace */
+- SYS("ip link add dev %s type vxlan external gbp dstport 4789",
++ SYS(fail, "ip link add dev %s type vxlan external gbp dstport 4789",
+ VXLAN_TUNL_DEV1);
+- SYS("ip link set dev %s address %s up", VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
+- SYS("ip addr add dev %s %s/24", VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
+- SYS("ip neigh add %s lladdr %s dev %s",
++ SYS(fail, "ip link set dev %s address %s up", VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
++ SYS(fail, "ip addr add dev %s %s/24", VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
++ SYS(fail, "ip neigh add %s lladdr %s dev %s",
+ IP4_ADDR_TUNL_DEV0, MAC_TUNL_DEV0, VXLAN_TUNL_DEV1);
+
+ return 0;
+@@ -165,26 +150,26 @@ static void delete_vxlan_tunnel(void)
+
+ static int add_ip6vxlan_tunnel(void)
+ {
+- SYS("ip netns exec at_ns0 ip -6 addr add %s/96 dev veth0",
++ SYS(fail, "ip netns exec at_ns0 ip -6 addr add %s/96 dev veth0",
+ IP6_ADDR_VETH0);
+- SYS("ip netns exec at_ns0 ip link set dev veth0 up");
+- SYS("ip -6 addr add %s/96 dev veth1", IP6_ADDR1_VETH1);
+- SYS("ip -6 addr add %s/96 dev veth1", IP6_ADDR2_VETH1);
+- SYS("ip link set dev veth1 up");
++ SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up");
++ SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR1_VETH1);
++ SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR2_VETH1);
++ SYS(fail, "ip link set dev veth1 up");
+
+ /* at_ns0 namespace */
+- SYS("ip netns exec at_ns0 ip link add dev %s type vxlan external dstport 4789",
++ SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external dstport 4789",
+ IP6VXLAN_TUNL_DEV0);
+- SYS("ip netns exec at_ns0 ip addr add dev %s %s/24",
++ SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",
+ IP6VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0);
+- SYS("ip netns exec at_ns0 ip link set dev %s address %s up",
++ SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",
+ IP6VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);
+
+ /* root namespace */
+- SYS("ip link add dev %s type vxlan external dstport 4789",
++ SYS(fail, "ip link add dev %s type vxlan external dstport 4789",
+ IP6VXLAN_TUNL_DEV1);
+- SYS("ip addr add dev %s %s/24", IP6VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
+- SYS("ip link set dev %s address %s up",
++ SYS(fail, "ip addr add dev %s %s/24", IP6VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1);
++ SYS(fail, "ip link set dev %s address %s up",
+ IP6VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);
+
+ return 0;
+@@ -205,7 +190,7 @@ static void delete_ip6vxlan_tunnel(void)
+
+ static int test_ping(int family, const char *addr)
+ {
+- SYS("%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr);
++ SYS(fail, "%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr);
+ return 0;
+ fail:
+ return -1;
+diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c b/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c
+index 5e3a26b15ec623..d19f79048ff6fb 100644
+--- a/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c
++++ b/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c
+@@ -141,41 +141,33 @@ static const char * const xmit_policy_names[] = {
+ static int bonding_setup(struct skeletons *skeletons, int mode, int xmit_policy,
+ int bond_both_attach)
+ {
+-#define SYS(fmt, ...) \
+- ({ \
+- char cmd[1024]; \
+- snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
+- if (!ASSERT_OK(system(cmd), cmd)) \
+- return -1; \
+- })
+-
+- SYS("ip netns add ns_dst");
+- SYS("ip link add veth1_1 type veth peer name veth2_1 netns ns_dst");
+- SYS("ip link add veth1_2 type veth peer name veth2_2 netns ns_dst");
+-
+- SYS("ip link add bond1 type bond mode %s xmit_hash_policy %s",
++ SYS(fail, "ip netns add ns_dst");
++ SYS(fail, "ip link add veth1_1 type veth peer name veth2_1 netns ns_dst");
++ SYS(fail, "ip link add veth1_2 type veth peer name veth2_2 netns ns_dst");
++
++ SYS(fail, "ip link add bond1 type bond mode %s xmit_hash_policy %s",
+ mode_names[mode], xmit_policy_names[xmit_policy]);
+- SYS("ip link set bond1 up address " BOND1_MAC_STR " addrgenmode none");
+- SYS("ip -netns ns_dst link add bond2 type bond mode %s xmit_hash_policy %s",
++ SYS(fail, "ip link set bond1 up address " BOND1_MAC_STR " addrgenmode none");
++ SYS(fail, "ip -netns ns_dst link add bond2 type bond mode %s xmit_hash_policy %s",
+ mode_names[mode], xmit_policy_names[xmit_policy]);
+- SYS("ip -netns ns_dst link set bond2 up address " BOND2_MAC_STR " addrgenmode none");
++ SYS(fail, "ip -netns ns_dst link set bond2 up address " BOND2_MAC_STR " addrgenmode none");
+
+- SYS("ip link set veth1_1 master bond1");
++ SYS(fail, "ip link set veth1_1 master bond1");
+ if (bond_both_attach == BOND_BOTH_AND_ATTACH) {
+- SYS("ip link set veth1_2 master bond1");
++ SYS(fail, "ip link set veth1_2 master bond1");
+ } else {
+- SYS("ip link set veth1_2 up addrgenmode none");
++ SYS(fail, "ip link set veth1_2 up addrgenmode none");
+
+ if (xdp_attach(skeletons, skeletons->xdp_dummy->progs.xdp_dummy_prog, "veth1_2"))
+ return -1;
+ }
+
+- SYS("ip -netns ns_dst link set veth2_1 master bond2");
++ SYS(fail, "ip -netns ns_dst link set veth2_1 master bond2");
+
+ if (bond_both_attach == BOND_BOTH_AND_ATTACH)
+- SYS("ip -netns ns_dst link set veth2_2 master bond2");
++ SYS(fail, "ip -netns ns_dst link set veth2_2 master bond2");
+ else
+- SYS("ip -netns ns_dst link set veth2_2 up addrgenmode none");
++ SYS(fail, "ip -netns ns_dst link set veth2_2 up addrgenmode none");
+
+ /* Load a dummy program on sending side as with veth peer needs to have a
+ * XDP program loaded as well.
+@@ -194,8 +186,8 @@ static int bonding_setup(struct skeletons *skeletons, int mode, int xmit_policy,
+ }
+
+ return 0;
+-
+-#undef SYS
++fail:
++ return -1;
+ }
+
+ static void bonding_cleanup(struct skeletons *skeletons)
+diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
+index 15ad3366916136..b97b0818177251 100644
+--- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
++++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
+@@ -10,14 +10,6 @@
+ #include <bpf/bpf_endian.h>
+ #include "test_xdp_do_redirect.skel.h"
+
+-#define SYS(fmt, ...) \
+- ({ \
+- char cmd[1024]; \
+- snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
+- if (!ASSERT_OK(system(cmd), cmd)) \
+- goto out; \
+- })
+-
+ struct udp_packet {
+ struct ethhdr eth;
+ struct ipv6hdr iph;
+@@ -124,19 +116,19 @@ void serial_test_xdp_do_redirect(void)
+ * iface and NUM_PKTS-2 in the TC hook. We match the packets on the UDP
+ * payload.
+ */
+- SYS("ip netns add testns");
++ SYS(out, "ip netns add testns");
+ nstoken = open_netns("testns");
+ if (!ASSERT_OK_PTR(nstoken, "setns"))
+ goto out;
+
+- SYS("ip link add veth_src type veth peer name veth_dst");
+- SYS("ip link set dev veth_src address 00:11:22:33:44:55");
+- SYS("ip link set dev veth_dst address 66:77:88:99:aa:bb");
+- SYS("ip link set dev veth_src up");
+- SYS("ip link set dev veth_dst up");
+- SYS("ip addr add dev veth_src fc00::1/64");
+- SYS("ip addr add dev veth_dst fc00::2/64");
+- SYS("ip neigh add fc00::2 dev veth_src lladdr 66:77:88:99:aa:bb");
++ SYS(out, "ip link add veth_src type veth peer name veth_dst");
++ SYS(out, "ip link set dev veth_src address 00:11:22:33:44:55");
++ SYS(out, "ip link set dev veth_dst address 66:77:88:99:aa:bb");
++ SYS(out, "ip link set dev veth_src up");
++ SYS(out, "ip link set dev veth_dst up");
++ SYS(out, "ip addr add dev veth_src fc00::1/64");
++ SYS(out, "ip addr add dev veth_dst fc00::2/64");
++ SYS(out, "ip neigh add fc00::2 dev veth_src lladdr 66:77:88:99:aa:bb");
+
+ /* We enable forwarding in the test namespace because that will cause
+ * the packets that go through the kernel stack (with XDP_PASS) to be
+@@ -149,7 +141,7 @@ void serial_test_xdp_do_redirect(void)
+ * code didn't have this, so we keep the test behaviour to make sure the
+ * bug doesn't resurface.
+ */
+- SYS("sysctl -qw net.ipv6.conf.all.forwarding=1");
++ SYS(out, "sysctl -qw net.ipv6.conf.all.forwarding=1");
+
+ ifindex_src = if_nametoindex("veth_src");
+ ifindex_dst = if_nametoindex("veth_dst");
+@@ -200,6 +192,6 @@ void serial_test_xdp_do_redirect(void)
+ out:
+ if (nstoken)
+ close_netns(nstoken);
+- system("ip netns del testns");
++ SYS_NOFAIL("ip netns del testns");
+ test_xdp_do_redirect__destroy(skel);
+ }
+diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c
+index 13daa3746064af..0327c10fe6e971 100644
+--- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c
++++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c
+@@ -8,11 +8,6 @@
+
+ #define CMD_OUT_BUF_SIZE 1023
+
+-#define SYS(cmd) ({ \
+- if (!ASSERT_OK(system(cmd), (cmd))) \
+- goto out; \
+-})
+-
+ #define SYS_OUT(cmd, ...) ({ \
+ char buf[1024]; \
+ snprintf(buf, sizeof(buf), (cmd), ##__VA_ARGS__); \
+@@ -69,37 +64,37 @@ static void test_synproxy(bool xdp)
+ char buf[CMD_OUT_BUF_SIZE];
+ size_t size;
+
+- SYS("ip netns add synproxy");
++ SYS(out, "ip netns add synproxy");
+
+- SYS("ip link add tmp0 type veth peer name tmp1");
+- SYS("ip link set tmp1 netns synproxy");
+- SYS("ip link set tmp0 up");
+- SYS("ip addr replace 198.18.0.1/24 dev tmp0");
++ SYS(out, "ip link add tmp0 type veth peer name tmp1");
++ SYS(out, "ip link set tmp1 netns synproxy");
++ SYS(out, "ip link set tmp0 up");
++ SYS(out, "ip addr replace 198.18.0.1/24 dev tmp0");
+
+ /* When checksum offload is enabled, the XDP program sees wrong
+ * checksums and drops packets.
+ */
+- SYS("ethtool -K tmp0 tx off");
++ SYS(out, "ethtool -K tmp0 tx off");
+ if (xdp)
+ /* Workaround required for veth. */
+- SYS("ip link set tmp0 xdp object xdp_dummy.bpf.o section xdp 2> /dev/null");
++ SYS(out, "ip link set tmp0 xdp object xdp_dummy.bpf.o section xdp 2> /dev/null");
+
+ ns = open_netns("synproxy");
+ if (!ASSERT_OK_PTR(ns, "setns"))
+ goto out;
+
+- SYS("ip link set lo up");
+- SYS("ip link set tmp1 up");
+- SYS("ip addr replace 198.18.0.2/24 dev tmp1");
+- SYS("sysctl -w net.ipv4.tcp_syncookies=2");
+- SYS("sysctl -w net.ipv4.tcp_timestamps=1");
+- SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0");
+- SYS("iptables-legacy -t raw -I PREROUTING \
++ SYS(out, "ip link set lo up");
++ SYS(out, "ip link set tmp1 up");
++ SYS(out, "ip addr replace 198.18.0.2/24 dev tmp1");
++ SYS(out, "sysctl -w net.ipv4.tcp_syncookies=2");
++ SYS(out, "sysctl -w net.ipv4.tcp_timestamps=1");
++ SYS(out, "sysctl -w net.netfilter.nf_conntrack_tcp_loose=0");
++ SYS(out, "iptables-legacy -t raw -I PREROUTING \
+ -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack");
+- SYS("iptables-legacy -t filter -A INPUT \
++ SYS(out, "iptables-legacy -t filter -A INPUT \
+ -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \
+ -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460");
+- SYS("iptables-legacy -t filter -A INPUT \
++ SYS(out, "iptables-legacy -t filter -A INPUT \
+ -i tmp1 -m state --state INVALID -j DROP");
+
+ ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \
+@@ -170,8 +165,8 @@ static void test_synproxy(bool xdp)
+ if (ns)
+ close_netns(ns);
+
+- system("ip link del tmp0");
+- system("ip netns del synproxy");
++ SYS_NOFAIL("ip link del tmp0");
++ SYS_NOFAIL("ip netns del synproxy");
+ }
+
+ void serial_test_xdp_synproxy(void)
+diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h
+index ff1caffefa5256..b47606eebdb9b3 100644
+--- a/tools/testing/selftests/bpf/test_progs.h
++++ b/tools/testing/selftests/bpf/test_progs.h
+@@ -376,6 +376,21 @@ int test__join_cgroup(const char *path);
+ ___ok; \
+ })
+
++#define SYS(goto_label, fmt, ...) \
++ ({ \
++ char cmd[1024]; \
++ snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
++ if (!ASSERT_OK(system(cmd), cmd)) \
++ goto goto_label; \
++ })
++
++#define SYS_NOFAIL(fmt, ...) \
++ ({ \
++ char cmd[1024]; \
++ snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
++ system(cmd); \
++ })
++
+ static inline __u64 ptr_to_u64(const void *ptr)
+ {
+ return (__u64) (unsigned long) ptr;
+--
+2.53.0
+
--- /dev/null
+fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
+net-sched-act_pedit-check-static-offsets-a-priori.patch
+net-sched-act_pedit-rate-limit-datapath-messages.patch
+net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
+net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch
+selftests-bpf-move-sys-macro-into-the-test_progs.h.patch
+kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch
--- /dev/null
+From d2e2a601e491ee358eaa4bed941c868e492464ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 21:00:23 +0200
+Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 4e3d1b2c48ca6c55f1e9ca7f8dccc76f120f276c ]
+
+FUSE_NOTIFY_RETRIEVE must be limited to uptodate folios; !uptodate folios
+can contain uninitialized data.
+Since FUSE_NOTIFY_RETRIEVE is intended to only return data that is already
+in the page cache and not wait for data from the FUSE daemon, treat
+!uptodate folios as if they weren't present.
+
+This only has security impact on systems that don't enable automatic
+zero-initialization of all page allocations via
+CONFIG_INIT_ON_ALLOC_DEFAULT_ON or init_on_alloc=1.
+
+Cc: stable@kernel.org
+Fixes: 2d45ba381a74 ("fuse: add retrieve request")
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[adjusted for stable: page instead of folio]
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dev.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
+index 8f4a2ff56cc3be..6381a4626bc5a6 100644
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -1769,6 +1769,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
+ page = find_get_page(mapping, index);
+ if (!page)
+ break;
++ if (!PageUptodate(page)) {
++ put_page(page);
++ break;
++ }
+
+ this_num = min_t(unsigned, num, PAGE_SIZE - offset);
+ ap->pages[ap->num_pages] = page;
+--
+2.53.0
+
--- /dev/null
+From a7275443dd7ad729227f6f4f5929e7eb8a2d7bbd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 18:02:53 +0200
+Subject: gpio: Fix resource leaks on errors in gpiochip_add_data_with_key()
+
+From: Tzung-Bi Shih <tzungbi@kernel.org>
+
+[ Upstream commit 16fdabe143fce2cbf89139677728e17e21b46c28 ]
+
+Since commit aab5c6f20023 ("gpio: set device type for GPIO chips"),
+`gdev->dev.release` is unset. As a result, the reference count to
+`gdev->dev` isn't dropped on the error handling paths.
+
+Drop the reference on errors.
+
+Also reorder the instructions to make the error handling simpler.
+Now gpiochip_add_data_with_key() roughly looks like:
+
+ >>> Some memory allocation. Go to ERR ZONE 1 on errors.
+ >>> device_initialize().
+
+ gpiodev_release() takes over the responsibility for freeing the
+ resources of `gdev->dev`. The subsequent error handling paths
+ shouldn't go through ERR ZONE 1 again which leads to double free.
+
+ >>> Some initialization mainly on `gdev`.
+ >>> The rest of initialization. Go to ERR ZONE 2 on errors.
+ >>> Chip registration success and exit.
+
+ >>> ERR ZONE 2. gpio_device_put() and exit.
+ >>> ERR ZONE 1.
+
+Cc: stable@vger.kernel.org
+Fixes: aab5c6f20023 ("gpio: set device type for GPIO chips")
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
+Link: https://patch.msgid.link/20260205092840.2574840-1-tzungbi@kernel.org
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+[missing commit fcc8b637c542 ("gpiolib: switch the line state notifier
+ to atomic"), commit dcb73cbaaeb3 ("gpio: cdev: use raw notifier for
+ line state events") and commit d4f335b410dd ("gpiolib: rename GPIO chip
+ printk macros") in 6.12.y.
+ s/gpiochip_err/chip_err/ as well as replaced
+ rwlock_init+RAW_INIT_NOTIFIER_HEAD with BLOCKING_INIT_NOTIFIER_HEAD
+ based on missing commits, following same logic as in 16fdabe143fc.]
+Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpiolib.c | 101 ++++++++++++++++++++---------------------
+ 1 file changed, 48 insertions(+), 53 deletions(-)
+
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+index 97a32e6f901fce..878f9ab4a09829 100644
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -785,13 +785,15 @@ static const struct device_type gpio_dev_type = {
+ #define gcdev_unregister(gdev) device_del(&(gdev)->dev)
+ #endif
+
++/*
++ * An initial reference count has been held in gpiochip_add_data_with_key().
++ * The caller should drop the reference via gpio_device_put() on errors.
++ */
+ static int gpiochip_setup_dev(struct gpio_device *gdev)
+ {
+ struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
+ int ret;
+
+- device_initialize(&gdev->dev);
+-
+ /*
+ * If fwnode doesn't belong to another device, it's safe to clear its
+ * initialized flag.
+@@ -859,9 +861,11 @@ static void gpiochip_setup_devs(void)
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ srcu_read_lock_held(&gpio_devices_srcu)) {
+ ret = gpiochip_setup_dev(gdev);
+- if (ret)
++ if (ret) {
++ gpio_device_put(gdev);
+ dev_err(&gdev->dev,
+ "Failed to initialize gpio device (%d)\n", ret);
++ }
+ }
+ }
+
+@@ -941,70 +945,71 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+ int base = 0;
+ int ret;
+
+- /*
+- * First: allocate and populate the internal stat container, and
+- * set up the struct device.
+- */
+ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
+ if (!gdev)
+ return -ENOMEM;
+-
+- gdev->dev.type = &gpio_dev_type;
+- gdev->dev.bus = &gpio_bus_type;
+- gdev->dev.parent = gc->parent;
+- rcu_assign_pointer(gdev->chip, gc);
+-
+ gc->gpiodev = gdev;
+ gpiochip_set_data(gc, data);
+
+- device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
+-
+ ret = ida_alloc(&gpio_ida, GFP_KERNEL);
+ if (ret < 0)
+ goto err_free_gdev;
+ gdev->id = ret;
+
+- ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
++ ret = init_srcu_struct(&gdev->srcu);
+ if (ret)
+ goto err_free_ida;
++ rcu_assign_pointer(gdev->chip, gc);
+
+- if (gc->parent && gc->parent->driver)
+- gdev->owner = gc->parent->driver->owner;
+- else if (gc->owner)
+- /* TODO: remove chip->owner */
+- gdev->owner = gc->owner;
+- else
+- gdev->owner = THIS_MODULE;
++ ret = init_srcu_struct(&gdev->desc_srcu);
++ if (ret)
++ goto err_cleanup_gdev_srcu;
++
++ ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
++ if (ret)
++ goto err_cleanup_desc_srcu;
++
++ device_initialize(&gdev->dev);
++ /*
++ * After this point any allocated resources to `gdev` will be
++ * free():ed by gpiodev_release(). If you add new resources
++ * then make sure they get free():ed there.
++ */
++ gdev->dev.type = &gpio_dev_type;
++ gdev->dev.bus = &gpio_bus_type;
++ gdev->dev.parent = gc->parent;
++ device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
+
+ ret = gpiochip_get_ngpios(gc, &gdev->dev);
+ if (ret)
+- goto err_free_dev_name;
++ goto err_put_device;
++ gdev->ngpio = gc->ngpio;
+
+ gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
+ if (!gdev->descs) {
+ ret = -ENOMEM;
+- goto err_free_dev_name;
++ goto err_put_device;
+ }
+
+ gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
+ if (!gdev->label) {
+ ret = -ENOMEM;
+- goto err_free_descs;
++ goto err_put_device;
+ }
+
+- gdev->ngpio = gc->ngpio;
+ gdev->can_sleep = gc->can_sleep;
+-
+ BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
+ BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
+-
+- ret = init_srcu_struct(&gdev->srcu);
+- if (ret)
+- goto err_free_label;
+-
+- ret = init_srcu_struct(&gdev->desc_srcu);
+- if (ret)
+- goto err_cleanup_gdev_srcu;
++#ifdef CONFIG_PINCTRL
++ INIT_LIST_HEAD(&gdev->pin_ranges);
++#endif
++ if (gc->parent && gc->parent->driver)
++ gdev->owner = gc->parent->driver->owner;
++ else if (gc->owner)
++ /* TODO: remove chip->owner */
++ gdev->owner = gc->owner;
++ else
++ gdev->owner = THIS_MODULE;
+
+ scoped_guard(mutex, &gpio_devices_lock) {
+ /*
+@@ -1020,7 +1025,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+ if (base < 0) {
+ ret = base;
+ base = 0;
+- goto err_cleanup_desc_srcu;
++ goto err_put_device;
+ }
+
+ /*
+@@ -1040,14 +1045,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+ ret = gpiodev_add_to_list_unlocked(gdev);
+ if (ret) {
+ chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
+- goto err_cleanup_desc_srcu;
++ goto err_put_device;
+ }
+ }
+
+-#ifdef CONFIG_PINCTRL
+- INIT_LIST_HEAD(&gdev->pin_ranges);
+-#endif
+-
+ if (gc->names)
+ gpiochip_set_desc_names(gc);
+
+@@ -1128,25 +1129,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+ scoped_guard(mutex, &gpio_devices_lock)
+ list_del_rcu(&gdev->list);
+ synchronize_srcu(&gpio_devices_srcu);
+- if (gdev->dev.release) {
+- /* release() has been registered by gpiochip_setup_dev() */
+- gpio_device_put(gdev);
+- goto err_print_message;
+- }
++err_put_device:
++ gpio_device_put(gdev);
++ goto err_print_message;
++
+ err_cleanup_desc_srcu:
+ cleanup_srcu_struct(&gdev->desc_srcu);
+ err_cleanup_gdev_srcu:
+ cleanup_srcu_struct(&gdev->srcu);
+-err_free_label:
+- kfree_const(gdev->label);
+-err_free_descs:
+- kfree(gdev->descs);
+-err_free_dev_name:
+- kfree(dev_name(&gdev->dev));
+ err_free_ida:
+ ida_free(&gpio_ida, gdev->id);
+ err_free_gdev:
+ kfree(gdev);
++
+ err_print_message:
+ /* failures here can mean systems won't boot... */
+ if (ret != -EPROBE_DEFER) {
+--
+2.53.0
+
--- /dev/null
+From c68d5842d3af42b69a10497209ff27f2e39000ee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 18:02:51 +0200
+Subject: gpiolib: Extract gpiochip_choose_fwnode() for wider use
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit 375790f18396b2ba706e031b150c58cd37b45a11 ]
+
+Extract gpiochip_choose_fwnode() for the future use in another function.
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Tested-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+Reviewed-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+Link: https://lore.kernel.org/r/20250213195621.3133406-2-andriy.shevchenko@linux.intel.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 16fdabe143fc ("gpio: Fix resource leaks on errors in gpiochip_add_data_with_key()")
+Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpiolib.c | 24 ++++++++++++++++--------
+ 1 file changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+index 5c8cd816569634..d48a57b899f79c 100644
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -883,6 +883,21 @@ void *gpiochip_get_data(struct gpio_chip *gc)
+ }
+ EXPORT_SYMBOL_GPL(gpiochip_get_data);
+
++/*
++ * If the calling driver provides the specific firmware node,
++ * use it. Otherwise use the one from the parent device, if any.
++ */
++static struct fwnode_handle *gpiochip_choose_fwnode(struct gpio_chip *gc)
++{
++ if (gc->fwnode)
++ return gc->fwnode;
++
++ if (gc->parent)
++ return dev_fwnode(gc->parent);
++
++ return NULL;
++}
++
+ int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev)
+ {
+ u32 ngpios = gc->ngpio;
+@@ -942,14 +957,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+ gc->gpiodev = gdev;
+ gpiochip_set_data(gc, data);
+
+- /*
+- * If the calling driver did not initialize firmware node,
+- * do it here using the parent device, if any.
+- */
+- if (gc->fwnode)
+- device_set_node(&gdev->dev, gc->fwnode);
+- else if (gc->parent)
+- device_set_node(&gdev->dev, dev_fwnode(gc->parent));
++ device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
+
+ gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
+ if (gdev->id < 0) {
+--
+2.53.0
+
--- /dev/null
+From 3a7dd6114e85e94410a0b3ba8adbdb41f6621b23 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 18:02:52 +0200
+Subject: gpiolib: Remove redundant assignment of return variable
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit 550300b9a295a591e0721a31f8c964a4bc08d51c ]
+
+In some functions the returned variable is assigned to 0 and then
+reassigned to the actual value. Remove redundant assignments.
+
+In one case make it more clear that the assignment is not needed.
+
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://lore.kernel.org/r/20250416095645.2027695-9-andriy.shevchenko@linux.intel.com
+Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
+Stable-dep-of: 16fdabe143fc ("gpio: Fix resource leaks on errors in gpiochip_add_data_with_key()")
+Signed-off-by: Quentin Schulz <quentin.schulz@cherry.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpiolib.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+index d48a57b899f79c..97a32e6f901fce 100644
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -939,7 +939,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+ struct gpio_device *gdev;
+ unsigned int desc_index;
+ int base = 0;
+- int ret = 0;
++ int ret;
+
+ /*
+ * First: allocate and populate the internal stat container, and
+@@ -959,11 +959,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
+
+ device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
+
+- gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
+- if (gdev->id < 0) {
+- ret = gdev->id;
++ ret = ida_alloc(&gpio_ida, GFP_KERNEL);
++ if (ret < 0)
+ goto err_free_gdev;
+- }
++ gdev->id = ret;
+
+ ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
+ if (ret)
+@@ -2882,7 +2881,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output);
+ */
+ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
+ {
+- int ret = 0;
++ int ret;
+
+ VALIDATE_DESC(desc);
+
+@@ -2915,7 +2914,7 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
+ */
+ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
+ {
+- int ret = 0;
++ int ret;
+
+ VALIDATE_DESC(desc);
+
+--
+2.53.0
+
--- /dev/null
+From 07583b18e1460cfe4a321e5b3eec1690f83ca6e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 13:51:58 -0400
+Subject: io_uring/net: Avoid msghdr on op_connect/op_bind async data
+
+From: Gabriel Krisman Bertazi <krisman@suse.de>
+
+[ Upstream commit 3979840cd858f30f43ea9f4e7f7f1f56de82d698 ]
+This fixes a memory leak due to the lack of the cleanup hook for the
+iovec. The stable backport differs from upstream by dropping the
+io_connect_bpf_populate hunk, which didn't exist at the time and by
+fixing the merge conflict due to the introduction of
+io_bind_file_create and by using the older async_data allocation API.
+
+Both IORING_OP_CONNECT and IORING_OP_BIND reuse the msghdr object just
+to store the sockaddr. Beyond allocating a much larger object than
+needed, msghdr can also wrap an iovec, which will be recycled
+unnecessarily. This uses the sockaddr directly.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ io_uring/net.c | 36 ++++++++++++++++++------------------
+ io_uring/opdef.c | 4 ++--
+ 2 files changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/io_uring/net.c b/io_uring/net.c
+index 8eb0ebdc6a720c..f2f2ae1037e9b3 100644
+--- a/io_uring/net.c
++++ b/io_uring/net.c
+@@ -1719,7 +1719,7 @@ int io_socket(struct io_kiocb *req, unsigned int issue_flags)
+ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ {
+ struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect);
+- struct io_async_msghdr *io;
++ struct sockaddr_storage *addr;
+
+ if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
+ return -EINVAL;
+@@ -1728,17 +1728,17 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ conn->addr_len = READ_ONCE(sqe->addr2);
+ conn->in_progress = conn->seen_econnaborted = false;
+
+- io = io_msg_alloc_async(req);
+- if (unlikely(!io))
++ if (io_alloc_async_data(req))
+ return -ENOMEM;
++ addr = req->async_data;
+
+- return move_addr_to_kernel(conn->addr, conn->addr_len, &io->addr);
++ return move_addr_to_kernel(conn->addr, conn->addr_len, addr);
+ }
+
+ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ {
+ struct io_connect *connect = io_kiocb_to_cmd(req, struct io_connect);
+- struct io_async_msghdr *io = req->async_data;
++ struct sockaddr_storage *addr = req->async_data;
+ unsigned file_flags;
+ int ret;
+ bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
+@@ -1752,8 +1752,7 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+
+ file_flags = force_nonblock ? O_NONBLOCK : 0;
+
+- ret = __sys_connect_file(req->file, &io->addr, connect->addr_len,
+- file_flags);
++ ret = __sys_connect_file(req->file, addr, connect->addr_len, file_flags);
+ if ((ret == -EAGAIN || ret == -EINPROGRESS || ret == -ECONNABORTED)
+ && force_nonblock) {
+ if (ret == -EINPROGRESS) {
+@@ -1782,7 +1781,6 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ out:
+ if (ret < 0)
+ req_set_fail(req);
+- io_req_msg_cleanup(req, issue_flags);
+ io_req_set_res(req, ret, 0);
+ return IOU_OK;
+ }
+@@ -1792,15 +1790,15 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ * which in turn end up in mnt_want_write() which will grab the fs
+ * percpu start write sem. This can trigger a lockdep warning.
+ */
+-static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len)
++static int io_bind_file_create(const struct sockaddr_storage *addr, int addr_len)
+ {
+ const struct sockaddr_un *sun;
+
+- if (io->addr.ss_family != AF_UNIX)
++ if (addr->ss_family != AF_UNIX)
+ return 0;
+ if (addr_len <= offsetof(struct sockaddr_un, sun_path))
+ return 0;
+- sun = (const struct sockaddr_un *) &io->addr;
++ sun = (const struct sockaddr_un *) addr;
+ return sun->sun_path[0] != '\0';
+ }
+
+@@ -1808,7 +1806,7 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ {
+ struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);
+ struct sockaddr __user *uaddr;
+- struct io_async_msghdr *io;
++ struct sockaddr_storage *addr;
+ int ret;
+
+ if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
+@@ -1817,21 +1815,23 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
+ bind->addr_len = READ_ONCE(sqe->addr2);
+
+- io = io_msg_alloc_async(req);
+- if (unlikely(!io))
++ if (io_alloc_async_data(req))
+ return -ENOMEM;
+- ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr);
++ addr = req->async_data;
++
++ ret = move_addr_to_kernel(uaddr, bind->addr_len, addr);
+ if (unlikely(ret))
+ return ret;
+- if (io_bind_file_create(io, bind->addr_len))
++ if (io_bind_file_create(addr, bind->addr_len))
+ req->flags |= REQ_F_FORCE_ASYNC;
+ return 0;
+ }
+
++
+ int io_bind(struct io_kiocb *req, unsigned int issue_flags)
+ {
+ struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);
+- struct io_async_msghdr *io = req->async_data;
++ struct sockaddr_storage *addr = req->async_data;
+ struct socket *sock;
+ int ret;
+
+@@ -1839,7 +1839,7 @@ int io_bind(struct io_kiocb *req, unsigned int issue_flags)
+ if (unlikely(!sock))
+ return -ENOTSOCK;
+
+- ret = __sys_bind_socket(sock, &io->addr, bind->addr_len);
++ ret = __sys_bind_socket(sock, addr, bind->addr_len);
+ if (ret < 0)
+ req_set_fail(req);
+ io_req_set_res(req, ret, 0);
+diff --git a/io_uring/opdef.c b/io_uring/opdef.c
+index 5dc1cba158a060..bbb62d2ab2a3bf 100644
+--- a/io_uring/opdef.c
++++ b/io_uring/opdef.c
+@@ -205,7 +205,7 @@ const struct io_issue_def io_issue_defs[] = {
+ .unbound_nonreg_file = 1,
+ .pollout = 1,
+ #if defined(CONFIG_NET)
+- .async_size = sizeof(struct io_async_msghdr),
++ .async_size = sizeof(struct sockaddr_storage),
+ .prep = io_connect_prep,
+ .issue = io_connect,
+ #else
+@@ -501,7 +501,7 @@ const struct io_issue_def io_issue_defs[] = {
+ .needs_file = 1,
+ .prep = io_bind_prep,
+ .issue = io_bind,
+- .async_size = sizeof(struct io_async_msghdr),
++ .async_size = sizeof(struct sockaddr_storage),
+ #else
+ .prep = io_eopnotsupp_prep,
+ #endif
+--
+2.53.0
+
--- /dev/null
+wifi-mt76-mt7921-avoid-undesired-changes-of-the-pres.patch
+wifi-mt76-mt7921-fix-a-potential-scan-no-aps.patch
+wifi-mt76-mt7921-fix-potential-deadlock-in-mt7921_ro.patch
+fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
+gpiolib-extract-gpiochip_choose_fwnode-for-wider-use.patch
+gpiolib-remove-redundant-assignment-of-return-variab.patch
+gpio-fix-resource-leaks-on-errors-in-gpiochip_add_da.patch
+io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
--- /dev/null
+From 1a61d0d142cd6351bc6de8cb58749a5101a016e5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:14:11 +0300
+Subject: wifi: mt76: mt7921: avoid undesired changes of the preset regulatory
+ domain
+
+From: Leon Yen <leon.yen@mediatek.com>
+
+commit 2425dc7beaadc39c2636f97f8bdc22dc3cf88149 upstream.
+
+Some countries have strict RF restrictions where changing the regulatory
+domain dynamically based on the connected AP is not acceptable.
+This patch disables Beacon country IE hinting when a valid country code
+is set from usersland (e.g., by system using iw or CRDA).
+
+Signed-off-by: Leon Yen <leon.yen@mediatek.com>
+Signed-off-by: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
+Tested-by: David Ruth <druth@chromium.org>
+Link: https://patch.msgid.link/20240412085357.13756-1-mingyen.hsieh@mediatek.com
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ajrat Makhmutov <rauty@altlinux.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/init.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+index 4bd533c4ba9a1c..276dfb9c26e0dd 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+@@ -137,6 +137,13 @@ mt7921_regd_notifier(struct wiphy *wiphy,
+ dev->mt76.region = request->dfs_region;
+ dev->country_ie_env = request->country_ie_env;
+
++ if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
++ if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
++ wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
++ else
++ wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
++ }
++
+ if (pm->suspended)
+ return;
+
+--
+2.53.0
+
--- /dev/null
+From 9425416de0c652e2c2e58f45be5440c12af4fb02 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:14:12 +0300
+Subject: wifi: mt76: mt7921: fix a potential scan no APs
+
+From: Quan Zhou <quan.zhou@mediatek.com>
+
+commit 5ed54896b6bd444223092cab361b0785932119ab upstream.
+
+In multi-channel scenarios, the granted channel must be aborted before
+station remove. Otherwise, the firmware will be put into a wrong state,
+resulting in have chance to make subsequence scan no APs.
+With this patch, the granted channel will be always aborted before
+station remove.
+
+Signed-off-by: Quan Zhou <quan.zhou@mediatek.com>
+Reviewed-by: Sean Wang <sean.wang@mediatek.com>
+Tested-by: David Ruth <druth@chromium.org>
+Reviewed-by: David Ruth <druth@chromium.org>
+Link: https://patch.msgid.link/1ac1ae779db86d4012199a24ea2ca74050ed4af6.1721300411.git.quan.zhou@mediatek.com
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Ajrat Makhmutov <rauty@altlinux.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/main.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+index a93ae4e44f16a4..f2fffca868b519 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+@@ -368,9 +368,9 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
+ del_timer_sync(&phy->roc_timer);
+ cancel_work_sync(&phy->roc_work);
+ if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+- ieee80211_iterate_active_interfaces(mt76_hw(dev),
+- IEEE80211_IFACE_ITER_RESUME_ALL,
+- mt7921_roc_iter, (void *)phy);
++ ieee80211_iterate_interfaces(mt76_hw(dev),
++ IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7921_roc_iter, (void *)phy);
+ }
+ EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync);
+
+@@ -881,6 +881,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+ struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+
++ mt7921_roc_abort_sync(dev);
+ mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->deflink.wcid);
+ mt76_connac_pm_wake(&dev->mphy, &dev->pm);
+
+--
+2.53.0
+
--- /dev/null
+From ea4a895ec0beb4b6fe6c953fca362fbb92242e7b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jun 2026 11:14:13 +0300
+Subject: wifi: mt76: mt7921: fix potential deadlock in mt7921_roc_abort_sync
+
+From: Sean Wang <sean.wang@mediatek.com>
+
+commit d5059e52fd8bc624ec4255c9fa01a266513d126b upstream.
+
+roc_abort_sync() can deadlock with roc_work(). roc_work() holds
+dev->mt76.mutex, while cancel_work_sync() waits for roc_work()
+to finish. If the caller already owns the same mutex, both
+sides block and no progress is possible.
+
+This deadlock can occur during station removal when
+mt76_sta_state() -> mt76_sta_remove() -> mt7921_mac_sta_remove() ->
+mt7921_roc_abort_sync() invokes cancel_work_sync() while
+roc_work() is still running and holding dev->mt76.mutex.
+
+This avoids the mutex deadlock and preserves exactly-once
+work ownership.
+
+Fixes: 352d966126e6 ("wifi: mt76: mt7921: fix a potential association failure upon resuming")
+Co-developed-by: Quan Zhou <quan.zhou@mediatek.com>
+Signed-off-by: Quan Zhou <quan.zhou@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Link: https://patch.msgid.link/20260126180013.8167-1-sean.wang@kernel.org
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+[Ajrat: keep del_timer_sync() instead of timer_delete_sync() -- the
+ timer API rename is not present in 6.12.y. ]
+Signed-off-by: Ajrat Makhmutov <rauty@altlinux.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/main.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+index f2fffca868b519..99561094640f19 100644
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+@@ -365,12 +365,15 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
+ {
+ struct mt792x_phy *phy = &dev->phy;
+
++ if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
++ return;
++
+ del_timer_sync(&phy->roc_timer);
+- cancel_work_sync(&phy->roc_work);
+- if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+- ieee80211_iterate_interfaces(mt76_hw(dev),
+- IEEE80211_IFACE_ITER_RESUME_ALL,
+- mt7921_roc_iter, (void *)phy);
++ cancel_work(&phy->roc_work);
++
++ ieee80211_iterate_interfaces(mt76_hw(dev),
++ IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7921_roc_iter, (void *)phy);
+ }
+ EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync);
+
+--
+2.53.0
+
--- /dev/null
+From de8b6eab2e17530a0a148bd21fe4ac7b4b309ed5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 21:00:23 +0200
+Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 4e3d1b2c48ca6c55f1e9ca7f8dccc76f120f276c ]
+
+FUSE_NOTIFY_RETRIEVE must be limited to uptodate folios; !uptodate folios
+can contain uninitialized data.
+Since FUSE_NOTIFY_RETRIEVE is intended to only return data that is already
+in the page cache and not wait for data from the FUSE daemon, treat
+!uptodate folios as if they weren't present.
+
+This only has security impact on systems that don't enable automatic
+zero-initialization of all page allocations via
+CONFIG_INIT_ON_ALLOC_DEFAULT_ON or init_on_alloc=1.
+
+Cc: stable@kernel.org
+Fixes: 2d45ba381a74 ("fuse: add retrieve request")
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[adjusted for stable: page instead of folio]
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dev.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
+index 29a6c3a5829d83..a7705b5b22e09d 100644
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -1722,6 +1722,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
+ page = find_get_page(mapping, index);
+ if (!page)
+ break;
++ if (!PageUptodate(page)) {
++ put_page(page);
++ break;
++ }
+
+ this_num = min_t(unsigned, num, PAGE_SIZE - offset);
+ ap->pages[ap->num_pages] = page;
+--
+2.53.0
+
--- /dev/null
+From 6f9ef73e9adb530163e33981e26609afbecb49c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 22:10:35 +0800
+Subject: net/sched: fix pedit partial COW leading to page cache corruption
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+
+[ Upstream commit 899ee91156e57784090c5565e4f31bd7dbffbc5a ]
+
+tcf_pedit_act() computes the COW range for skb_ensure_writable()
+once before the key loop using tcfp_off_max_hint, but the hint does
+not account for the runtime header offset added by typed keys. This
+can leave part of the write region un-COW'd.
+
+Fix by moving skb_ensure_writable() inside the per-key loop where
+the actual write offset is known, and add overflow checking on the
+offset arithmetic. For negative offsets (e.g. Ethernet header edits
+at ingress), use skb_cow() to COW the headroom instead. Guard
+offset_valid() against INT_MIN, where negation is undefined.
+
+Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable")
+Reported-by: Yiming Qian <yimingqian591@gmail.com>
+Reported-by: Keenan Dong <keenanat2000@gmail.com>
+Reported-by: Han Guidong <2045gemini@gmail.com>
+Reported-by: Zhang Cen <rollkingzzc@gmail.com>
+Reviewed-by: Han Guidong <2045gemini@gmail.com>
+Tested-by: Han Guidong <2045gemini@gmail.com>
+Reviewed-by: Davide Caratti <dcaratti@redhat.com>
+Tested-by: Davide Caratti <dcaratti@redhat.com>
+Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Tested-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Reviewed-by: Victor Nogueira <victor@mojatatu.com>
+Tested-by: Victor Nogueira <victor@mojatatu.com>
+Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
+Signed-off-by: Rajat Gupta <rajat.gupta@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com
+[rename include file from linux/unaligned.h to asm/unaligned.h]
+Conflicts:
+ include/net/tc_act/tc_pedit.h
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/tc_act/tc_pedit.h | 1 -
+ net/sched/act_pedit.c | 77 +++++++++++++++++++----------------
+ 2 files changed, 41 insertions(+), 37 deletions(-)
+
+diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
+index 83fe3993178180..a26d4cd3b8d6f3 100644
+--- a/include/net/tc_act/tc_pedit.h
++++ b/include/net/tc_act/tc_pedit.h
+@@ -14,7 +14,6 @@ struct tcf_pedit_key_ex {
+ struct tcf_pedit_parms {
+ struct tc_pedit_key *tcfp_keys;
+ struct tcf_pedit_key_ex *tcfp_keys_ex;
+- u32 tcfp_off_max_hint;
+ unsigned char tcfp_nkeys;
+ unsigned char tcfp_flags;
+ struct rcu_head rcu;
+diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
+index 1ef8fcfa9997d1..80b2a1e9d5ceb9 100644
+--- a/net/sched/act_pedit.c
++++ b/net/sched/act_pedit.c
+@@ -16,6 +16,8 @@
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
+ #include <linux/slab.h>
++#include <linux/overflow.h>
++#include <asm/unaligned.h>
+ #include <net/ipv6.h>
+ #include <net/netlink.h>
+ #include <net/pkt_sched.h>
+@@ -242,7 +244,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ goto out_free_ex;
+ }
+
+- nparms->tcfp_off_max_hint = 0;
+ nparms->tcfp_flags = parm->flags;
+ nparms->tcfp_nkeys = parm->nkeys;
+
+@@ -268,14 +269,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
+ BITS_PER_TYPE(int) - 1,
+ nparms->tcfp_keys[i].shift);
+
+- /* The AT option can read a single byte, we can bound the actual
+- * value with uchar max.
+- */
+- cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift;
+-
+- /* Each key touches 4 bytes starting from the computed offset */
+- nparms->tcfp_off_max_hint =
+- max(nparms->tcfp_off_max_hint, cur + 4);
+ }
+
+ p = to_pedit(*a);
+@@ -318,15 +311,12 @@ static void tcf_pedit_cleanup(struct tc_action *a)
+ call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu);
+ }
+
+-static bool offset_valid(struct sk_buff *skb, int offset)
++static bool offset_valid(struct sk_buff *skb, int offset, int len)
+ {
+- if (offset > 0 && offset > skb->len)
+- return false;
+-
+- if (offset < 0 && -offset > skb_headroom(skb))
++ if (offset < -(int)skb_headroom(skb))
+ return false;
+
+- return true;
++ return offset <= (int)skb->len - len;
+ }
+
+ static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
+@@ -393,18 +383,10 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
+ struct tcf_pedit_key_ex *tkey_ex;
+ struct tcf_pedit_parms *parms;
+ struct tc_pedit_key *tkey;
+- u32 max_offset;
+ int i;
+
+ parms = rcu_dereference_bh(p->parms);
+
+- max_offset = (skb_transport_header_was_set(skb) ?
+- skb_transport_offset(skb) :
+- skb_network_offset(skb)) +
+- parms->tcfp_off_max_hint;
+- if (skb_ensure_writable(skb, min(skb->len, max_offset)))
+- goto done;
+-
+ tcf_lastuse_update(&p->tcf_tm);
+ tcf_action_update_bstats(&p->common, skb);
+
+@@ -412,10 +394,11 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
+ tkey_ex = parms->tcfp_keys_ex;
+
+ for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) {
++ int write_offset, write_len;
+ int offset = tkey->off;
+ int hoffset = 0;
+- u32 *ptr, hdata;
+- u32 val;
++ u32 cur_val, val;
++ u32 *ptr;
+ int rc;
+
+ if (tkey_ex) {
+@@ -433,13 +416,15 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
+
+ if (tkey->offmask) {
+ u8 *d, _d;
++ int at_offset;
+
+- if (!offset_valid(skb, hoffset + tkey->at)) {
++ if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) ||
++ !offset_valid(skb, at_offset, sizeof(_d))) {
+ pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n",
+ hoffset + tkey->at);
+ goto bad;
+ }
+- d = skb_header_pointer(skb, hoffset + tkey->at,
++ d = skb_header_pointer(skb, at_offset,
+ sizeof(_d), &_d);
+ if (!d)
+ goto bad;
+@@ -451,31 +436,51 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
+ }
+ }
+
+- if (!offset_valid(skb, hoffset + offset)) {
+- pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset);
++ if (check_add_overflow(hoffset, offset, &write_offset)) {
++ pr_info_ratelimited("tc action pedit offset overflow\n");
+ goto bad;
+ }
+
+- ptr = skb_header_pointer(skb, hoffset + offset,
+- sizeof(hdata), &hdata);
+- if (!ptr)
++ if (!offset_valid(skb, write_offset, sizeof(*ptr))) {
++ pr_info_ratelimited("tc action pedit offset %d out of bounds\n",
++ write_offset);
+ goto bad;
++ }
++
++ if (write_offset < 0) {
++ if (skb_cow(skb, -write_offset))
++ goto bad;
++ if (write_offset + (int)sizeof(*ptr) > 0) {
++ if (skb_ensure_writable(skb,
++ min_t(int, skb->len,
++ write_offset + (int)sizeof(*ptr))))
++ goto bad;
++ }
++ } else {
++ if (check_add_overflow(write_offset, (int)sizeof(*ptr),
++ &write_len))
++ goto bad;
++ if (skb_ensure_writable(skb, min_t(int, skb->len,
++ write_len)))
++ goto bad;
++ }
++
++ ptr = (u32 *)(skb->data + write_offset);
++ cur_val = get_unaligned(ptr);
+ /* just do it, baby */
+ switch (cmd) {
+ case TCA_PEDIT_KEY_EX_CMD_SET:
+ val = tkey->val;
+ break;
+ case TCA_PEDIT_KEY_EX_CMD_ADD:
+- val = (*ptr + tkey->val) & ~tkey->mask;
++ val = (cur_val + tkey->val) & ~tkey->mask;
+ break;
+ default:
+ pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd);
+ goto bad;
+ }
+
+- *ptr = ((*ptr & tkey->mask) ^ val);
+- if (ptr == &hdata)
+- skb_store_bits(skb, hoffset + offset, ptr, 4);
++ put_unaligned((cur_val & tkey->mask) ^ val, ptr);
+ }
+
+ goto done;
+--
+2.53.0
+
--- /dev/null
+fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
+net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
--- /dev/null
+From 5be0c5bd0d20338b7eebef5942bdab498fa3726f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 15:27:22 -0400
+Subject: io_uring/net: Avoid msghdr on op_connect/op_bind async data
+
+From: Gabriel Krisman Bertazi <krisman@suse.de>
+
+[ Upstream commit 3979840cd858f30f43ea9f4e7f7f1f56de82d698 ]
+This fixes a memory leak due to the lack of the cleanup hook for the
+iovec. The stable backport differs from upstream by dropping the
+io_connect_bpf_populate hunk, which didn't exist at the time and by
+fixing the merge conflict due to the introduction of
+io_bind_file_create.
+
+Both IORING_OP_CONNECT and IORING_OP_BIND reuse the msghdr object just
+to store the sockaddr. Beyond allocating a much larger object than
+needed, msghdr can also wrap an iovec, which will be recycled
+unnecessarily. This uses the sockaddr directly.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ io_uring/net.c | 36 ++++++++++++++++++------------------
+ io_uring/opdef.c | 4 ++--
+ 2 files changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/io_uring/net.c b/io_uring/net.c
+index 1329fc9d72fd6f..798bf5dedc2e95 100644
+--- a/io_uring/net.c
++++ b/io_uring/net.c
+@@ -1775,7 +1775,7 @@ int io_socket(struct io_kiocb *req, unsigned int issue_flags)
+ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ {
+ struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect);
+- struct io_async_msghdr *io;
++ struct sockaddr_storage *addr;
+
+ if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
+ return -EINVAL;
+@@ -1784,17 +1784,17 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ conn->addr_len = READ_ONCE(sqe->addr2);
+ conn->in_progress = conn->seen_econnaborted = false;
+
+- io = io_msg_alloc_async(req);
+- if (unlikely(!io))
++ addr = io_uring_alloc_async_data(NULL, req);
++ if (unlikely(!addr))
+ return -ENOMEM;
+
+- return move_addr_to_kernel(conn->addr, conn->addr_len, &io->addr);
++ return move_addr_to_kernel(conn->addr, conn->addr_len, addr);
+ }
+
+ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ {
+ struct io_connect *connect = io_kiocb_to_cmd(req, struct io_connect);
+- struct io_async_msghdr *io = req->async_data;
++ struct sockaddr_storage *addr = req->async_data;
+ unsigned file_flags;
+ int ret;
+ bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
+@@ -1808,8 +1808,7 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+
+ file_flags = force_nonblock ? O_NONBLOCK : 0;
+
+- ret = __sys_connect_file(req->file, &io->addr, connect->addr_len,
+- file_flags);
++ ret = __sys_connect_file(req->file, addr, connect->addr_len, file_flags);
+ if ((ret == -EAGAIN || ret == -EINPROGRESS || ret == -ECONNABORTED)
+ && force_nonblock) {
+ if (ret == -EINPROGRESS) {
+@@ -1838,7 +1837,6 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ out:
+ if (ret < 0)
+ req_set_fail(req);
+- io_req_msg_cleanup(req, issue_flags);
+ io_req_set_res(req, ret, 0);
+ return IOU_COMPLETE;
+ }
+@@ -1848,15 +1846,15 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ * which in turn end up in mnt_want_write() which will grab the fs
+ * percpu start write sem. This can trigger a lockdep warning.
+ */
+-static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len)
++static int io_bind_file_create(const struct sockaddr_storage *addr, int addr_len)
+ {
+ const struct sockaddr_un *sun;
+
+- if (io->addr.ss_family != AF_UNIX)
++ if (addr->ss_family != AF_UNIX)
+ return 0;
+ if (addr_len <= offsetof(struct sockaddr_un, sun_path))
+ return 0;
+- sun = (const struct sockaddr_un *) &io->addr;
++ sun = (const struct sockaddr_un *) addr;
+ return sun->sun_path[0] != '\0';
+ }
+
+@@ -1864,7 +1862,7 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ {
+ struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);
+ struct sockaddr __user *uaddr;
+- struct io_async_msghdr *io;
++ struct sockaddr_storage *addr;
+ int ret;
+
+ if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
+@@ -1873,21 +1871,23 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
+ bind->addr_len = READ_ONCE(sqe->addr2);
+
+- io = io_msg_alloc_async(req);
+- if (unlikely(!io))
++ addr = io_uring_alloc_async_data(NULL, req);
++ if (unlikely(!addr))
+ return -ENOMEM;
+- ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr);
++
++ ret = move_addr_to_kernel(uaddr, bind->addr_len, addr);
+ if (unlikely(ret))
+ return ret;
+- if (io_bind_file_create(io, bind->addr_len))
++ if (io_bind_file_create(addr, bind->addr_len))
+ req->flags |= REQ_F_FORCE_ASYNC;
+ return 0;
+ }
+
++
+ int io_bind(struct io_kiocb *req, unsigned int issue_flags)
+ {
+ struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);
+- struct io_async_msghdr *io = req->async_data;
++ struct sockaddr_storage *addr = req->async_data;
+ struct socket *sock;
+ int ret;
+
+@@ -1895,7 +1895,7 @@ int io_bind(struct io_kiocb *req, unsigned int issue_flags)
+ if (unlikely(!sock))
+ return -ENOTSOCK;
+
+- ret = __sys_bind_socket(sock, &io->addr, bind->addr_len);
++ ret = __sys_bind_socket(sock, addr, bind->addr_len);
+ if (ret < 0)
+ req_set_fail(req);
+ io_req_set_res(req, ret, 0);
+diff --git a/io_uring/opdef.c b/io_uring/opdef.c
+index 91a23baf415e89..2e1752df8748b4 100644
+--- a/io_uring/opdef.c
++++ b/io_uring/opdef.c
+@@ -207,7 +207,7 @@ const struct io_issue_def io_issue_defs[] = {
+ .unbound_nonreg_file = 1,
+ .pollout = 1,
+ #if defined(CONFIG_NET)
+- .async_size = sizeof(struct io_async_msghdr),
++ .async_size = sizeof(struct sockaddr_storage),
+ .prep = io_connect_prep,
+ .issue = io_connect,
+ #else
+@@ -510,7 +510,7 @@ const struct io_issue_def io_issue_defs[] = {
+ .needs_file = 1,
+ .prep = io_bind_prep,
+ .issue = io_bind,
+- .async_size = sizeof(struct io_async_msghdr),
++ .async_size = sizeof(struct sockaddr_storage),
+ #else
+ .prep = io_eopnotsupp_prep,
+ #endif
+--
+2.53.0
+
--- /dev/null
+io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
--- /dev/null
+From 3e227abb5cb2647397db03b9949e1336ceb9e1cf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 13:49:47 -0400
+Subject: io_uring/net: Avoid msghdr on op_connect/op_bind async data
+
+From: Gabriel Krisman Bertazi <krisman@suse.de>
+
+[ Upstream commit 3979840cd858f30f43ea9f4e7f7f1f56de82d698 ]
+This fixes a memory leak due to the lack of the cleanup hook for the
+iovec. The stable backport differs from upstream by dropping the
+io_connect_bpf_populate hunk, which didn't exist at the time and by
+fixing the merge conflict due to the introduction of
+io_bind_file_create.
+
+Both IORING_OP_CONNECT and IORING_OP_BIND reuse the msghdr object just
+to store the sockaddr. Beyond allocating a much larger object than
+needed, msghdr can also wrap an iovec, which will be recycled
+unnecessarily. This uses the sockaddr directly.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ io_uring/net.c | 36 ++++++++++++++++++------------------
+ io_uring/opdef.c | 4 ++--
+ 2 files changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/io_uring/net.c b/io_uring/net.c
+index ee848eb65ec99e..bf60cd393b11a5 100644
+--- a/io_uring/net.c
++++ b/io_uring/net.c
+@@ -1733,7 +1733,7 @@ int io_socket(struct io_kiocb *req, unsigned int issue_flags)
+ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ {
+ struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect);
+- struct io_async_msghdr *io;
++ struct sockaddr_storage *addr;
+
+ if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
+ return -EINVAL;
+@@ -1742,17 +1742,17 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ conn->addr_len = READ_ONCE(sqe->addr2);
+ conn->in_progress = conn->seen_econnaborted = false;
+
+- io = io_msg_alloc_async(req);
+- if (unlikely(!io))
++ addr = io_uring_alloc_async_data(NULL, req);
++ if (unlikely(!addr))
+ return -ENOMEM;
+
+- return move_addr_to_kernel(conn->addr, conn->addr_len, &io->addr);
++ return move_addr_to_kernel(conn->addr, conn->addr_len, addr);
+ }
+
+ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ {
+ struct io_connect *connect = io_kiocb_to_cmd(req, struct io_connect);
+- struct io_async_msghdr *io = req->async_data;
++ struct sockaddr_storage *addr = req->async_data;
+ unsigned file_flags;
+ int ret;
+ bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
+@@ -1766,8 +1766,7 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+
+ file_flags = force_nonblock ? O_NONBLOCK : 0;
+
+- ret = __sys_connect_file(req->file, &io->addr, connect->addr_len,
+- file_flags);
++ ret = __sys_connect_file(req->file, addr, connect->addr_len, file_flags);
+ if ((ret == -EAGAIN || ret == -EINPROGRESS || ret == -ECONNABORTED)
+ && force_nonblock) {
+ if (ret == -EINPROGRESS) {
+@@ -1796,7 +1795,6 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ out:
+ if (ret < 0)
+ req_set_fail(req);
+- io_req_msg_cleanup(req, issue_flags);
+ io_req_set_res(req, ret, 0);
+ return IOU_COMPLETE;
+ }
+@@ -1806,15 +1804,15 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
+ * which in turn end up in mnt_want_write() which will grab the fs
+ * percpu start write sem. This can trigger a lockdep warning.
+ */
+-static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len)
++static int io_bind_file_create(const struct sockaddr_storage *addr, int addr_len)
+ {
+ const struct sockaddr_un *sun;
+
+- if (io->addr.ss_family != AF_UNIX)
++ if (addr->ss_family != AF_UNIX)
+ return 0;
+ if (addr_len <= offsetof(struct sockaddr_un, sun_path))
+ return 0;
+- sun = (const struct sockaddr_un *) &io->addr;
++ sun = (const struct sockaddr_un *) addr;
+ return sun->sun_path[0] != '\0';
+ }
+
+@@ -1822,7 +1820,7 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ {
+ struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);
+ struct sockaddr __user *uaddr;
+- struct io_async_msghdr *io;
++ struct sockaddr_storage *addr;
+ int ret;
+
+ if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in)
+@@ -1831,21 +1829,23 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+ uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
+ bind->addr_len = READ_ONCE(sqe->addr2);
+
+- io = io_msg_alloc_async(req);
+- if (unlikely(!io))
++ addr = io_uring_alloc_async_data(NULL, req);
++ if (unlikely(!addr))
+ return -ENOMEM;
+- ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr);
++
++ ret = move_addr_to_kernel(uaddr, bind->addr_len, addr);
+ if (unlikely(ret))
+ return ret;
+- if (io_bind_file_create(io, bind->addr_len))
++ if (io_bind_file_create(addr, bind->addr_len))
+ req->flags |= REQ_F_FORCE_ASYNC;
+ return 0;
+ }
+
++
+ int io_bind(struct io_kiocb *req, unsigned int issue_flags)
+ {
+ struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind);
+- struct io_async_msghdr *io = req->async_data;
++ struct sockaddr_storage *addr = req->async_data;
+ struct socket *sock;
+ int ret;
+
+@@ -1853,7 +1853,7 @@ int io_bind(struct io_kiocb *req, unsigned int issue_flags)
+ if (unlikely(!sock))
+ return -ENOTSOCK;
+
+- ret = __sys_bind_socket(sock, &io->addr, bind->addr_len);
++ ret = __sys_bind_socket(sock, addr, bind->addr_len);
+ if (ret < 0)
+ req_set_fail(req);
+ io_req_set_res(req, ret, 0);
+diff --git a/io_uring/opdef.c b/io_uring/opdef.c
+index c3ef52b7081132..34cd320a427b8f 100644
+--- a/io_uring/opdef.c
++++ b/io_uring/opdef.c
+@@ -203,7 +203,7 @@ const struct io_issue_def io_issue_defs[] = {
+ .unbound_nonreg_file = 1,
+ .pollout = 1,
+ #if defined(CONFIG_NET)
+- .async_size = sizeof(struct io_async_msghdr),
++ .async_size = sizeof(struct sockaddr_storage),
+ .prep = io_connect_prep,
+ .issue = io_connect,
+ #else
+@@ -503,7 +503,7 @@ const struct io_issue_def io_issue_defs[] = {
+ .needs_file = 1,
+ .prep = io_bind_prep,
+ .issue = io_bind,
+- .async_size = sizeof(struct io_async_msghdr),
++ .async_size = sizeof(struct sockaddr_storage),
+ #else
+ .prep = io_eopnotsupp_prep,
+ #endif
+--
+2.53.0
+
--- /dev/null
+io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch