* [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption
@ 2026-06-18 7:53 Wentao Guan
2026-06-18 7:53 ` [PATCH 5.10.y v2 1/9] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan
` (9 more replies)
0 siblings, 10 replies; 28+ messages in thread
From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw)
To: sashal, gregkh, guanwentao
Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba,
rajat.gupta, rollkingzzc, toke, victor, yimingqian591
Fixes CVE-2026-46331: net/sched: fix pedit partial COW leading to page cache corruption
Link: https://lore.kernel.org/all/2026061625-CVE-2026-46331-47be@gregkh/
changelog v2:
Add ("net/sched: act_pedit: Parse L3 Header for L4 offset") commit.
v1 Link: https://lore.kernel.org/stable/20260618043539.1557035-1-guanwentao@uniontech.com/T/#ma9ef6d260833405f60ae88a1686e967b1416d80c
Max Tottenham (1):
net/sched: act_pedit: Parse L3 Header for L4 offset
Pedro Tammela (7):
net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys
net/sched: transition act_pedit to rcu and percpu stats
net/sched: simplify tcf_pedit_act
net/sched: act_pedit: remove extra check for key type
net/sched: act_pedit: check static offsets a priori
net/sched: act_pedit: rate limit datapath messages
net/sched: act_pedit: free pedit keys on bail from offset check
Rajat Gupta (1):
net/sched: fix pedit partial COW leading to page cache corruption
include/net/tc_act/tc_pedit.h | 80 ++++++--
net/sched/act_pedit.c | 360 +++++++++++++++++++---------------
2 files changed, 272 insertions(+), 168 deletions(-)
--
2.30.2
^ permalink raw reply [flat|nested] 28+ messages in thread* [PATCH 5.10.y v2 1/9] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 2/9] net/sched: transition act_pedit to rcu and percpu stats Wentao Guan ` (8 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 5036034572b79daa6d6600338e8e8229e2a44b09 ] Transform two checks in the 'ex' key parsing into netlink policies removing extra if checks. Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 a44101b2f4419..510a3b5b8c0c1 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v2 2/9] net/sched: transition act_pedit to rcu and percpu stats 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 1/9] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 3/9] net/sched: simplify tcf_pedit_act Wentao Guan ` (7 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, Paolo Abeni From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 52cf89f78c01bf39973f3e70d366921d70faff7a ] The software pedit action didn't get the same love as some of the other actions and it's still using spinlocks and shared stats in the datapath. Transition the action to rcu and percpu stats as this improves the action's performance dramatically on multiple cpu deployments. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Conflicts: net/sched/act_pedit.c Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 3e02709a1df65..83fe399317818 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -4,22 +4,29 @@ #include <net/act_api.h> #include <linux/tc_act/tc_pedit.h> +#include <linux/types.h> struct tcf_pedit_key_ex { enum pedit_header_type htype; enum pedit_cmd cmd; }; -struct tcf_pedit { - struct tc_action common; - unsigned char tcfp_nkeys; - unsigned char tcfp_flags; - u32 tcfp_off_max_hint; +struct tcf_pedit_parms { struct tc_pedit_key *tcfp_keys; struct tcf_pedit_key_ex *tcfp_keys_ex; + u32 tcfp_off_max_hint; + unsigned char tcfp_nkeys; + unsigned char tcfp_flags; + struct rcu_head rcu; +}; + +struct tcf_pedit { + struct tc_action common; + struct tcf_pedit_parms __rcu *parms; }; #define to_pedit(a) ((struct tcf_pedit *)a) +#define to_pedit_parms(a) (rcu_dereference(to_pedit(a)->parms)) static inline bool is_tcf_pedit(const struct tc_action *a) { @@ -32,37 +39,81 @@ static inline bool is_tcf_pedit(const struct tc_action *a) static inline int tcf_pedit_nkeys(const struct tc_action *a) { - return to_pedit(a)->tcfp_nkeys; + struct tcf_pedit_parms *parms; + int nkeys; + + rcu_read_lock(); + parms = to_pedit_parms(a); + nkeys = parms->tcfp_nkeys; + rcu_read_unlock(); + + return nkeys; } static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) { - if (to_pedit(a)->tcfp_keys_ex) - return to_pedit(a)->tcfp_keys_ex[index].htype; + u32 htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; + struct tcf_pedit_parms *parms; + + rcu_read_lock(); + parms = to_pedit_parms(a); + if (parms->tcfp_keys_ex) + htype = parms->tcfp_keys_ex[index].htype; + rcu_read_unlock(); - return TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; + return htype; } static inline u32 tcf_pedit_cmd(const struct tc_action *a, int index) { - if (to_pedit(a)->tcfp_keys_ex) - return to_pedit(a)->tcfp_keys_ex[index].cmd; + struct tcf_pedit_parms *parms; + u32 cmd = __PEDIT_CMD_MAX; - return __PEDIT_CMD_MAX; + rcu_read_lock(); + parms = to_pedit_parms(a); + if (parms->tcfp_keys_ex) + cmd = parms->tcfp_keys_ex[index].cmd; + rcu_read_unlock(); + + return cmd; } static inline u32 tcf_pedit_mask(const struct tc_action *a, int index) { - return to_pedit(a)->tcfp_keys[index].mask; + struct tcf_pedit_parms *parms; + u32 mask; + + rcu_read_lock(); + parms = to_pedit_parms(a); + mask = parms->tcfp_keys[index].mask; + rcu_read_unlock(); + + return mask; } static inline u32 tcf_pedit_val(const struct tc_action *a, int index) { - return to_pedit(a)->tcfp_keys[index].val; + struct tcf_pedit_parms *parms; + u32 val; + + rcu_read_lock(); + parms = to_pedit_parms(a); + val = parms->tcfp_keys[index].val; + rcu_read_unlock(); + + return val; } static inline u32 tcf_pedit_offset(const struct tc_action *a, int index) { - return to_pedit(a)->tcfp_keys[index].off; + struct tcf_pedit_parms *parms; + u32 off; + + rcu_read_lock(); + parms = to_pedit_parms(a); + off = parms->tcfp_keys[index].off; + rcu_read_unlock(); + + return off; } #endif /* __NET_TC_PED_H */ diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 510a3b5b8c0c1..0fbffebfbdc9d 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v2 3/9] net/sched: simplify tcf_pedit_act 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 1/9] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 2/9] net/sched: transition act_pedit to rcu and percpu stats Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH v5.10.y v2 4/9] net/sched: act_pedit: remove extra check for key type Wentao Guan ` (6 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, Paolo Abeni From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 95b069382351826c0ae37938070aa82dbeaf288d ] Remove the check for a negative number of keys as this cannot ever happen Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 0fbffebfbdc9d..84152d3a49246 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5.10.y v2 4/9] net/sched: act_pedit: remove extra check for key type 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (2 preceding siblings ...) 2026-06-18 7:53 ` [PATCH 5.10.y v2 3/9] net/sched: simplify tcf_pedit_act Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 5/9] net/sched: act_pedit: check static offsets a priori Wentao Guan ` (5 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 577140180ba28d0d37bc898c7bd6702c83aa106f ] The netlink parsing already validates the key 'htype'. Remove the datapath check as it's redundant. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 84152d3a49246..957ce9017c3f7 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v2 5/9] net/sched: act_pedit: check static offsets a priori 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (3 preceding siblings ...) 2026-06-18 7:53 ` [PATCH v5.10.y v2 4/9] net/sched: act_pedit: remove extra check for key type Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 6/9] net/sched: act_pedit: rate limit datapath messages Wentao Guan ` (4 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit e1201bc781c28766720e78a5e099ffa568be4d74 ] Static key offsets should always be on 32 bit boundaries. Validate them on create/update time for static offsets and move the datapath validation for runtime offsets only. iproute2 already errors out if a given offset and data size cannot be packed to a 32 bit boundary. This change will make sure users which create/update pedit instances directly via netlink also error out, instead of finding out when packets are traversing. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 957ce9017c3f7..95ae885ecba16 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v2 6/9] net/sched: act_pedit: rate limit datapath messages 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (4 preceding siblings ...) 2026-06-18 7:53 ` [PATCH 5.10.y v2 5/9] net/sched: act_pedit: check static offsets a priori Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 7/9] net/sched: act_pedit: Parse L3 Header for L4 offset Wentao Guan ` (3 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a ] Unbounded info messages in the pedit datapath can flood the printk ring buffer quite easily depending on the action created. As these messages are informational, usually printing some, not all, is enough to bring attention to the real issue. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 95ae885ecba16..ecad6fc39dc3d 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v2 7/9] net/sched: act_pedit: Parse L3 Header for L4 offset 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (5 preceding siblings ...) 2026-06-18 7:53 ` [PATCH 5.10.y v2 6/9] net/sched: act_pedit: rate limit datapath messages Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y 8/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (2 subsequent siblings) 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Max Tottenham, Josh Hunt, kernel test robot, Pedro Tammela, David S . Miller From: Max Tottenham <mtottenh@akamai.com> [ Upstream commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b ] Instead of relying on skb->transport_header being set correctly, opt instead to parse the L3 header length out of the L3 headers for both IPv4/IPv6 when the Extended Layer Op for tcp/udp is used. This fixes a bug if GRO is disabled, when GRO is disabled skb->transport_header is set by __netif_receive_skb_core() to point to the L3 header, it's later fixed by the upper protocol layers, but act_pedit will receive the SKB before the fixups are completed. The existing behavior causes the following to edit the L3 header if GRO is disabled instead of the UDP header: tc filter add dev eth0 ingress protocol ip flower ip_proto udp \ dst_ip 192.168.1.3 action pedit ex munge udp set dport 18053 Also re-introduce a rate-limited warning if we were unable to extract the header offset when using the 'ex' interface. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Signed-off-by: Max Tottenham <mtottenh@akamai.com> Reviewed-by: Josh Hunt <johunt@akamai.com> Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202305261541.N165u9TZ-lkp@intel.com/ Reviewed-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b) Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 ecad6fc39dc3d..df31b2b7b4225 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -13,7 +13,10 @@ #include <linux/rtnetlink.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/ip.h> +#include <linux/ipv6.h> #include <linux/slab.h> +#include <net/ipv6.h> #include <net/netlink.h> #include <net/pkt_sched.h> #include <linux/tc_act/tc_pedit.h> @@ -313,28 +316,58 @@ static bool offset_valid(struct sk_buff *skb, int offset) return true; } -static void pedit_skb_hdr_offset(struct sk_buff *skb, +static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type) +{ + const int noff = skb_network_offset(skb); + int ret = -EINVAL; + struct iphdr _iph; + + switch (skb->protocol) { + case htons(ETH_P_IP): { + const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph); + + if (!iph) + goto out; + *hoffset = noff + iph->ihl * 4; + ret = 0; + break; + } + case htons(ETH_P_IPV6): + ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL; + break; + } +out: + return ret; +} + +static int pedit_skb_hdr_offset(struct sk_buff *skb, enum pedit_header_type htype, int *hoffset) { + int ret = -EINVAL; /* 'htype' is validated in the netlink parsing */ switch (htype) { case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: - if (skb_mac_header_was_set(skb)) + if (skb_mac_header_was_set(skb)) { *hoffset = skb_mac_offset(skb); + ret = 0; + } break; case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: *hoffset = skb_network_offset(skb); + ret = 0; break; case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: + ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP); + break; case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: - if (skb_transport_header_was_set(skb)) - *hoffset = skb_transport_offset(skb); + ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP); break; default: break; } + return ret; } static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, @@ -369,6 +402,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, int hoffset = 0; u32 *ptr, hdata; u32 val; + int rc; if (tkey_ex) { htype = tkey_ex->htype; @@ -377,7 +411,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tkey_ex++; } - pedit_skb_hdr_offset(skb, htype, &hoffset); + rc = pedit_skb_hdr_offset(skb, htype, &hoffset); + if (rc) { + pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype); + goto bad; + } if (tkey->offmask) { u8 *d, _d; -- 2.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y 8/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (6 preceding siblings ...) 2026-06-18 7:53 ` [PATCH 5.10.y v2 7/9] net/sched: act_pedit: Parse L3 Header for L4 offset Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 9/9] net/sched: act_pedit: free pedit keys on bail from offset check Wentao Guan 2026-06-19 4:06 ` [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591 From: Rajat Gupta <rajat.gupta@oss.qualcomm.com> [ Upstream commit 899ee91156e57784090c5565e4f31bd7dbffbc5a ] tcf_pedit_act() computes the COW range for skb_ensure_writable() once before the key loop using tcfp_off_max_hint, but the hint does not account for the runtime header offset added by typed keys. This can leave part of the write region un-COW'd. Fix by moving skb_ensure_writable() inside the per-key loop where the actual write offset is known, and add overflow checking on the offset arithmetic. For negative offsets (e.g. Ethernet header edits at ingress), use skb_cow() to COW the headroom instead. Guard offset_valid() against INT_MIN, where negation is undefined. Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable") Reported-by: Yiming Qian <yimingqian591@gmail.com> Reported-by: Keenan Dong <keenanat2000@gmail.com> Reported-by: Han Guidong <2045gemini@gmail.com> Reported-by: Zhang Cen <rollkingzzc@gmail.com> Reviewed-by: Han Guidong <2045gemini@gmail.com> Tested-by: Han Guidong <2045gemini@gmail.com> Reviewed-by: Davide Caratti <dcaratti@redhat.com> Tested-by: Davide Caratti <dcaratti@redhat.com> Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com> Tested-by: Toke Høiland-Jørgensen <toke@redhat.com> Reviewed-by: Victor Nogueira <victor@mojatatu.com> Tested-by: Victor Nogueira <victor@mojatatu.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Rajat Gupta <rajat.gupta@oss.qualcomm.com> Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com [rename include file from linux/unaligned.h to asm/unaligned.h] Conflicts: include/net/tc_act/tc_pedit.h net/sched/act_pedit.c Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 83fe399317818..a26d4cd3b8d6f 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 df31b2b7b4225..35fa94ba0edf8 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -17,6 +17,8 @@ #include <linux/ipv6.h> #include <linux/slab.h> #include <net/ipv6.h> +#include <linux/overflow.h> +#include <asm/unaligned.h> #include <net/netlink.h> #include <net/pkt_sched.h> #include <linux/tc_act/tc_pedit.h> @@ -229,7 +231,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, goto out_release; } - nparms->tcfp_off_max_hint = 0; nparms->tcfp_flags = parm->flags; nparms->tcfp_nkeys = parm->nkeys; @@ -257,14 +258,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, BITS_PER_TYPE(int) - 1, nparms->tcfp_keys[i].shift); - /* The AT option can read a single byte, we can bound the actual - * value with uchar max. - */ - cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift; - - /* Each key touches 4 bytes starting from the computed offset */ - nparms->tcfp_off_max_hint = - max(nparms->tcfp_off_max_hint, cur + 4); } p = to_pedit(*a); @@ -305,15 +298,12 @@ static void tcf_pedit_cleanup(struct tc_action *a) call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu); } -static bool offset_valid(struct sk_buff *skb, int offset) +static bool offset_valid(struct sk_buff *skb, int offset, int len) { - if (offset > 0 && offset > skb->len) - return false; - - if (offset < 0 && -offset > skb_headroom(skb)) + if (offset < -(int)skb_headroom(skb)) return false; - return true; + return offset <= (int)skb->len - len; } static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type) @@ -379,18 +369,10 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_pedit_key_ex *tkey_ex; struct tcf_pedit_parms *parms; struct tc_pedit_key *tkey; - u32 max_offset; int i; parms = rcu_dereference_bh(p->parms); - max_offset = (skb_transport_header_was_set(skb) ? - skb_transport_offset(skb) : - skb_network_offset(skb)) + - parms->tcfp_off_max_hint; - if (skb_ensure_writable(skb, min(skb->len, max_offset))) - goto done; - tcf_lastuse_update(&p->tcf_tm); tcf_action_update_bstats(&p->common, skb); @@ -398,10 +380,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tkey_ex = parms->tcfp_keys_ex; for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { + int write_offset, write_len; int offset = tkey->off; int hoffset = 0; - u32 *ptr, hdata; - u32 val; + u32 cur_val, val; + u32 *ptr; int rc; if (tkey_ex) { @@ -419,13 +402,15 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, if (tkey->offmask) { u8 *d, _d; + int at_offset; - if (!offset_valid(skb, hoffset + tkey->at)) { + if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) || + !offset_valid(skb, at_offset, sizeof(_d))) { pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n", hoffset + tkey->at); goto bad; } - d = skb_header_pointer(skb, hoffset + tkey->at, + d = skb_header_pointer(skb, at_offset, sizeof(_d), &_d); if (!d) goto bad; @@ -437,31 +422,51 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, } } - if (!offset_valid(skb, hoffset + offset)) { - pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset); + if (check_add_overflow(hoffset, offset, &write_offset)) { + pr_info_ratelimited("tc action pedit offset overflow\n"); goto bad; } - ptr = skb_header_pointer(skb, hoffset + offset, - sizeof(hdata), &hdata); - if (!ptr) + if (!offset_valid(skb, write_offset, sizeof(*ptr))) { + pr_info_ratelimited("tc action pedit offset %d out of bounds\n", + write_offset); goto bad; + } + + if (write_offset < 0) { + if (skb_cow(skb, -write_offset)) + goto bad; + if (write_offset + (int)sizeof(*ptr) > 0) { + if (skb_ensure_writable(skb, + min_t(int, skb->len, + write_offset + (int)sizeof(*ptr)))) + goto bad; + } + } else { + if (check_add_overflow(write_offset, (int)sizeof(*ptr), + &write_len)) + goto bad; + if (skb_ensure_writable(skb, min_t(int, skb->len, + write_len))) + goto bad; + } + + ptr = (u32 *)(skb->data + write_offset); + cur_val = get_unaligned(ptr); /* just do it, baby */ switch (cmd) { case TCA_PEDIT_KEY_EX_CMD_SET: val = tkey->val; break; case TCA_PEDIT_KEY_EX_CMD_ADD: - val = (*ptr + tkey->val) & ~tkey->mask; + val = (cur_val + tkey->val) & ~tkey->mask; break; default: pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd); goto bad; } - *ptr = ((*ptr & tkey->mask) ^ val); - if (ptr == &hdata) - skb_store_bits(skb, hoffset + offset, ptr, 4); + put_unaligned((cur_val & tkey->mask) ^ val, ptr); } goto done; -- 2.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v2 9/9] net/sched: act_pedit: free pedit keys on bail from offset check 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (7 preceding siblings ...) 2026-06-18 7:53 ` [PATCH 5.10.y 8/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan @ 2026-06-18 7:53 ` Wentao Guan 2026-06-19 4:06 ` [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin 9 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-18 7:53 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591, Pedro Tammela, Ido Schimmel, Ido Schimmel, Paolo Abeni From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb ] Ido Schimmel reports a memleak on a syzkaller instance: BUG: memory leak unreferenced object 0xffff88803d45e400 (size 1024): comm "syz-executor292", pid 563, jiffies 4295025223 (age 51.781s) hex dump (first 32 bytes): 28 bd 70 00 fb db df 25 02 00 14 1f ff 02 00 02 (.p....%........ 00 32 00 00 1f 00 00 00 ac 14 14 3e 08 00 07 00 .2.........>.... backtrace: [<ffffffff81bd0f2c>] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] [<ffffffff81bd0f2c>] slab_post_alloc_hook mm/slab.h:772 [inline] [<ffffffff81bd0f2c>] slab_alloc_node mm/slub.c:3452 [inline] [<ffffffff81bd0f2c>] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491 [<ffffffff81a865d9>] __do_kmalloc_node mm/slab_common.c:966 [inline] [<ffffffff81a865d9>] __kmalloc+0x59/0x1a0 mm/slab_common.c:980 [<ffffffff83aa85c3>] kmalloc include/linux/slab.h:584 [inline] [<ffffffff83aa85c3>] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245 [<ffffffff83a90623>] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394 [<ffffffff83a90e58>] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459 [<ffffffff83a96258>] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985 [<ffffffff83a96997>] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044 [<ffffffff83920a8d>] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395 [<ffffffff83b24305>] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575 [<ffffffff83901806>] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413 [<ffffffff83b21cae>] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] [<ffffffff83b21cae>] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365 [<ffffffff83b2293f>] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942 [<ffffffff8380c39f>] sock_sendmsg_nosec net/socket.c:724 [inline] [<ffffffff8380c39f>] sock_sendmsg net/socket.c:747 [inline] [<ffffffff8380c39f>] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503 [<ffffffff838156d2>] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557 [<ffffffff8381594f>] __sys_sendmsg+0x11f/0x200 net/socket.c:2586 [<ffffffff83815ab0>] __do_sys_sendmsg net/socket.c:2595 [inline] [<ffffffff83815ab0>] __se_sys_sendmsg net/socket.c:2593 [inline] [<ffffffff83815ab0>] __x64_sys_sendmsg+0x80/0xc0 net/socket.c:2593 The recently added static offset check missed a free to the key buffer when bailing out on error. Fixes: e1201bc781c2 ("net/sched: act_pedit: check static offsets a priori") Reported-by: Ido Schimmel <idosch@idosch.org> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Tested-by: Ido Schimmel <idosch@nvidia.com> Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 35fa94ba0edf8..0601deea04d72 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (8 preceding siblings ...) 2026-06-18 7:53 ` [PATCH 5.10.y v2 9/9] net/sched: act_pedit: free pedit keys on bail from offset check Wentao Guan @ 2026-06-19 4:06 ` Sasha Levin 2026-06-23 10:01 ` Wentao Guan 9 siblings, 1 reply; 28+ messages in thread From: Sasha Levin @ 2026-06-19 4:06 UTC (permalink / raw) To: sashal, gregkh, guanwentao Cc: stable, 2045gemini, dcaratti, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, toke, victor, yimingqian591 > [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to > page cache corruption (CVE-2026-46331) Queued the v2 series for 5.10, thanks. -- Thanks, Sasha ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-19 4:06 ` [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin @ 2026-06-23 10:01 ` Wentao Guan 2026-06-25 10:41 ` Sasha Levin 0 siblings, 1 reply; 28+ messages in thread From: Wentao Guan @ 2026-06-23 10:01 UTC (permalink / raw) To: sashal Cc: 2045gemini, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, stable, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller Hello, pls add 10/9 backport ("net/sched: act_pedit: fix action bind logic") commit e9e42292ea76a8358b0c02ffd530d78e133a1b73 upstream to the series, it also fixed the memleak in tcf_pedit_init() -> if (bind) -> tcfp_keys_ex leak. BRs Wentao Guan From 78ecdf028f51269c2031eddfc7c970eb5dd43221 Mon Sep 17 00:00:00 2001 From: Pedro Tammela <pctammela@mojatatu.com> Date: Fri, 24 Feb 2023 12:00:56 -0300 Subject: [PATCH] net/sched: act_pedit: fix action bind logic The TC architecture allows filters and actions to be created independently. In filters the user can reference action objects using: tc action add action pedit ... index 1 tc filter add ... action pedit index 1 In the current code for act_pedit this is broken as it checks netlink attributes for create/update before actually checking if we are binding to an existing action. tdc results: 1..69 ok 1 319a - Add pedit action that mangles IP TTL ok 2 7e67 - Replace pedit action with invalid goto chain ok 3 377e - Add pedit action with RAW_OP offset u32 ok 4 a0ca - Add pedit action with RAW_OP offset u32 (INVALID) ok 5 dd8a - Add pedit action with RAW_OP offset u16 u16 ok 6 53db - Add pedit action with RAW_OP offset u16 (INVALID) ok 7 5c7e - Add pedit action with RAW_OP offset u8 add value ok 8 2893 - Add pedit action with RAW_OP offset u8 quad ok 9 3a07 - Add pedit action with RAW_OP offset u8-u16-u8 ok 10 ab0f - Add pedit action with RAW_OP offset u16-u8-u8 ok 11 9d12 - Add pedit action with RAW_OP offset u32 set u16 clear u8 invert ok 12 ebfa - Add pedit action with RAW_OP offset overflow u32 (INVALID) ok 13 f512 - Add pedit action with RAW_OP offset u16 at offmask shift set ok 14 c2cb - Add pedit action with RAW_OP offset u32 retain value ok 15 1762 - Add pedit action with RAW_OP offset u8 clear value ok 16 bcee - Add pedit action with RAW_OP offset u8 retain value ok 17 e89f - Add pedit action with RAW_OP offset u16 retain value ok 18 c282 - Add pedit action with RAW_OP offset u32 clear value ok 19 c422 - Add pedit action with RAW_OP offset u16 invert value ok 20 d3d3 - Add pedit action with RAW_OP offset u32 invert value ok 21 57e5 - Add pedit action with RAW_OP offset u8 preserve value ok 22 99e0 - Add pedit action with RAW_OP offset u16 preserve value ok 23 1892 - Add pedit action with RAW_OP offset u32 preserve value ok 24 4b60 - Add pedit action with RAW_OP negative offset u16/u32 set value ok 25 a5a7 - Add pedit action with LAYERED_OP eth set src ok 26 86d4 - Add pedit action with LAYERED_OP eth set src & dst ok 27 f8a9 - Add pedit action with LAYERED_OP eth set dst ok 28 c715 - Add pedit action with LAYERED_OP eth set src (INVALID) ok 29 8131 - Add pedit action with LAYERED_OP eth set dst (INVALID) ok 30 ba22 - Add pedit action with LAYERED_OP eth type set/clear sequence ok 31 dec4 - Add pedit action with LAYERED_OP eth set type (INVALID) ok 32 ab06 - Add pedit action with LAYERED_OP eth add type ok 33 918d - Add pedit action with LAYERED_OP eth invert src ok 34 a8d4 - Add pedit action with LAYERED_OP eth invert dst ok 35 ee13 - Add pedit action with LAYERED_OP eth invert type ok 36 7588 - Add pedit action with LAYERED_OP ip set src ok 37 0fa7 - Add pedit action with LAYERED_OP ip set dst ok 38 5810 - Add pedit action with LAYERED_OP ip set src & dst ok 39 1092 - Add pedit action with LAYERED_OP ip set ihl & dsfield ok 40 02d8 - Add pedit action with LAYERED_OP ip set ttl & protocol ok 41 3e2d - Add pedit action with LAYERED_OP ip set ttl (INVALID) ok 42 31ae - Add pedit action with LAYERED_OP ip ttl clear/set ok 43 486f - Add pedit action with LAYERED_OP ip set duplicate fields ok 44 e790 - Add pedit action with LAYERED_OP ip set ce, df, mf, firstfrag, nofrag fields ok 45 cc8a - Add pedit action with LAYERED_OP ip set tos ok 46 7a17 - Add pedit action with LAYERED_OP ip set precedence ok 47 c3b6 - Add pedit action with LAYERED_OP ip add tos ok 48 43d3 - Add pedit action with LAYERED_OP ip add precedence ok 49 438e - Add pedit action with LAYERED_OP ip clear tos ok 50 6b1b - Add pedit action with LAYERED_OP ip clear precedence ok 51 824a - Add pedit action with LAYERED_OP ip invert tos ok 52 106f - Add pedit action with LAYERED_OP ip invert precedence ok 53 6829 - Add pedit action with LAYERED_OP beyond ip set dport & sport ok 54 afd8 - Add pedit action with LAYERED_OP beyond ip set icmp_type & icmp_code ok 55 3143 - Add pedit action with LAYERED_OP beyond ip set dport (INVALID) ok 56 815c - Add pedit action with LAYERED_OP ip6 set src ok 57 4dae - Add pedit action with LAYERED_OP ip6 set dst ok 58 fc1f - Add pedit action with LAYERED_OP ip6 set src & dst ok 59 6d34 - Add pedit action with LAYERED_OP ip6 dst retain value (INVALID) ok 60 94bb - Add pedit action with LAYERED_OP ip6 traffic_class ok 61 6f5e - Add pedit action with LAYERED_OP ip6 flow_lbl ok 62 6795 - Add pedit action with LAYERED_OP ip6 set payload_len, nexthdr, hoplimit ok 63 1442 - Add pedit action with LAYERED_OP tcp set dport & sport ok 64 b7ac - Add pedit action with LAYERED_OP tcp sport set (INVALID) ok 65 cfcc - Add pedit action with LAYERED_OP tcp flags set ok 66 3bc4 - Add pedit action with LAYERED_OP tcp set dport, sport & flags fields ok 67 f1c8 - Add pedit action with LAYERED_OP udp set dport & sport ok 68 d784 - Add pedit action with mixed RAW/LAYERED_OP #1 ok 69 70ca - Add pedit action with mixed RAW/LAYERED_OP #2 Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Fixes: f67169fef8db ("net/sched: act_pedit: fix WARN() in the traffic path") Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit e9e42292ea76a8358b0c02ffd530d78e133a1b73) Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- net/sched/act_pedit.c | 58 +++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 35fa94ba0edf8..d40907e7771f4 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -182,26 +182,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, } parm = nla_data(pattr); - if (!parm->nkeys) { - NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed"); - return -EINVAL; - } - ksize = parm->nkeys * sizeof(struct tc_pedit_key); - if (nla_len(pattr) < sizeof(*parm) + ksize) { - NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid"); - return -EINVAL; - } - - 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); @@ -210,25 +190,49 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, &act_pedit_ops, bind, flags); if (ret) { tcf_idr_cleanup(tn, index); - goto out_free_ex; + return ret; } ret = ACT_P_CREATED; } else if (err > 0) { if (bind) - goto out_free; + return 0; if (!ovr) { ret = -EEXIST; goto out_release; } } else { - ret = err; - goto out_free_ex; + return err; + } + + if (!parm->nkeys) { + NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed"); + ret = -EINVAL; + goto out_release; + } + ksize = parm->nkeys * sizeof(struct tc_pedit_key); + if (nla_len(pattr) < sizeof(*parm) + ksize) { + NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid"); + ret = -EINVAL; + goto out_release; + } + + nparms = kzalloc(sizeof(*nparms), GFP_KERNEL); + if (!nparms) { + ret = -ENOMEM; + goto out_release; + } + + 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; } err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); if (err < 0) { ret = err; - goto out_release; + goto out_free_ex; } nparms->tcfp_flags = parm->flags; @@ -278,12 +282,12 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, put_chain: if (goto_ch) 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(nparms); +out_release: + tcf_idr_release(*a, bind); return ret; } -- 2.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-23 10:01 ` Wentao Guan @ 2026-06-25 10:41 ` Sasha Levin 2026-06-25 11:45 ` Wentao Guan 2026-06-26 17:54 ` Sasha Levin 0 siblings, 2 replies; 28+ messages in thread From: Sasha Levin @ 2026-06-25 10:41 UTC (permalink / raw) To: guanwentao Cc: Sasha Levin, 2045gemini, dcaratti, gregkh, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, stable, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller > Please add net/sched: act_pedit: fix action bind logic (upstream > e9e42292ea76) as a 10th patch to the 5.10.y pedit series - it also > fixes the tcfp_keys_ex memleak on the if (bind) early-return path. It doesn't apply cleanly. Could you send a backport please? -- Thanks, Sasha ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-25 10:41 ` Sasha Levin @ 2026-06-25 11:45 ` Wentao Guan 2026-06-26 17:54 ` Sasha Levin 1 sibling, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-25 11:45 UTC (permalink / raw) To: Sasha Levin Cc: Sasha Levin, 2045gemini, dcaratti, Greg KH, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, stable, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, davem Hi Sasha, > > Please add net/sched: act_pedit: fix action bind logic (upstream > > e9e42292ea76) as a 10th patch to the 5.10.y pedit series - it also > > fixes the tcfp_keys_ex memleak on the if (bind) early-return path. > > It doesn't apply cleanly. Could you send a backport please? I sent the backport in https://lore.kernel.org/stable/20260623100141.2383966-1-guanwentao@uniontech.com/ and the series can apply in v5.10.y: eec0bb1f2a236 (HEAD) net/sched: act_pedit: fix action bind logic b19978c6043be net/sched: act_pedit: free pedit keys on bail from offset check fab4425a6fe8f net/sched: fix pedit partial COW leading to page cache corruption fc9e1255ef4bb net/sched: act_pedit: Parse L3 Header for L4 offset 275c264dbce08 net/sched: act_pedit: rate limit datapath messages ba97882f1e194 net/sched: act_pedit: check static offsets a priori d1b0e1e1e6ac2 net/sched: act_pedit: remove extra check for key type 12fe9e83dea27 net/sched: simplify tcf_pedit_act fb630e2e978fd net/sched: transition act_pedit to rcu and percpu stats 5b742d7402870 net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys 5dbd240ac9a06 (tag: v5.10.258, stable-gh/linux-5.10.y) Linux 5.10.258 BRs Wentao Guan ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-25 10:41 ` Sasha Levin 2026-06-25 11:45 ` Wentao Guan @ 2026-06-26 17:54 ` Sasha Levin 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan 2026-06-26 18:18 ` [PATCH 5.10.y v2 0/9] " Wentao Guan 1 sibling, 2 replies; 28+ messages in thread From: Sasha Levin @ 2026-06-26 17:54 UTC (permalink / raw) To: guanwentao Cc: Sasha Levin, 2045gemini, dcaratti, gregkh, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, stable, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, David S . Miller > pls add 10/9 backport (net/sched: act_pedit: fix action bind logic) Thanks Wentao. The inline-pasted backport got mangled by mail reflow (the @@ hunk counts no longer match the body), so it's not git-am'able, and a plain cherry-pick of e9e42292ea76 doesn't apply on top of the queued v2 series. Could you resend it as a standalone [PATCH 5.10.y] via git send-email? -- Thanks, Sasha ^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 00/10] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-26 17:54 ` Sasha Levin @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 01/10] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan ` (10 more replies) 2026-06-26 18:18 ` [PATCH 5.10.y v2 0/9] " Wentao Guan 1 sibling, 11 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 Fixes CVE-2026-46331: net/sched: fix pedit partial COW leading to page cache corruption Link: https://lore.kernel.org/all/2026061625-CVE-2026-46331-47be@gregkh/ changelog v3: Add ("net/sched: act_pedit: fix action bind logic") commit, it also fixed the memleak in tcf_pedit_init() -> if (bind) -> tcfp_keys_ex leak. v2 Link: https://lore.kernel.org/stable/20260618075342.1599593-1-guanwentao@uniontech.com/ changelog v2: Add ("net/sched: act_pedit: Parse L3 Header for L4 offset") commit. v1 Link: https://lore.kernel.org/stable/20260618043539.1557035-1-guanwentao@uniontech.com/T/#ma9ef6d260833405f60ae88a1686e967b1416d80c Max Tottenham (1): net/sched: act_pedit: Parse L3 Header for L4 offset Pedro Tammela (8): net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys net/sched: transition act_pedit to rcu and percpu stats net/sched: simplify tcf_pedit_act net/sched: act_pedit: remove extra check for key type net/sched: act_pedit: check static offsets a priori net/sched: act_pedit: rate limit datapath messages net/sched: act_pedit: free pedit keys on bail from offset check net/sched: act_pedit: fix action bind logic Rajat Gupta (1): net/sched: fix pedit partial COW leading to page cache corruption include/net/tc_act/tc_pedit.h | 80 +++++-- net/sched/act_pedit.c | 390 +++++++++++++++++++--------------- 2 files changed, 289 insertions(+), 181 deletions(-) -- 2.30.2 ^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 01/10] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 02/10] net/sched: transition act_pedit to rcu and percpu stats Wentao Guan ` (9 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 5036034572b79daa6d6600338e8e8229e2a44b09 ] Transform two checks in the 'ex' key parsing into netlink policies removing extra if checks. Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 a44101b2f4419..510a3b5b8c0c1 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 02/10] net/sched: transition act_pedit to rcu and percpu stats 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 01/10] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 03/10] net/sched: simplify tcf_pedit_act Wentao Guan ` (8 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591, Paolo Abeni From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 52cf89f78c01bf39973f3e70d366921d70faff7a ] The software pedit action didn't get the same love as some of the other actions and it's still using spinlocks and shared stats in the datapath. Transition the action to rcu and percpu stats as this improves the action's performance dramatically on multiple cpu deployments. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Conflicts: net/sched/act_pedit.c Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 3e02709a1df65..83fe399317818 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -4,22 +4,29 @@ #include <net/act_api.h> #include <linux/tc_act/tc_pedit.h> +#include <linux/types.h> struct tcf_pedit_key_ex { enum pedit_header_type htype; enum pedit_cmd cmd; }; -struct tcf_pedit { - struct tc_action common; - unsigned char tcfp_nkeys; - unsigned char tcfp_flags; - u32 tcfp_off_max_hint; +struct tcf_pedit_parms { struct tc_pedit_key *tcfp_keys; struct tcf_pedit_key_ex *tcfp_keys_ex; + u32 tcfp_off_max_hint; + unsigned char tcfp_nkeys; + unsigned char tcfp_flags; + struct rcu_head rcu; +}; + +struct tcf_pedit { + struct tc_action common; + struct tcf_pedit_parms __rcu *parms; }; #define to_pedit(a) ((struct tcf_pedit *)a) +#define to_pedit_parms(a) (rcu_dereference(to_pedit(a)->parms)) static inline bool is_tcf_pedit(const struct tc_action *a) { @@ -32,37 +39,81 @@ static inline bool is_tcf_pedit(const struct tc_action *a) static inline int tcf_pedit_nkeys(const struct tc_action *a) { - return to_pedit(a)->tcfp_nkeys; + struct tcf_pedit_parms *parms; + int nkeys; + + rcu_read_lock(); + parms = to_pedit_parms(a); + nkeys = parms->tcfp_nkeys; + rcu_read_unlock(); + + return nkeys; } static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) { - if (to_pedit(a)->tcfp_keys_ex) - return to_pedit(a)->tcfp_keys_ex[index].htype; + u32 htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; + struct tcf_pedit_parms *parms; + + rcu_read_lock(); + parms = to_pedit_parms(a); + if (parms->tcfp_keys_ex) + htype = parms->tcfp_keys_ex[index].htype; + rcu_read_unlock(); - return TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; + return htype; } static inline u32 tcf_pedit_cmd(const struct tc_action *a, int index) { - if (to_pedit(a)->tcfp_keys_ex) - return to_pedit(a)->tcfp_keys_ex[index].cmd; + struct tcf_pedit_parms *parms; + u32 cmd = __PEDIT_CMD_MAX; - return __PEDIT_CMD_MAX; + rcu_read_lock(); + parms = to_pedit_parms(a); + if (parms->tcfp_keys_ex) + cmd = parms->tcfp_keys_ex[index].cmd; + rcu_read_unlock(); + + return cmd; } static inline u32 tcf_pedit_mask(const struct tc_action *a, int index) { - return to_pedit(a)->tcfp_keys[index].mask; + struct tcf_pedit_parms *parms; + u32 mask; + + rcu_read_lock(); + parms = to_pedit_parms(a); + mask = parms->tcfp_keys[index].mask; + rcu_read_unlock(); + + return mask; } static inline u32 tcf_pedit_val(const struct tc_action *a, int index) { - return to_pedit(a)->tcfp_keys[index].val; + struct tcf_pedit_parms *parms; + u32 val; + + rcu_read_lock(); + parms = to_pedit_parms(a); + val = parms->tcfp_keys[index].val; + rcu_read_unlock(); + + return val; } static inline u32 tcf_pedit_offset(const struct tc_action *a, int index) { - return to_pedit(a)->tcfp_keys[index].off; + struct tcf_pedit_parms *parms; + u32 off; + + rcu_read_lock(); + parms = to_pedit_parms(a); + off = parms->tcfp_keys[index].off; + rcu_read_unlock(); + + return off; } #endif /* __NET_TC_PED_H */ diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 510a3b5b8c0c1..0fbffebfbdc9d 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 03/10] net/sched: simplify tcf_pedit_act 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 01/10] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan 2026-06-26 18:07 ` [PATCH 02/10] net/sched: transition act_pedit to rcu and percpu stats Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 04/10] net/sched: act_pedit: remove extra check for key type Wentao Guan ` (7 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591, Paolo Abeni From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 95b069382351826c0ae37938070aa82dbeaf288d ] Remove the check for a negative number of keys as this cannot ever happen Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 0fbffebfbdc9d..84152d3a49246 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 04/10] net/sched: act_pedit: remove extra check for key type 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (2 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 03/10] net/sched: simplify tcf_pedit_act Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 05/10] net/sched: act_pedit: check static offsets a priori Wentao Guan ` (6 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 577140180ba28d0d37bc898c7bd6702c83aa106f ] The netlink parsing already validates the key 'htype'. Remove the datapath check as it's redundant. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 84152d3a49246..957ce9017c3f7 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 05/10] net/sched: act_pedit: check static offsets a priori 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (3 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 04/10] net/sched: act_pedit: remove extra check for key type Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 06/10] net/sched: act_pedit: rate limit datapath messages Wentao Guan ` (5 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit e1201bc781c28766720e78a5e099ffa568be4d74 ] Static key offsets should always be on 32 bit boundaries. Validate them on create/update time for static offsets and move the datapath validation for runtime offsets only. iproute2 already errors out if a given offset and data size cannot be packed to a 32 bit boundary. This change will make sure users which create/update pedit instances directly via netlink also error out, instead of finding out when packets are traversing. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 957ce9017c3f7..95ae885ecba16 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 06/10] net/sched: act_pedit: rate limit datapath messages 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (4 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 05/10] net/sched: act_pedit: check static offsets a priori Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 07/10] net/sched: act_pedit: Parse L3 Header for L4 offset Wentao Guan ` (4 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit e3c9673e2f6e1b3aa4bb87c570336e10f364c28a ] Unbounded info messages in the pedit datapath can flood the printk ring buffer quite easily depending on the action created. As these messages are informational, usually printing some, not all, is enough to bring attention to the real issue. Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 95ae885ecba16..ecad6fc39dc3d 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 07/10] net/sched: act_pedit: Parse L3 Header for L4 offset 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (5 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 06/10] net/sched: act_pedit: rate limit datapath messages Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 08/10] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan ` (3 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591, Max Tottenham, Josh Hunt, kernel test robot From: Max Tottenham <mtottenh@akamai.com> [ Upstream commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b ] Instead of relying on skb->transport_header being set correctly, opt instead to parse the L3 header length out of the L3 headers for both IPv4/IPv6 when the Extended Layer Op for tcp/udp is used. This fixes a bug if GRO is disabled, when GRO is disabled skb->transport_header is set by __netif_receive_skb_core() to point to the L3 header, it's later fixed by the upper protocol layers, but act_pedit will receive the SKB before the fixups are completed. The existing behavior causes the following to edit the L3 header if GRO is disabled instead of the UDP header: tc filter add dev eth0 ingress protocol ip flower ip_proto udp \ dst_ip 192.168.1.3 action pedit ex munge udp set dport 18053 Also re-introduce a rate-limited warning if we were unable to extract the header offset when using the 'ex' interface. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Signed-off-by: Max Tottenham <mtottenh@akamai.com> Reviewed-by: Josh Hunt <johunt@akamai.com> Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202305261541.N165u9TZ-lkp@intel.com/ Reviewed-by: Pedro Tammela <pctammela@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit 6c02568fd1ae53099b4ab86365c5be1ff15f586b) Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 ecad6fc39dc3d..df31b2b7b4225 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -13,7 +13,10 @@ #include <linux/rtnetlink.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/ip.h> +#include <linux/ipv6.h> #include <linux/slab.h> +#include <net/ipv6.h> #include <net/netlink.h> #include <net/pkt_sched.h> #include <linux/tc_act/tc_pedit.h> @@ -313,28 +316,58 @@ static bool offset_valid(struct sk_buff *skb, int offset) return true; } -static void pedit_skb_hdr_offset(struct sk_buff *skb, +static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type) +{ + const int noff = skb_network_offset(skb); + int ret = -EINVAL; + struct iphdr _iph; + + switch (skb->protocol) { + case htons(ETH_P_IP): { + const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph); + + if (!iph) + goto out; + *hoffset = noff + iph->ihl * 4; + ret = 0; + break; + } + case htons(ETH_P_IPV6): + ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL; + break; + } +out: + return ret; +} + +static int pedit_skb_hdr_offset(struct sk_buff *skb, enum pedit_header_type htype, int *hoffset) { + int ret = -EINVAL; /* 'htype' is validated in the netlink parsing */ switch (htype) { case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH: - if (skb_mac_header_was_set(skb)) + if (skb_mac_header_was_set(skb)) { *hoffset = skb_mac_offset(skb); + ret = 0; + } break; case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4: case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6: *hoffset = skb_network_offset(skb); + ret = 0; break; case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP: + ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP); + break; case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP: - if (skb_transport_header_was_set(skb)) - *hoffset = skb_transport_offset(skb); + ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP); break; default: break; } + return ret; } static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, @@ -369,6 +402,7 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, int hoffset = 0; u32 *ptr, hdata; u32 val; + int rc; if (tkey_ex) { htype = tkey_ex->htype; @@ -377,7 +411,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tkey_ex++; } - pedit_skb_hdr_offset(skb, htype, &hoffset); + rc = pedit_skb_hdr_offset(skb, htype, &hoffset); + if (rc) { + pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype); + goto bad; + } if (tkey->offmask) { u8 *d, _d; -- 2.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 08/10] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (6 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 07/10] net/sched: act_pedit: Parse L3 Header for L4 offset Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 09/10] net/sched: act_pedit: free pedit keys on bail from offset check Wentao Guan ` (2 subsequent siblings) 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 From: Rajat Gupta <rajat.gupta@oss.qualcomm.com> [ Upstream commit 899ee91156e57784090c5565e4f31bd7dbffbc5a ] tcf_pedit_act() computes the COW range for skb_ensure_writable() once before the key loop using tcfp_off_max_hint, but the hint does not account for the runtime header offset added by typed keys. This can leave part of the write region un-COW'd. Fix by moving skb_ensure_writable() inside the per-key loop where the actual write offset is known, and add overflow checking on the offset arithmetic. For negative offsets (e.g. Ethernet header edits at ingress), use skb_cow() to COW the headroom instead. Guard offset_valid() against INT_MIN, where negation is undefined. Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable") Reported-by: Yiming Qian <yimingqian591@gmail.com> Reported-by: Keenan Dong <keenanat2000@gmail.com> Reported-by: Han Guidong <2045gemini@gmail.com> Reported-by: Zhang Cen <rollkingzzc@gmail.com> Reviewed-by: Han Guidong <2045gemini@gmail.com> Tested-by: Han Guidong <2045gemini@gmail.com> Reviewed-by: Davide Caratti <dcaratti@redhat.com> Tested-by: Davide Caratti <dcaratti@redhat.com> Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com> Tested-by: Toke Høiland-Jørgensen <toke@redhat.com> Reviewed-by: Victor Nogueira <victor@mojatatu.com> Tested-by: Victor Nogueira <victor@mojatatu.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Rajat Gupta <rajat.gupta@oss.qualcomm.com> Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com [rename include file from linux/unaligned.h to asm/unaligned.h] Conflicts: include/net/tc_act/tc_pedit.h net/sched/act_pedit.c Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 83fe399317818..a26d4cd3b8d6f 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 df31b2b7b4225..35fa94ba0edf8 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -17,6 +17,8 @@ #include <linux/ipv6.h> #include <linux/slab.h> #include <net/ipv6.h> +#include <linux/overflow.h> +#include <asm/unaligned.h> #include <net/netlink.h> #include <net/pkt_sched.h> #include <linux/tc_act/tc_pedit.h> @@ -229,7 +231,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, goto out_release; } - nparms->tcfp_off_max_hint = 0; nparms->tcfp_flags = parm->flags; nparms->tcfp_nkeys = parm->nkeys; @@ -257,14 +258,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, BITS_PER_TYPE(int) - 1, nparms->tcfp_keys[i].shift); - /* The AT option can read a single byte, we can bound the actual - * value with uchar max. - */ - cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift; - - /* Each key touches 4 bytes starting from the computed offset */ - nparms->tcfp_off_max_hint = - max(nparms->tcfp_off_max_hint, cur + 4); } p = to_pedit(*a); @@ -305,15 +298,12 @@ static void tcf_pedit_cleanup(struct tc_action *a) call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu); } -static bool offset_valid(struct sk_buff *skb, int offset) +static bool offset_valid(struct sk_buff *skb, int offset, int len) { - if (offset > 0 && offset > skb->len) - return false; - - if (offset < 0 && -offset > skb_headroom(skb)) + if (offset < -(int)skb_headroom(skb)) return false; - return true; + return offset <= (int)skb->len - len; } static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type) @@ -379,18 +369,10 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_pedit_key_ex *tkey_ex; struct tcf_pedit_parms *parms; struct tc_pedit_key *tkey; - u32 max_offset; int i; parms = rcu_dereference_bh(p->parms); - max_offset = (skb_transport_header_was_set(skb) ? - skb_transport_offset(skb) : - skb_network_offset(skb)) + - parms->tcfp_off_max_hint; - if (skb_ensure_writable(skb, min(skb->len, max_offset))) - goto done; - tcf_lastuse_update(&p->tcf_tm); tcf_action_update_bstats(&p->common, skb); @@ -398,10 +380,11 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, tkey_ex = parms->tcfp_keys_ex; for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { + int write_offset, write_len; int offset = tkey->off; int hoffset = 0; - u32 *ptr, hdata; - u32 val; + u32 cur_val, val; + u32 *ptr; int rc; if (tkey_ex) { @@ -419,13 +402,15 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, if (tkey->offmask) { u8 *d, _d; + int at_offset; - if (!offset_valid(skb, hoffset + tkey->at)) { + if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) || + !offset_valid(skb, at_offset, sizeof(_d))) { pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n", hoffset + tkey->at); goto bad; } - d = skb_header_pointer(skb, hoffset + tkey->at, + d = skb_header_pointer(skb, at_offset, sizeof(_d), &_d); if (!d) goto bad; @@ -437,31 +422,51 @@ static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a, } } - if (!offset_valid(skb, hoffset + offset)) { - pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset); + if (check_add_overflow(hoffset, offset, &write_offset)) { + pr_info_ratelimited("tc action pedit offset overflow\n"); goto bad; } - ptr = skb_header_pointer(skb, hoffset + offset, - sizeof(hdata), &hdata); - if (!ptr) + if (!offset_valid(skb, write_offset, sizeof(*ptr))) { + pr_info_ratelimited("tc action pedit offset %d out of bounds\n", + write_offset); goto bad; + } + + if (write_offset < 0) { + if (skb_cow(skb, -write_offset)) + goto bad; + if (write_offset + (int)sizeof(*ptr) > 0) { + if (skb_ensure_writable(skb, + min_t(int, skb->len, + write_offset + (int)sizeof(*ptr)))) + goto bad; + } + } else { + if (check_add_overflow(write_offset, (int)sizeof(*ptr), + &write_len)) + goto bad; + if (skb_ensure_writable(skb, min_t(int, skb->len, + write_len))) + goto bad; + } + + ptr = (u32 *)(skb->data + write_offset); + cur_val = get_unaligned(ptr); /* just do it, baby */ switch (cmd) { case TCA_PEDIT_KEY_EX_CMD_SET: val = tkey->val; break; case TCA_PEDIT_KEY_EX_CMD_ADD: - val = (*ptr + tkey->val) & ~tkey->mask; + val = (cur_val + tkey->val) & ~tkey->mask; break; default: pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd); goto bad; } - *ptr = ((*ptr & tkey->mask) ^ val); - if (ptr == &hdata) - skb_store_bits(skb, hoffset + offset, ptr, 4); + put_unaligned((cur_val & tkey->mask) ^ val, ptr); } goto done; -- 2.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 09/10] net/sched: act_pedit: free pedit keys on bail from offset check 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (7 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 08/10] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 10/10] net/sched: act_pedit: fix action bind logic Wentao Guan 2026-06-27 16:35 ` [PATCH 5.10.y v3 00/10] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591, Ido Schimmel, Ido Schimmel, Paolo Abeni From: Pedro Tammela <pctammela@mojatatu.com> [ Upstream commit 1b483d9f5805c7e3d628d4995e97f4311fcb82eb ] Ido Schimmel reports a memleak on a syzkaller instance: BUG: memory leak unreferenced object 0xffff88803d45e400 (size 1024): comm "syz-executor292", pid 563, jiffies 4295025223 (age 51.781s) hex dump (first 32 bytes): 28 bd 70 00 fb db df 25 02 00 14 1f ff 02 00 02 (.p....%........ 00 32 00 00 1f 00 00 00 ac 14 14 3e 08 00 07 00 .2.........>.... backtrace: [<ffffffff81bd0f2c>] kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] [<ffffffff81bd0f2c>] slab_post_alloc_hook mm/slab.h:772 [inline] [<ffffffff81bd0f2c>] slab_alloc_node mm/slub.c:3452 [inline] [<ffffffff81bd0f2c>] __kmem_cache_alloc_node+0x25c/0x320 mm/slub.c:3491 [<ffffffff81a865d9>] __do_kmalloc_node mm/slab_common.c:966 [inline] [<ffffffff81a865d9>] __kmalloc+0x59/0x1a0 mm/slab_common.c:980 [<ffffffff83aa85c3>] kmalloc include/linux/slab.h:584 [inline] [<ffffffff83aa85c3>] tcf_pedit_init+0x793/0x1ae0 net/sched/act_pedit.c:245 [<ffffffff83a90623>] tcf_action_init_1+0x453/0x6e0 net/sched/act_api.c:1394 [<ffffffff83a90e58>] tcf_action_init+0x5a8/0x950 net/sched/act_api.c:1459 [<ffffffff83a96258>] tcf_action_add+0x118/0x4e0 net/sched/act_api.c:1985 [<ffffffff83a96997>] tc_ctl_action+0x377/0x490 net/sched/act_api.c:2044 [<ffffffff83920a8d>] rtnetlink_rcv_msg+0x46d/0xd70 net/core/rtnetlink.c:6395 [<ffffffff83b24305>] netlink_rcv_skb+0x185/0x490 net/netlink/af_netlink.c:2575 [<ffffffff83901806>] rtnetlink_rcv+0x26/0x30 net/core/rtnetlink.c:6413 [<ffffffff83b21cae>] netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline] [<ffffffff83b21cae>] netlink_unicast+0x5be/0x8a0 net/netlink/af_netlink.c:1365 [<ffffffff83b2293f>] netlink_sendmsg+0x9af/0xed0 net/netlink/af_netlink.c:1942 [<ffffffff8380c39f>] sock_sendmsg_nosec net/socket.c:724 [inline] [<ffffffff8380c39f>] sock_sendmsg net/socket.c:747 [inline] [<ffffffff8380c39f>] ____sys_sendmsg+0x3ef/0xaa0 net/socket.c:2503 [<ffffffff838156d2>] ___sys_sendmsg+0x122/0x1c0 net/socket.c:2557 [<ffffffff8381594f>] __sys_sendmsg+0x11f/0x200 net/socket.c:2586 [<ffffffff83815ab0>] __do_sys_sendmsg net/socket.c:2595 [inline] [<ffffffff83815ab0>] __se_sys_sendmsg net/socket.c:2593 [inline] [<ffffffff83815ab0>] __x64_sys_sendmsg+0x80/0xc0 net/socket.c:2593 The recently added static offset check missed a free to the key buffer when bailing out on error. Fixes: e1201bc781c2 ("net/sched: act_pedit: check static offsets a priori") Reported-by: Ido Schimmel <idosch@idosch.org> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Tested-by: Ido Schimmel <idosch@nvidia.com> Link: https://lore.kernel.org/r/20230425144725.669262-1-pctammela@mojatatu.com Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- 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 35fa94ba0edf8..0601deea04d72 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.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 5.10.y v3 10/10] net/sched: act_pedit: fix action bind logic 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (8 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 09/10] net/sched: act_pedit: free pedit keys on bail from offset check Wentao Guan @ 2026-06-26 18:07 ` Wentao Guan 2026-06-27 16:35 ` [PATCH 5.10.y v3 00/10] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin 10 siblings, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:07 UTC (permalink / raw) To: sashal Cc: 2045gemini, davem, dcaratti, gregkh, guanwentao, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 From: Pedro Tammela <pctammela@mojatatu.com> The TC architecture allows filters and actions to be created independently. In filters the user can reference action objects using: tc action add action pedit ... index 1 tc filter add ... action pedit index 1 In the current code for act_pedit this is broken as it checks netlink attributes for create/update before actually checking if we are binding to an existing action. tdc results: 1..69 ok 1 319a - Add pedit action that mangles IP TTL ok 2 7e67 - Replace pedit action with invalid goto chain ok 3 377e - Add pedit action with RAW_OP offset u32 ok 4 a0ca - Add pedit action with RAW_OP offset u32 (INVALID) ok 5 dd8a - Add pedit action with RAW_OP offset u16 u16 ok 6 53db - Add pedit action with RAW_OP offset u16 (INVALID) ok 7 5c7e - Add pedit action with RAW_OP offset u8 add value ok 8 2893 - Add pedit action with RAW_OP offset u8 quad ok 9 3a07 - Add pedit action with RAW_OP offset u8-u16-u8 ok 10 ab0f - Add pedit action with RAW_OP offset u16-u8-u8 ok 11 9d12 - Add pedit action with RAW_OP offset u32 set u16 clear u8 invert ok 12 ebfa - Add pedit action with RAW_OP offset overflow u32 (INVALID) ok 13 f512 - Add pedit action with RAW_OP offset u16 at offmask shift set ok 14 c2cb - Add pedit action with RAW_OP offset u32 retain value ok 15 1762 - Add pedit action with RAW_OP offset u8 clear value ok 16 bcee - Add pedit action with RAW_OP offset u8 retain value ok 17 e89f - Add pedit action with RAW_OP offset u16 retain value ok 18 c282 - Add pedit action with RAW_OP offset u32 clear value ok 19 c422 - Add pedit action with RAW_OP offset u16 invert value ok 20 d3d3 - Add pedit action with RAW_OP offset u32 invert value ok 21 57e5 - Add pedit action with RAW_OP offset u8 preserve value ok 22 99e0 - Add pedit action with RAW_OP offset u16 preserve value ok 23 1892 - Add pedit action with RAW_OP offset u32 preserve value ok 24 4b60 - Add pedit action with RAW_OP negative offset u16/u32 set value ok 25 a5a7 - Add pedit action with LAYERED_OP eth set src ok 26 86d4 - Add pedit action with LAYERED_OP eth set src & dst ok 27 f8a9 - Add pedit action with LAYERED_OP eth set dst ok 28 c715 - Add pedit action with LAYERED_OP eth set src (INVALID) ok 29 8131 - Add pedit action with LAYERED_OP eth set dst (INVALID) ok 30 ba22 - Add pedit action with LAYERED_OP eth type set/clear sequence ok 31 dec4 - Add pedit action with LAYERED_OP eth set type (INVALID) ok 32 ab06 - Add pedit action with LAYERED_OP eth add type ok 33 918d - Add pedit action with LAYERED_OP eth invert src ok 34 a8d4 - Add pedit action with LAYERED_OP eth invert dst ok 35 ee13 - Add pedit action with LAYERED_OP eth invert type ok 36 7588 - Add pedit action with LAYERED_OP ip set src ok 37 0fa7 - Add pedit action with LAYERED_OP ip set dst ok 38 5810 - Add pedit action with LAYERED_OP ip set src & dst ok 39 1092 - Add pedit action with LAYERED_OP ip set ihl & dsfield ok 40 02d8 - Add pedit action with LAYERED_OP ip set ttl & protocol ok 41 3e2d - Add pedit action with LAYERED_OP ip set ttl (INVALID) ok 42 31ae - Add pedit action with LAYERED_OP ip ttl clear/set ok 43 486f - Add pedit action with LAYERED_OP ip set duplicate fields ok 44 e790 - Add pedit action with LAYERED_OP ip set ce, df, mf, firstfrag, nofrag fields ok 45 cc8a - Add pedit action with LAYERED_OP ip set tos ok 46 7a17 - Add pedit action with LAYERED_OP ip set precedence ok 47 c3b6 - Add pedit action with LAYERED_OP ip add tos ok 48 43d3 - Add pedit action with LAYERED_OP ip add precedence ok 49 438e - Add pedit action with LAYERED_OP ip clear tos ok 50 6b1b - Add pedit action with LAYERED_OP ip clear precedence ok 51 824a - Add pedit action with LAYERED_OP ip invert tos ok 52 106f - Add pedit action with LAYERED_OP ip invert precedence ok 53 6829 - Add pedit action with LAYERED_OP beyond ip set dport & sport ok 54 afd8 - Add pedit action with LAYERED_OP beyond ip set icmp_type & icmp_code ok 55 3143 - Add pedit action with LAYERED_OP beyond ip set dport (INVALID) ok 56 815c - Add pedit action with LAYERED_OP ip6 set src ok 57 4dae - Add pedit action with LAYERED_OP ip6 set dst ok 58 fc1f - Add pedit action with LAYERED_OP ip6 set src & dst ok 59 6d34 - Add pedit action with LAYERED_OP ip6 dst retain value (INVALID) ok 60 94bb - Add pedit action with LAYERED_OP ip6 traffic_class ok 61 6f5e - Add pedit action with LAYERED_OP ip6 flow_lbl ok 62 6795 - Add pedit action with LAYERED_OP ip6 set payload_len, nexthdr, hoplimit ok 63 1442 - Add pedit action with LAYERED_OP tcp set dport & sport ok 64 b7ac - Add pedit action with LAYERED_OP tcp sport set (INVALID) ok 65 cfcc - Add pedit action with LAYERED_OP tcp flags set ok 66 3bc4 - Add pedit action with LAYERED_OP tcp set dport, sport & flags fields ok 67 f1c8 - Add pedit action with LAYERED_OP udp set dport & sport ok 68 d784 - Add pedit action with mixed RAW/LAYERED_OP #1 ok 69 70ca - Add pedit action with mixed RAW/LAYERED_OP #2 Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Fixes: f67169fef8db ("net/sched: act_pedit: fix WARN() in the traffic path") Reviewed-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit e9e42292ea76a8358b0c02ffd530d78e133a1b73) Signed-off-by: Wentao Guan <guanwentao@uniontech.com> --- net/sched/act_pedit.c | 58 +++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 0601deea04d72..fe9e826184c16 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -182,26 +182,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, } parm = nla_data(pattr); - if (!parm->nkeys) { - NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed"); - return -EINVAL; - } - ksize = parm->nkeys * sizeof(struct tc_pedit_key); - if (nla_len(pattr) < sizeof(*parm) + ksize) { - NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid"); - return -EINVAL; - } - - 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); @@ -210,25 +190,49 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, &act_pedit_ops, bind, flags); if (ret) { tcf_idr_cleanup(tn, index); - goto out_free_ex; + return ret; } ret = ACT_P_CREATED; } else if (err > 0) { if (bind) - goto out_free; + return 0; if (!ovr) { ret = -EEXIST; goto out_release; } } else { - ret = err; - goto out_free_ex; + return err; + } + + if (!parm->nkeys) { + NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed"); + ret = -EINVAL; + goto out_release; + } + ksize = parm->nkeys * sizeof(struct tc_pedit_key); + if (nla_len(pattr) < sizeof(*parm) + ksize) { + NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid"); + ret = -EINVAL; + goto out_release; + } + + nparms = kzalloc(sizeof(*nparms), GFP_KERNEL); + if (!nparms) { + ret = -ENOMEM; + goto out_release; + } + + 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; } err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); if (err < 0) { ret = err; - goto out_release; + goto out_free_ex; } nparms->tcfp_flags = parm->flags; @@ -280,12 +284,12 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, put_chain: if (goto_ch) 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(nparms); +out_release: + tcf_idr_release(*a, bind); return ret; } -- 2.30.2 ^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v3 00/10] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan ` (9 preceding siblings ...) 2026-06-26 18:07 ` [PATCH 5.10.y v3 10/10] net/sched: act_pedit: fix action bind logic Wentao Guan @ 2026-06-27 16:35 ` Sasha Levin 10 siblings, 0 replies; 28+ messages in thread From: Sasha Levin @ 2026-06-27 16:35 UTC (permalink / raw) To: Wentao Guan Cc: Sasha Levin, 2045gemini, davem, dcaratti, gregkh, jhs, keenanat2000, kuba, pctammela, rajat.gupta, rollkingzzc, simon.horman, stable, toke, victor, yimingqian591 On Sat, 27 Jun 2026 02:07 +0800, Wentao Guan <guanwentao@uniontech.com> wrote: > [PATCH 5.10.y v3 00/10] net/sched: fix pedit partial COW leading to page cache corruption Applied the 10/10 patch on top of the already-queued series for 5.10. -- Thanks, Sasha ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption 2026-06-26 17:54 ` Sasha Levin 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan @ 2026-06-26 18:18 ` Wentao Guan 1 sibling, 0 replies; 28+ messages in thread From: Wentao Guan @ 2026-06-26 18:18 UTC (permalink / raw) To: Sasha Levin Cc: Sasha Levin, 2045gemini, dcaratti, Greg KH, jhs, keenanat2000, kuba, rajat.gupta, rollkingzzc, stable, toke, victor, yimingqian591, Pedro Tammela, Simon Horman, davem > pls add 10/9 backport (net/sched: act_pedit: fix action bind logic) > Thanks Wentao. The inline-pasted backport got mangled by mail reflow (the > @@ hunk counts no longer match the body), so it's not git-am'able, and a > plain cherry-pick of e9e42292ea76 doesn't apply on top of the queued v2 > series. Could you resend it as a standalone [PATCH 5.10.y] via > git send-email? Thanks, I send the whole series, you can pick the 10/10 on the top. BRs Wentao Guan ^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2026-06-27 16:35 UTC | newest] Thread overview: 28+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-18 7:53 [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 1/9] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 2/9] net/sched: transition act_pedit to rcu and percpu stats Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 3/9] net/sched: simplify tcf_pedit_act Wentao Guan 2026-06-18 7:53 ` [PATCH v5.10.y v2 4/9] net/sched: act_pedit: remove extra check for key type Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 5/9] net/sched: act_pedit: check static offsets a priori Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 6/9] net/sched: act_pedit: rate limit datapath messages Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 7/9] net/sched: act_pedit: Parse L3 Header for L4 offset Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y 8/9] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan 2026-06-18 7:53 ` [PATCH 5.10.y v2 9/9] net/sched: act_pedit: free pedit keys on bail from offset check Wentao Guan 2026-06-19 4:06 ` [PATCH 5.10.y v2 0/9] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin 2026-06-23 10:01 ` Wentao Guan 2026-06-25 10:41 ` Sasha Levin 2026-06-25 11:45 ` Wentao Guan 2026-06-26 17:54 ` Sasha Levin 2026-06-26 18:07 ` [PATCH 5.10.y v3 00/10] " Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 01/10] net/sched: act_pedit: use NLA_POLICY for parsing 'ex' keys Wentao Guan 2026-06-26 18:07 ` [PATCH 02/10] net/sched: transition act_pedit to rcu and percpu stats Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 03/10] net/sched: simplify tcf_pedit_act Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 04/10] net/sched: act_pedit: remove extra check for key type Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 05/10] net/sched: act_pedit: check static offsets a priori Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 06/10] net/sched: act_pedit: rate limit datapath messages Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 07/10] net/sched: act_pedit: Parse L3 Header for L4 offset Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 08/10] net/sched: fix pedit partial COW leading to page cache corruption Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 09/10] net/sched: act_pedit: free pedit keys on bail from offset check Wentao Guan 2026-06-26 18:07 ` [PATCH 5.10.y v3 10/10] net/sched: act_pedit: fix action bind logic Wentao Guan 2026-06-27 16:35 ` [PATCH 5.10.y v3 00/10] net/sched: fix pedit partial COW leading to page cache corruption Sasha Levin 2026-06-26 18:18 ` [PATCH 5.10.y v2 0/9] " Wentao Guan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).