From: Sasha Levin Date: Fri, 19 Jun 2026 04:06:27 +0000 (-0400) Subject: Fixes for all trees X-Git-Tag: v5.10.259~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2a353bd4a047d926b3c79ab1e2686b50aa991b65;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- 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 index 0000000000..6a2811d457 --- /dev/null +++ b/staging-5.10/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch @@ -0,0 +1,50 @@ +From 2ddc3e7caadf352b6e0d11fc08bacbde92c1b530 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 21:00:23 +0200 +Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios + +From: Jann Horn + +[ 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 +Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com +Acked-by: Miklos Szeredi +Signed-off-by: Christian Brauner (Amutable) +[adjusted for stable: page instead of folio] +Signed-off-by: Jann Horn +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..c8d239c4c3 --- /dev/null +++ b/staging-5.10/net-sched-act_pedit-check-static-offsets-a-priori.patch @@ -0,0 +1,79 @@ +From ecf771d6c0b4b04f30c11c6dda7b9717ed31d4dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:44 +0800 +Subject: net/sched: act_pedit: check static offsets a priori + +From: Pedro Tammela + +[ 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 +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..1a3fb572f3 --- /dev/null +++ b/staging-5.10/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch @@ -0,0 +1,85 @@ +From 9bfe0e032f451c11caaa42d6e909747ba0e38d00 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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: + [] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] + [] slab_post_alloc_hook mm/slab.h:772 [inline] + [] slab_alloc_node mm/slub.c:3452 [inline] + [] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491 + [] __do_kmalloc_node mm/slab_common.c:966 [inline] + [] __kmalloc+0x59/0x1a0 mm/slab_common.c:980 + [] kmalloc include/linux/slab.h:584 [inline] + [] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245 + [] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394 + [] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459 + [] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985 + [] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044 + [] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395 + [] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575 + [] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413 + [] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] + [] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365 + [] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942 + [] sock_sendmsg_nosec net/socket.c:724 [inline] + [] sock_sendmsg net/socket.c:747 [inline] + [] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503 + [] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557 + [] __sys_sendmsg+0x11f/0x200 net/socket.c:2586 + [] __do_sys_sendmsg net/socket.c:2595 [inline] + [] __se_sys_sendmsg net/socket.c:2593 [inline] + [] __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 +Signed-off-by: Pedro Tammela +Reviewed-by: Ido Schimmel +Tested-by: Ido Schimmel +Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..766950726b --- /dev/null +++ b/staging-5.10/net-sched-act_pedit-parse-l3-header-for-l4-offset.patch @@ -0,0 +1,142 @@ +From d2a0396da8a211854254a031e458c4293eee28ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:48 +0800 +Subject: net/sched: act_pedit: Parse L3 Header for L4 offset + +From: Max Tottenham + +[ 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 +Reviewed-by: Josh Hunt +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202305261541.N165u9TZ-lkp@intel.com/ +Reviewed-by: Pedro Tammela +Signed-off-by: David S. Miller +(cherry picked from commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b) +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include ++#include + #include ++#include + #include + #include + #include +@@ -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 index 0000000000..05a7ea7064 --- /dev/null +++ b/staging-5.10/net-sched-act_pedit-rate-limit-datapath-messages.patch @@ -0,0 +1,69 @@ +From c744ad742f2186b1c0b2998bc32348f5ad2289f4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:46 +0800 +Subject: net/sched: act_pedit: rate limit datapath messages + +From: Pedro Tammela + +[ 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 +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..435c7caea3 --- /dev/null +++ b/staging-5.10/net-sched-act_pedit-remove-extra-check-for-key-type.patch @@ -0,0 +1,98 @@ +From 1d610bf3d0adb7044c162bf713cf65e638d5c2d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:42 +0800 +Subject: net/sched: act_pedit: remove extra check for key type + +From: Pedro Tammela + +[ Upstream commit 577140180ba28d0d37bc898c7bd6702c83aa106f ] + +The netlink parsing already validates the key 'htype'. +Remove the datapath check as it's redundant. + +Reviewed-by: Jamal Hadi Salim +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..7937a9662d --- /dev/null +++ b/staging-5.10/net-sched-act_pedit-use-nla_policy-for-parsing-ex-ke.patch @@ -0,0 +1,53 @@ +From fd434c1137b7e44d2b52fc703df732947fe45e1b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:36 +0800 +Subject: net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys + +From: Pedro Tammela + +[ Upstream commit 5036034572b79daa6d6600338e8e8229e2a44b09 ] + +Transform two checks in the 'ex' key parsing into netlink policies +removing extra if checks. + +Signed-off-by: Pedro Tammela +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..70545a52fa --- /dev/null +++ b/staging-5.10/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch @@ -0,0 +1,233 @@ +From 05750b74ef98d989e2d907e8cf4b4c25fc24b160 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reported-by: Keenan Dong +Reported-by: Han Guidong <2045gemini@gmail.com> +Reported-by: Zhang Cen +Reviewed-by: Han Guidong <2045gemini@gmail.com> +Tested-by: Han Guidong <2045gemini@gmail.com> +Reviewed-by: Davide Caratti +Tested-by: Davide Caratti +Reviewed-by: Toke Høiland-Jørgensen +Tested-by: Toke Høiland-Jørgensen +Reviewed-by: Victor Nogueira +Tested-by: Victor Nogueira +Acked-by: Jamal Hadi Salim +Signed-off-by: Rajat Gupta +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 +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include ++#include + #include + #include + #include +@@ -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 index 0000000000..90d40a2d63 --- /dev/null +++ b/staging-5.10/net-sched-simplify-tcf_pedit_act.patch @@ -0,0 +1,194 @@ +From c0d15083891cb8d52d3a11c00a76a98bcbf66da0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:40 +0800 +Subject: net/sched: simplify tcf_pedit_act + +From: Pedro Tammela + +[ Upstream commit 95b069382351826c0ae37938070aa82dbeaf288d ] + +Remove the check for a negative number of keys as +this cannot ever happen + +Reviewed-by: Jamal Hadi Salim +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..3233a57fc6 --- /dev/null +++ b/staging-5.10/net-sched-transition-act_pedit-to-rcu-and-percpu-sta.patch @@ -0,0 +1,439 @@ +From 85521f7ed7d2b93d4ab27bc01d3fc74285cdbc21 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 15:53:38 +0800 +Subject: net/sched: transition act_pedit to rcu and percpu stats + +From: Pedro Tammela + +[ 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 +Signed-off-by: Pedro Tammela +Reviewed-by: Simon Horman +Signed-off-by: Paolo Abeni +Conflicts: + net/sched/act_pedit.c +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 + #include ++#include + + 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 index 0000000000..2153636ec7 --- /dev/null +++ b/staging-5.10/series @@ -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 index 0000000000..5ecb4a09ba --- /dev/null +++ b/staging-5.15/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch @@ -0,0 +1,50 @@ +From 317cdc5119de09e0fe86e671be2e301c5f10c43b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 21:00:23 +0200 +Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios + +From: Jann Horn + +[ 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 +Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com +Acked-by: Miklos Szeredi +Signed-off-by: Christian Brauner (Amutable) +[adjusted for stable: page instead of folio] +Signed-off-by: Jann Horn +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..9ac4e49b0b --- /dev/null +++ b/staging-5.15/net-sched-act_pedit-check-static-offsets-a-priori.patch @@ -0,0 +1,80 @@ +From 6277e99e50f9ff6fe0e7a003dd63c46696987aca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:55:04 +0800 +Subject: net/sched: act_pedit: check static offsets a priori + +From: Pedro Tammela + +[ 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 +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +(cherry picked from commit e1201bc781c28766720e78a5e099ffa568be4d74) +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..8451e1c076 --- /dev/null +++ b/staging-5.15/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch @@ -0,0 +1,85 @@ +From e7db67a6bc4fbe88ab7855e54bc66a0622c5bae6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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: + [] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] + [] slab_post_alloc_hook mm/slab.h:772 [inline] + [] slab_alloc_node mm/slub.c:3452 [inline] + [] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491 + [] __do_kmalloc_node mm/slab_common.c:966 [inline] + [] __kmalloc+0x59/0x1a0 mm/slab_common.c:980 + [] kmalloc include/linux/slab.h:584 [inline] + [] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245 + [] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394 + [] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459 + [] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985 + [] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044 + [] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395 + [] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575 + [] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413 + [] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] + [] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365 + [] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942 + [] sock_sendmsg_nosec net/socket.c:724 [inline] + [] sock_sendmsg net/socket.c:747 [inline] + [] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503 + [] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557 + [] __sys_sendmsg+0x11f/0x200 net/socket.c:2586 + [] __do_sys_sendmsg net/socket.c:2595 [inline] + [] __se_sys_sendmsg net/socket.c:2593 [inline] + [] __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 +Signed-off-by: Pedro Tammela +Reviewed-by: Ido Schimmel +Tested-by: Ido Schimmel +Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com +Signed-off-by: Paolo Abeni +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..d275e2fe5f --- /dev/null +++ b/staging-5.15/net-sched-act_pedit-rate-limit-datapath-messages.patch @@ -0,0 +1,70 @@ +From 0e6bd4abb28768262b9720f63bc9c010c10d2bfb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:55:06 +0800 +Subject: net/sched: act_pedit: rate limit datapath messages + +From: Pedro Tammela + +[ 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 +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +(cherry picked from commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a) +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..8bfbfd5118 --- /dev/null +++ b/staging-5.15/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch @@ -0,0 +1,232 @@ +From 74bfeffa765a0b4b52535c236dbb6e06e1899b23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reported-by: Keenan Dong +Reported-by: Han Guidong <2045gemini@gmail.com> +Reported-by: Zhang Cen +Reviewed-by: Han Guidong <2045gemini@gmail.com> +Tested-by: Han Guidong <2045gemini@gmail.com> +Reviewed-by: Davide Caratti +Tested-by: Davide Caratti +Reviewed-by: Toke Høiland-Jørgensen +Tested-by: Toke Høiland-Jørgensen +Reviewed-by: Victor Nogueira +Tested-by: Victor Nogueira +Acked-by: Jamal Hadi Salim +Signed-off-by: Rajat Gupta +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 +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include ++#include + #include + #include + #include +@@ -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 index 0000000000..b9e0218d2b --- /dev/null +++ b/staging-5.15/series @@ -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 index 0000000000..8760a1fc1b --- /dev/null +++ b/staging-6.1/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch @@ -0,0 +1,50 @@ +From ad1a051b51afd0fc2ed5b2abe21cff701777e4fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 21:00:23 +0200 +Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios + +From: Jann Horn + +[ 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 +Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com +Acked-by: Miklos Szeredi +Signed-off-by: Christian Brauner (Amutable) +[adjusted for stable: page instead of folio] +Signed-off-by: Jann Horn +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..b3fecb3832 --- /dev/null +++ b/staging-6.1/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch @@ -0,0 +1,95 @@ +From 9e83a3ccc3afcc78eed4e7ad29c6d0fb50e3199d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Sean Christopherson +Link: https://lore.kernel.org/r/20220928232015.745948-1-seanjc@google.com +(cherry picked from commit 0b5e7a16a0a79a3742f0df9e45bca46f01b40e6a) +Signed-off-by: Hanne-Lotta Mäenpää +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..85b1d28c2b --- /dev/null +++ b/staging-6.1/net-sched-act_pedit-check-static-offsets-a-priori.patch @@ -0,0 +1,80 @@ +From 3a6d2fa661f7656af8f9e56f62f0a78b28e4c633 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:42:48 +0800 +Subject: net/sched: act_pedit: check static offsets a priori + +From: Pedro Tammela + +[ 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 +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +(cherry picked from commit e1201bc781c28766720e78a5e099ffa568be4d74) +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..f38140696a --- /dev/null +++ b/staging-6.1/net-sched-act_pedit-free-pedit-keys-on-bail-from-off.patch @@ -0,0 +1,86 @@ +From 2da6b49af1372a16ceba89f9472546d80088fc87 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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: + [] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] + [] slab_post_alloc_hook mm/slab.h:772 [inline] + [] slab_alloc_node mm/slub.c:3452 [inline] + [] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491 + [] __do_kmalloc_node mm/slab_common.c:966 [inline] + [] __kmalloc+0x59/0x1a0 mm/slab_common.c:980 + [] kmalloc include/linux/slab.h:584 [inline] + [] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245 + [] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394 + [] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459 + [] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985 + [] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044 + [] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395 + [] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575 + [] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413 + [] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] + [] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365 + [] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942 + [] sock_sendmsg_nosec net/socket.c:724 [inline] + [] sock_sendmsg net/socket.c:747 [inline] + [] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503 + [] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557 + [] __sys_sendmsg+0x11f/0x200 net/socket.c:2586 + [] __do_sys_sendmsg net/socket.c:2595 [inline] + [] __se_sys_sendmsg net/socket.c:2593 [inline] + [] __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 +Signed-off-by: Pedro Tammela +Reviewed-by: Ido Schimmel +Tested-by: Ido Schimmel +Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com +Signed-off-by: Paolo Abeni +(cherry picked from commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb) +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..1cf93277bd --- /dev/null +++ b/staging-6.1/net-sched-act_pedit-rate-limit-datapath-messages.patch @@ -0,0 +1,70 @@ +From d1f0ba56cadeaa4f2d6897dd6c6848bf998896a4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:42:49 +0800 +Subject: net/sched: act_pedit: rate limit datapath messages + +From: Pedro Tammela + +[ 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 +Reviewed-by: Simon Horman +Signed-off-by: Pedro Tammela +Signed-off-by: David S. Miller +(cherry picked from commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a) +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..a1982ac4e5 --- /dev/null +++ b/staging-6.1/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch @@ -0,0 +1,232 @@ +From 97f407db528043146b47e25b45f6a11b997ddc10 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reported-by: Keenan Dong +Reported-by: Han Guidong <2045gemini@gmail.com> +Reported-by: Zhang Cen +Reviewed-by: Han Guidong <2045gemini@gmail.com> +Tested-by: Han Guidong <2045gemini@gmail.com> +Reviewed-by: Davide Caratti +Tested-by: Davide Caratti +Reviewed-by: Toke Høiland-Jørgensen +Tested-by: Toke Høiland-Jørgensen +Reviewed-by: Victor Nogueira +Tested-by: Victor Nogueira +Acked-by: Jamal Hadi Salim +Signed-off-by: Rajat Gupta +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 +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include ++#include + #include + #include + #include +@@ -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 index 0000000000..63287035bb --- /dev/null +++ b/staging-6.1/selftests-bpf-move-sys-macro-into-the-test_progs.h.patch @@ -0,0 +1,730 @@ +From 0b8a74b3bed2508da5d3c54e740379bbca6b4116 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 09:40:31 +0800 +Subject: selftests/bpf: move SYS() macro into the test_progs.h + +From: Hangbin Liu + +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 +Signed-off-by: Hangbin Liu +Link: https://lore.kernel.org/r/20230224061343.506571-2-liuhangbin@gmail.com +Signed-off-by: Martin KaFai Lau +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 +Signed-off-by: Sasha Levin +--- + .../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 + #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 + #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 index 0000000000..5be786350e --- /dev/null +++ b/staging-6.1/series @@ -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 index 0000000000..0a97fcc1b8 --- /dev/null +++ b/staging-6.12/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch @@ -0,0 +1,50 @@ +From d2e2a601e491ee358eaa4bed941c868e492464ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 21:00:23 +0200 +Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios + +From: Jann Horn + +[ 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 +Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com +Acked-by: Miklos Szeredi +Signed-off-by: Christian Brauner (Amutable) +[adjusted for stable: page instead of folio] +Signed-off-by: Jann Horn +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..6084403e0a --- /dev/null +++ b/staging-6.12/gpio-fix-resource-leaks-on-errors-in-gpiochip_add_da.patch @@ -0,0 +1,250 @@ +From a7275443dd7ad729227f6f4f5929e7eb8a2d7bbd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Tzung-Bi Shih +Link: https://patch.msgid.link/20260205092840.2574840-1-tzungbi@kernel.org +Signed-off-by: Bartosz Golaszewski +[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 +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..1e3a782d37 --- /dev/null +++ b/staging-6.12/gpiolib-extract-gpiochip_choose_fwnode-for-wider-use.patch @@ -0,0 +1,69 @@ +From c68d5842d3af42b69a10497209ff27f2e39000ee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 18:02:51 +0200 +Subject: gpiolib: Extract gpiochip_choose_fwnode() for wider use + +From: Andy Shevchenko + +[ Upstream commit 375790f18396b2ba706e031b150c58cd37b45a11 ] + +Extract gpiochip_choose_fwnode() for the future use in another function. + +Signed-off-by: Andy Shevchenko +Reviewed-by: Linus Walleij +Tested-by: Mathieu Dubois-Briand +Reviewed-by: Mathieu Dubois-Briand +Link: https://lore.kernel.org/r/20250213195621.3133406-2-andriy.shevchenko@linux.intel.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 16fdabe143fc ("gpio: Fix resource leaks on errors in gpiochip_add_data_with_key()") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..1cf0f68f0b --- /dev/null +++ b/staging-6.12/gpiolib-remove-redundant-assignment-of-return-variab.patch @@ -0,0 +1,74 @@ +From 3a7dd6114e85e94410a0b3ba8adbdb41f6621b23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 18:02:52 +0200 +Subject: gpiolib: Remove redundant assignment of return variable + +From: Andy Shevchenko + +[ 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 +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20250416095645.2027695-9-andriy.shevchenko@linux.intel.com +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 16fdabe143fc ("gpio: Fix resource leaks on errors in gpiochip_add_data_with_key()") +Signed-off-by: Quentin Schulz +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..d93f602344 --- /dev/null +++ b/staging-6.12/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch @@ -0,0 +1,174 @@ +From 07583b18e1460cfe4a321e5b3eec1690f83ca6e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de +Signed-off-by: Jens Axboe +Signed-off-by: Gabriel Krisman Bertazi +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..6e61e56e92 --- /dev/null +++ b/staging-6.12/series @@ -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 index 0000000000..d29eafb2cf --- /dev/null +++ b/staging-6.12/wifi-mt76-mt7921-avoid-undesired-changes-of-the-pres.patch @@ -0,0 +1,47 @@ +From 1a61d0d142cd6351bc6de8cb58749a5101a016e5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:14:11 +0300 +Subject: wifi: mt76: mt7921: avoid undesired changes of the preset regulatory + domain + +From: Leon Yen + +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 +Signed-off-by: Ming Yen Hsieh +Tested-by: David Ruth +Link: https://patch.msgid.link/20240412085357.13756-1-mingyen.hsieh@mediatek.com +Signed-off-by: Felix Fietkau +Signed-off-by: Ajrat Makhmutov +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..7d316e9450 --- /dev/null +++ b/staging-6.12/wifi-mt76-mt7921-fix-a-potential-scan-no-aps.patch @@ -0,0 +1,55 @@ +From 9425416de0c652e2c2e58f45be5440c12af4fb02 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:14:12 +0300 +Subject: wifi: mt76: mt7921: fix a potential scan no APs + +From: Quan Zhou + +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 +Reviewed-by: Sean Wang +Tested-by: David Ruth +Reviewed-by: David Ruth +Link: https://patch.msgid.link/1ac1ae779db86d4012199a24ea2ca74050ed4af6.1721300411.git.quan.zhou@mediatek.com +Signed-off-by: Felix Fietkau +Signed-off-by: Ajrat Makhmutov +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..e30472f26c --- /dev/null +++ b/staging-6.12/wifi-mt76-mt7921-fix-potential-deadlock-in-mt7921_ro.patch @@ -0,0 +1,64 @@ +From ea4a895ec0beb4b6fe6c953fca362fbb92242e7b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 11:14:13 +0300 +Subject: wifi: mt76: mt7921: fix potential deadlock in mt7921_roc_abort_sync + +From: Sean Wang + +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 +Signed-off-by: Quan Zhou +Signed-off-by: Sean Wang +Link: https://patch.msgid.link/20260126180013.8167-1-sean.wang@kernel.org +Signed-off-by: Felix Fietkau +[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 +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..08c82661a0 --- /dev/null +++ b/staging-6.6/fuse-limit-fuse_notify_retrieve-to-uptodate-folios.patch @@ -0,0 +1,50 @@ +From de8b6eab2e17530a0a148bd21fe4ac7b4b309ed5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 21:00:23 +0200 +Subject: fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios + +From: Jann Horn + +[ 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 +Link: https://patch.msgid.link/20260519-fuse-retrieve-uptodate-v1-1-a7a1912a37f9@google.com +Acked-by: Miklos Szeredi +Signed-off-by: Christian Brauner (Amutable) +[adjusted for stable: page instead of folio] +Signed-off-by: Jann Horn +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..7481e9b03d --- /dev/null +++ b/staging-6.6/net-sched-fix-pedit-partial-cow-leading-to-page-cach.patch @@ -0,0 +1,232 @@ +From 6f9ef73e9adb530163e33981e26609afbecb49c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reported-by: Keenan Dong +Reported-by: Han Guidong <2045gemini@gmail.com> +Reported-by: Zhang Cen +Reviewed-by: Han Guidong <2045gemini@gmail.com> +Tested-by: Han Guidong <2045gemini@gmail.com> +Reviewed-by: Davide Caratti +Tested-by: Davide Caratti +Reviewed-by: Toke Høiland-Jørgensen +Tested-by: Toke Høiland-Jørgensen +Reviewed-by: Victor Nogueira +Tested-by: Victor Nogueira +Acked-by: Jamal Hadi Salim +Signed-off-by: Rajat Gupta +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 +Signed-off-by: Wentao Guan +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include ++#include + #include + #include + #include +@@ -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 index 0000000000..bef91ff4ac --- /dev/null +++ b/staging-6.6/series @@ -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 index 0000000000..f15d3ac6c3 --- /dev/null +++ b/staging-7.0/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch @@ -0,0 +1,174 @@ +From 5be0c5bd0d20338b7eebef5942bdab498fa3726f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de +Signed-off-by: Jens Axboe +Signed-off-by: Gabriel Krisman Bertazi +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..7dd13f0fed --- /dev/null +++ b/staging-7.0/series @@ -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 index 0000000000..874a1f49ab --- /dev/null +++ b/staging-7.1/io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch @@ -0,0 +1,174 @@ +From 3e227abb5cb2647397db03b9949e1336ceb9e1cf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://patch.msgid.link/20260602215327.1885109-2-krisman@suse.de +Signed-off-by: Jens Axboe +Signed-off-by: Gabriel Krisman Bertazi +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..7dd13f0fed --- /dev/null +++ b/staging-7.1/series @@ -0,0 +1 @@ +io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch