]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Fri, 19 Jun 2026 04:06:27 +0000 (00:06 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 19 Jun 2026 04:06:27 +0000 (00:06 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
41 files changed:
staging-5.10/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch [new file with mode: 0644]
staging-5.10/net-sched-act_pedit-check-static-offsets-a-priori.patch [new file with mode: 0644]
staging-5.10/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch [new file with mode: 0644]
staging-5.10/net-sched-act_pedit-parse-l3-header-for-l4-offset.patch [new file with mode: 0644]
staging-5.10/net-sched-act_pedit-rate-limit-datapath-messages.patch [new file with mode: 0644]
staging-5.10/net-sched-act_pedit-remove-extra-check-for-key-type.patch [new file with mode: 0644]
staging-5.10/net-sched-act_pedit-use-nla_policy-for-parsing-ex-ke.patch [new file with mode: 0644]
staging-5.10/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch [new file with mode: 0644]
staging-5.10/net-sched-simplify-tcf_pedit_act.patch [new file with mode: 0644]
staging-5.10/net-sched-transition-act_pedit-to-rcu-and-percpu-sta.patch [new file with mode: 0644]
staging-5.10/series [new file with mode: 0644]
staging-5.15/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch [new file with mode: 0644]
staging-5.15/net-sched-act_pedit-check-static-offsets-a-priori.patch [new file with mode: 0644]
staging-5.15/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch [new file with mode: 0644]
staging-5.15/net-sched-act_pedit-rate-limit-datapath-messages.patch [new file with mode: 0644]
staging-5.15/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch [new file with mode: 0644]
staging-5.15/series [new file with mode: 0644]
staging-6.1/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch [new file with mode: 0644]
staging-6.1/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch [new file with mode: 0644]
staging-6.1/net-sched-act_pedit-check-static-offsets-a-priori.patch [new file with mode: 0644]
staging-6.1/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch [new file with mode: 0644]
staging-6.1/net-sched-act_pedit-rate-limit-datapath-messages.patch [new file with mode: 0644]
staging-6.1/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch [new file with mode: 0644]
staging-6.1/selftests-bpf-move-sys-macro-into-the-test_progs.h.patch [new file with mode: 0644]
staging-6.1/series [new file with mode: 0644]
staging-6.12/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch [new file with mode: 0644]
staging-6.12/gpio-fix-resource-leaks-on-errors-in-gpiochip_add_da.patch [new file with mode: 0644]
staging-6.12/gpiolib-extract-gpiochip_choose_fwnode-for-wider-use.patch [new file with mode: 0644]
staging-6.12/gpiolib-remove-redundant-assignment-of-return-variab.patch [new file with mode: 0644]
staging-6.12/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch [new file with mode: 0644]
staging-6.12/series [new file with mode: 0644]
staging-6.12/wifi-mt76-mt7921-avoid-undesired-changes-of-the-pres.patch [new file with mode: 0644]
staging-6.12/wifi-mt76-mt7921-fix-a-potential-scan-no-aps.patch [new file with mode: 0644]
staging-6.12/wifi-mt76-mt7921-fix-potential-deadlock-in-mt7921_ro.patch [new file with mode: 0644]
staging-6.6/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch [new file with mode: 0644]
staging-6.6/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch [new file with mode: 0644]
staging-6.6/series [new file with mode: 0644]
staging-7.0/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch [new file with mode: 0644]
staging-7.0/series [new file with mode: 0644]
staging-7.1/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch [new file with mode: 0644]
staging-7.1/series [new file with mode: 0644]

diff --git a/staging-5.10/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch b/staging-5.10/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
new file mode 100644 (file)
index 0000000..6a2811d
--- /dev/null
@@ -0,0 +1,50 @@
+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
+
diff --git a/staging-5.10/net-sched-act_pedit-check-static-offsets-a-priori.patch b/staging-5.10/net-sched-act_pedit-check-static-offsets-a-priori.patch
new file mode 100644 (file)
index 0000000..c8d239c
--- /dev/null
@@ -0,0 +1,79 @@
+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
+
diff --git a/staging-5.10/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch b/staging-5.10/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch
new file mode 100644 (file)
index 0000000..1a3fb57
--- /dev/null
@@ -0,0 +1,85 @@
+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
+
diff --git a/staging-5.10/net-sched-act_pedit-parse-l3-header-for-l4-offset.patch b/staging-5.10/net-sched-act_pedit-parse-l3-header-for-l4-offset.patch
new file mode 100644 (file)
index 0000000..7669507
--- /dev/null
@@ -0,0 +1,142 @@
+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
+
diff --git a/staging-5.10/net-sched-act_pedit-rate-limit-datapath-messages.patch b/staging-5.10/net-sched-act_pedit-rate-limit-datapath-messages.patch
new file mode 100644 (file)
index 0000000..05a7ea7
--- /dev/null
@@ -0,0 +1,69 @@
+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
+
diff --git a/staging-5.10/net-sched-act_pedit-remove-extra-check-for-key-type.patch b/staging-5.10/net-sched-act_pedit-remove-extra-check-for-key-type.patch
new file mode 100644 (file)
index 0000000..435c7ca
--- /dev/null
@@ -0,0 +1,98 @@
+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
+
diff --git a/staging-5.10/net-sched-act_pedit-use-nla_policy-for-parsing-ex-ke.patch b/staging-5.10/net-sched-act_pedit-use-nla_policy-for-parsing-ex-ke.patch
new file mode 100644 (file)
index 0000000..7937a96
--- /dev/null
@@ -0,0 +1,53 @@
+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
+
diff --git a/staging-5.10/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch b/staging-5.10/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
new file mode 100644 (file)
index 0000000..70545a5
--- /dev/null
@@ -0,0 +1,233 @@
+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
+
diff --git a/staging-5.10/net-sched-simplify-tcf_pedit_act.patch b/staging-5.10/net-sched-simplify-tcf_pedit_act.patch
new file mode 100644 (file)
index 0000000..90d40a2
--- /dev/null
@@ -0,0 +1,194 @@
+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
+
diff --git a/staging-5.10/net-sched-transition-act_pedit-to-rcu-and-percpu-sta.patch b/staging-5.10/net-sched-transition-act_pedit-to-rcu-and-percpu-sta.patch
new file mode 100644 (file)
index 0000000..3233a57
--- /dev/null
@@ -0,0 +1,439 @@
+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
+
diff --git a/staging-5.10/series b/staging-5.10/series
new file mode 100644 (file)
index 0000000..2153636
--- /dev/null
@@ -0,0 +1,10 @@
+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
diff --git a/staging-5.15/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch b/staging-5.15/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
new file mode 100644 (file)
index 0000000..5ecb4a0
--- /dev/null
@@ -0,0 +1,50 @@
+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
+
diff --git a/staging-5.15/net-sched-act_pedit-check-static-offsets-a-priori.patch b/staging-5.15/net-sched-act_pedit-check-static-offsets-a-priori.patch
new file mode 100644 (file)
index 0000000..9ac4e49
--- /dev/null
@@ -0,0 +1,80 @@
+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
+
diff --git a/staging-5.15/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch b/staging-5.15/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch
new file mode 100644 (file)
index 0000000..8451e1c
--- /dev/null
@@ -0,0 +1,85 @@
+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
+
diff --git a/staging-5.15/net-sched-act_pedit-rate-limit-datapath-messages.patch b/staging-5.15/net-sched-act_pedit-rate-limit-datapath-messages.patch
new file mode 100644 (file)
index 0000000..d275e2f
--- /dev/null
@@ -0,0 +1,70 @@
+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
+
diff --git a/staging-5.15/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch b/staging-5.15/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
new file mode 100644 (file)
index 0000000..8bfbfd5
--- /dev/null
@@ -0,0 +1,232 @@
+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
+
diff --git a/staging-5.15/series b/staging-5.15/series
new file mode 100644 (file)
index 0000000..b9e0218
--- /dev/null
@@ -0,0 +1,5 @@
+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
diff --git a/staging-6.1/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch b/staging-6.1/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
new file mode 100644 (file)
index 0000000..8760a1f
--- /dev/null
@@ -0,0 +1,50 @@
+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
+
diff --git a/staging-6.1/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch b/staging-6.1/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch
new file mode 100644 (file)
index 0000000..b3fecb3
--- /dev/null
@@ -0,0 +1,95 @@
+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
+
diff --git a/staging-6.1/net-sched-act_pedit-check-static-offsets-a-priori.patch b/staging-6.1/net-sched-act_pedit-check-static-offsets-a-priori.patch
new file mode 100644 (file)
index 0000000..85b1d28
--- /dev/null
@@ -0,0 +1,80 @@
+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
+
diff --git a/staging-6.1/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch b/staging-6.1/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch
new file mode 100644 (file)
index 0000000..f381406
--- /dev/null
@@ -0,0 +1,86 @@
+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
+
diff --git a/staging-6.1/net-sched-act_pedit-rate-limit-datapath-messages.patch b/staging-6.1/net-sched-act_pedit-rate-limit-datapath-messages.patch
new file mode 100644 (file)
index 0000000..1cf9327
--- /dev/null
@@ -0,0 +1,70 @@
+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
+
diff --git a/staging-6.1/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch b/staging-6.1/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
new file mode 100644 (file)
index 0000000..a1982ac
--- /dev/null
@@ -0,0 +1,232 @@
+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
+
diff --git a/staging-6.1/selftests-bpf-move-sys-macro-into-the-test_progs.h.patch b/staging-6.1/selftests-bpf-move-sys-macro-into-the-test_progs.h.patch
new file mode 100644 (file)
index 0000000..6328703
--- /dev/null
@@ -0,0 +1,730 @@
+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
+
diff --git a/staging-6.1/series b/staging-6.1/series
new file mode 100644 (file)
index 0000000..5be7863
--- /dev/null
@@ -0,0 +1,7 @@
+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
diff --git a/staging-6.12/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch b/staging-6.12/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
new file mode 100644 (file)
index 0000000..0a97fcc
--- /dev/null
@@ -0,0 +1,50 @@
+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
+
diff --git a/staging-6.12/gpio-fix-resource-leaks-on-errors-in-gpiochip_add_da.patch b/staging-6.12/gpio-fix-resource-leaks-on-errors-in-gpiochip_add_da.patch
new file mode 100644 (file)
index 0000000..6084403
--- /dev/null
@@ -0,0 +1,250 @@
+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
+
diff --git a/staging-6.12/gpiolib-extract-gpiochip_choose_fwnode-for-wider-use.patch b/staging-6.12/gpiolib-extract-gpiochip_choose_fwnode-for-wider-use.patch
new file mode 100644 (file)
index 0000000..1e3a782
--- /dev/null
@@ -0,0 +1,69 @@
+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
+
diff --git a/staging-6.12/gpiolib-remove-redundant-assignment-of-return-variab.patch b/staging-6.12/gpiolib-remove-redundant-assignment-of-return-variab.patch
new file mode 100644 (file)
index 0000000..1cf0f68
--- /dev/null
@@ -0,0 +1,74 @@
+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
+
diff --git a/staging-6.12/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch b/staging-6.12/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
new file mode 100644 (file)
index 0000000..d93f602
--- /dev/null
@@ -0,0 +1,174 @@
+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
+
diff --git a/staging-6.12/series b/staging-6.12/series
new file mode 100644 (file)
index 0000000..6e61e56
--- /dev/null
@@ -0,0 +1,8 @@
+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
diff --git a/staging-6.12/wifi-mt76-mt7921-avoid-undesired-changes-of-the-pres.patch b/staging-6.12/wifi-mt76-mt7921-avoid-undesired-changes-of-the-pres.patch
new file mode 100644 (file)
index 0000000..d29eafb
--- /dev/null
@@ -0,0 +1,47 @@
+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
+
diff --git a/staging-6.12/wifi-mt76-mt7921-fix-a-potential-scan-no-aps.patch b/staging-6.12/wifi-mt76-mt7921-fix-a-potential-scan-no-aps.patch
new file mode 100644 (file)
index 0000000..7d316e9
--- /dev/null
@@ -0,0 +1,55 @@
+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
+
diff --git a/staging-6.12/wifi-mt76-mt7921-fix-potential-deadlock-in-mt7921_ro.patch b/staging-6.12/wifi-mt76-mt7921-fix-potential-deadlock-in-mt7921_ro.patch
new file mode 100644 (file)
index 0000000..e30472f
--- /dev/null
@@ -0,0 +1,64 @@
+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
+
diff --git a/staging-6.6/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch b/staging-6.6/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
new file mode 100644 (file)
index 0000000..08c8266
--- /dev/null
@@ -0,0 +1,50 @@
+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
+
diff --git a/staging-6.6/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch b/staging-6.6/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
new file mode 100644 (file)
index 0000000..7481e9b
--- /dev/null
@@ -0,0 +1,232 @@
+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
+
diff --git a/staging-6.6/series b/staging-6.6/series
new file mode 100644 (file)
index 0000000..bef91ff
--- /dev/null
@@ -0,0 +1,2 @@
+fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch
+net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch
diff --git a/staging-7.0/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch b/staging-7.0/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
new file mode 100644 (file)
index 0000000..f15d3ac
--- /dev/null
@@ -0,0 +1,174 @@
+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
+
diff --git a/staging-7.0/series b/staging-7.0/series
new file mode 100644 (file)
index 0000000..7dd13f0
--- /dev/null
@@ -0,0 +1 @@
+io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
diff --git a/staging-7.1/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch b/staging-7.1/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
new file mode 100644 (file)
index 0000000..874a1f4
--- /dev/null
@@ -0,0 +1,174 @@
+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
+
diff --git a/staging-7.1/series b/staging-7.1/series
new file mode 100644 (file)
index 0000000..7dd13f0
--- /dev/null
@@ -0,0 +1 @@
+io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch