]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net/sched: Only allow act_ct to bind to clsact/ingress qdiscs and shared blocks
authorVictor Nogueira <victor@mojatatu.com>
Wed, 25 Feb 2026 13:43:48 +0000 (10:43 -0300)
committerJakub Kicinski <kuba@kernel.org>
Sat, 28 Feb 2026 03:06:21 +0000 (19:06 -0800)
As Paolo said earlier [1]:

"Since the blamed commit below, classify can return TC_ACT_CONSUMED while
the current skb being held by the defragmentation engine. As reported by
GangMin Kim, if such packet is that may cause a UaF when the defrag engine
later on tries to tuch again such packet."

act_ct was never meant to be used in the egress path, however some users
are attaching it to egress today [2]. Attempting to reach a middle
ground, we noticed that, while most qdiscs are not handling
TC_ACT_CONSUMED, clsact/ingress qdiscs are. With that in mind, we
address the issue by only allowing act_ct to bind to clsact/ingress
qdiscs and shared blocks. That way it's still possible to attach act_ct to
egress (albeit only with clsact).

[1] https://lore.kernel.org/netdev/674b8cbfc385c6f37fb29a1de08d8fe5c2b0fbee.1771321118.git.pabeni@redhat.com/
[2] https://lore.kernel.org/netdev/cc6bfb4a-4a2b-42d8-b9ce-7ef6644fb22b@ovn.org/

Reported-by: GangMin Kim <km.kim1503@gmail.com>
Fixes: 3f14b377d01d ("net/sched: act_ct: fix skb leak and crash on ooo frags")
CC: stable@vger.kernel.org
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Link: https://patch.msgid.link/20260225134349.1287037-1-victor@mojatatu.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/act_api.h
net/sched/act_ct.c
net/sched/cls_api.c

index e1e8f0f7dacb6b11591021949277c1378c39dafc..d11b791079302f50c47e174979767e0b24afc59a 100644 (file)
@@ -70,6 +70,7 @@ struct tc_action {
 #define TCA_ACT_FLAGS_REPLACE  (1U << (TCA_ACT_FLAGS_USER_BITS + 2))
 #define TCA_ACT_FLAGS_NO_RTNL  (1U << (TCA_ACT_FLAGS_USER_BITS + 3))
 #define TCA_ACT_FLAGS_AT_INGRESS       (1U << (TCA_ACT_FLAGS_USER_BITS + 4))
+#define TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT     (1U << (TCA_ACT_FLAGS_USER_BITS + 5))
 
 /* Update lastuse only if needed, to avoid dirtying a cache line.
  * We use a temp variable to avoid fetching jiffies twice.
index bd51522c7953c5b4f2b9febd56059027f93857a3..7d5e50c921a07df0ad71aaa50aa4b02242a7592f 100644 (file)
@@ -1360,6 +1360,12 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla,
                return -EINVAL;
        }
 
+       if (bind && !(flags & TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT)) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Attaching ct to a non ingress/clsact qdisc is unsupported");
+               return -EOPNOTSUPP;
+       }
+
        err = nla_parse_nested(tb, TCA_CT_MAX, nla, ct_policy, extack);
        if (err < 0)
                return err;
index 343309e9009b0b3705a42ef8c4a4e9c8c99b6016..4829c27446e3369ad2ae9b3fcb285eca47d59933 100644 (file)
@@ -2228,6 +2228,11 @@ static bool is_qdisc_ingress(__u32 classid)
        return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS));
 }
 
+static bool is_ingress_or_clsact(struct tcf_block *block, struct Qdisc *q)
+{
+       return tcf_block_shared(block) || (q && !!(q->flags & TCQ_F_INGRESS));
+}
+
 static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
                          struct netlink_ext_ack *extack)
 {
@@ -2420,6 +2425,8 @@ replay:
                flags |= TCA_ACT_FLAGS_NO_RTNL;
        if (is_qdisc_ingress(parent))
                flags |= TCA_ACT_FLAGS_AT_INGRESS;
+       if (is_ingress_or_clsact(block, q))
+               flags |= TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT;
        err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
                              flags, extack);
        if (err == 0) {