From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pf1-f170.google.com (mail-pf1-f170.google.com [209.85.210.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FD1B4156EC for ; Wed, 5 Aug 2026 09:55:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.170 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785923737; cv=none; b=k+4pHeWqLATIzWsQC2kSAx9QltLQ/eWzBRyezmJT1k9+QHRjQm4R/zbAQREQu9Vdi+YSIPY+wOY+O3yRYJzXGhHzCcvpJIJ85mUUaopEbCecuRRE/EkZLz08N4njyf//A83E4GyEsir6uq4Aa9jHtZ5+vLjPx5nLlxHUhgEEQNg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785923737; c=relaxed/simple; bh=laFDajcbsTqcT6ZcbK37Ex1BDVWiUC0e7NghyynrKYo=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=HUBpCo0HOqYV6Yb4QMq6K3s/2VButa0WJbtrg3jx2Nv5zFMG7GwAMn/kzNoLsPJO24bTyHUHddYxk5aG/jkHlKSqradKGYVYpjyvHStYojaVzi7nUl6P1hzycFeOCDxZPqF57boKcisqu7SxJ9UvLUsSly0s0EnIjJndjfgOqi0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IBHKZE7W; arc=none smtp.client-ip=209.85.210.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IBHKZE7W" Received: by mail-pf1-f170.google.com with SMTP id d2e1a72fcca58-84536ecfc5bso935130b3a.2 for ; Wed, 05 Aug 2026 02:55:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785923735; x=1786528535; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=fkkkeE2AhcVlDMOIzDVMCAo0rWWKAmZ5F7iC5BAAoSk=; b=IBHKZE7WpBtkAdHl20jF9LUUG+uPFNT6tnMI/BEZaQPLEJtMs7JZJmC0RSLj6ytoAL lcqYOJJBl9xgDWQqMNNtLcGRlasHcqUHV3ckefSah50bETPe1HA5DLDUErjteVO1xJ0E z5CX23kpK09j+qf/qbfH/fmxRkE6XApdm/YyGE4MmJnYtNMUYsRiLJmLmguB33BICugA LHiFoLYGMaTtlWaPxQcS9DF96rZ87L+0xnc6itIJD94y9tb3ZcUdGhwFAyQ52P1eY/5m 6g9fo7S5q1wk42772Z/W10vrbqcXlhc3qvfxR5eViIUCzI9zZHEzl3+9TNHGSWMWUyTN kC8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785923735; x=1786528535; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=fkkkeE2AhcVlDMOIzDVMCAo0rWWKAmZ5F7iC5BAAoSk=; b=KcApfmJJZc7QLP3xocw+Kd85QFnStUosaYhtMT22eeQYOsorvWDcluzqnDoUJ5erWr XUiZiilQKNQ07O01xsxqsjfe/3EWCqksv0VE8JK/bR0LNa9jjyOeBPyL/e/sKBftykNU +0qkw8RCmYz2fNU6cRcn7d3TduLt2pwOB5TZ2dlSMtjPh9jCW+0VDfvK2ZlNcIrhwCuy rxevSVRAr9md3gD3FykmqNtDculN9zRqHfA5irqPi0cDQx6j1FhR8W8+3qU3ID281m3y xdRd4d6t3Bo5bjoqiTm0cJIBj4eOQBzKxeSR8Q7dWVwxLwKViBkXMJjI7DULOyFoRoLq l1Lw== X-Forwarded-Encrypted: i=1; AHgh+RphgesvOHmJGxGOYke0fLhJR/4mBkBa7SP2WvA/qZLPgPr2sAgR2cy0MMrKfRMLArHkFPTOUA8=@vger.kernel.org X-Gm-Message-State: AOJu0YzrnzCL6suQfIx4i7P9Yzb/Od6a9MhOHkcYQUAxLtFzbIdMa3HE Rrhm2FCGwvqDyTzWaLb6okMDMKSiHL/odat0duWSPoUKCGMD3mqBHTND X-Gm-Gg: AR+sD11H5nlUEljTgrt9PJ1FWc4Is6dB4KgP0YwOFMfezRknUWWY5MwYvmplLkTef3M XSIQCPBR6od5976/CiRYfSW1CS/P4J1ANMcCNx94i/1WmqZYcYICN11A3oBZM0tLs1DslDZmaHM s3WCls2AH9tnUQQSyYpL+6TgSKf0++RmozA7+m8sy7fOCCNM3WhmL12h3n1/11x0BpRPM6GIQda ZB6lzwKq2PBPcACN23/nwyC8YzejeAQnhOrB4x7UdBomG1WFCVbBhiG6XX5EMFZuQyJkLLN2BYh tHVXHOkcwreBIc+0p2tSQWa1kZPTOq3Tt8UPq+t8CkfHQjNnPhdEm3AK8I2qG4IwKhoOTz8ZqXp DXrmSo4MKT+Hf6Abzmp7sio9k0wTwTkdxTQ8cicRAmSzDpjlzLyn3b4nT1svAjQBIWfMmwk8mc3 6FLOfSsMCq0Rvr24+IcC//4FMmKuu41TshaVVrtcHYPahfMAHuu/oZlhOJfaiEv+ieciisqcabL a8vLZW45X4SonJNRxx299cHYA89K5cQNWJpEI4= X-Received: by 2002:a05:6a00:b93:b0:842:5da3:9b89 with SMTP id d2e1a72fcca58-84f2e13a50cmr5858638b3a.38.1785923734663; Wed, 05 Aug 2026 02:55:34 -0700 (PDT) Received: from BOOK-P74QMIQ7E8.localdomain ([163.239.22.31]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cbe708be545sm1189839a12.16.2026.08.05.02.55.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Aug 2026 02:55:34 -0700 (PDT) From: hyunjungg To: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jamal Hadi Salim , Jiri Pirko , John Hurley , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Hyunjung Ko , stable@vger.kernel.org Subject: [PATCH net] net/sched: act_gact, act_police: range check the fallback control action Date: Wed, 5 Aug 2026 18:55:27 +0900 Message-Id: <20260805095527.204088-1-hj351016@gmail.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Hyunjung Ko tcf_action_check_ctrlact() range checks the primary control action: if (!opcode) ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0; TC_ACT_VALUE_MAX is TC_ACT_TRAP, so kernel-internal verdicts above it cannot be set that way. But act_gact and act_police each carry a second, independent control action supplied by user space that never reaches that helper - TCA_GACT_PROB.paction and TCA_POLICE_RESULT. Both only reject TC_ACT_GOTO_CHAIN, so any other value is stored verbatim and returned verbatim from the action. In particular user space can store TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is deliberately not part of the UAPI value range. That verdict tells every caller the action took ownership of the skb, so nobody frees it: sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for it. The result is one leaked sk_buff plus its data buffer per packet traversing the filter, unbounded, for all traffic on the chain including kernel-generated packets. Both are trivially deterministic. act_gact clamps tcfg_pval to >= 1, so with pval = 1 gact_determ() returns the fallback for every packet. act_police has no mandatory rate, so rate = 0 leaves tcfp_mtu = ~0 and tcf_police_mtu_check() always passes. TC_ACT_CONSUMED was added by commit 720f22fed81b, after both goto-chain guards were written (9469f375ab09 and c08f5ed5d625, Oct 2018); neither guard was widened when the new verdict appeared. Factor the existing range test out of tcf_action_check_ctrlact() as tcf_action_valid() and apply it to both fallbacks. The helper cannot call tcf_action_check_ctrlact() directly because that also allocates a goto_chain, which is exactly what these two sites must not do. Reproduced on v7.2-rc6: kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet. With this patch both configurations are rejected with -EINVAL and kmemleak reports none. Fixes: 720f22fed81b ("net: sched: refactor reinsert action") Cc: stable@vger.kernel.org # v5.3+ Signed-off-by: Hyunjung Ko --- include/net/act_api.h | 19 +++++++++++++++++++ net/sched/act_gact.c | 5 +++++ net/sched/act_police.c | 6 ++++++ 3 files changed, 30 insertions(+) Reproducer needs CONFIG_NET_ACT_GACT + CONFIG_GACT_PROB and CONFIG_NET_ACT_POLICE, plus CONFIG_DEBUG_KMEMLEAK and kmemleak=on to observe it. The bad value cannot be set with tc(8) - iproute2 only parses symbolic action names - so the fallback has to be planted over raw netlink: TCA_GACT_PROB.paction = 9 with ptype = PGACT_DETERM and pval = 1, or TCA_POLICE_RESULT = 9 with rate = 0. Attach either to a clsact ingress chain and every packet leaks its skb. Before, one sk_buff plus its data buffer per packet: kmemleak: 166 new suspected memory leaks unreferenced object 0xffff888103baadc0 (size 232): kmem_cache_alloc_node_noprof+0x2f1/0x3e0 __alloc_skb+0xe5/0x860 alloc_skb_with_frags+0x82/0x750 sock_alloc_send_pskb+0x658/0x7e0 packet_sendmsg+0x1833/0x4860 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0x102/0x5a0 After: both configurations are rejected at netlink time with -EINVAL and "invalid fallback control action", and kmemleak reports no unreferenced objects. For the same reason tdc cannot express the bad configuration, so no selftest accompanies this patch. A self-contained C reproducer is available on request. diff --git a/include/net/act_api.h b/include/net/act_api.h index 20d9e55f8564..fd03f6319e88 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp, struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, struct tcf_chain *newchain); +/* Range check for a control action supplied by user space. + * + * This is the same test tcf_action_check_ctrlact() applies to the primary + * control action, factored out for the *fallback* control actions + * (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT), + * which must not reach tcf_action_check_ctrlact() because they have no + * goto_chain to allocate. Without it, user space can store kernel-internal + * verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is + * deliberately not part of the UAPI value range. + */ +static inline bool tcf_action_valid(int action) +{ + int opcode = TC_ACT_EXT_OPCODE(action); + + if (!opcode) + return action <= TC_ACT_VALUE_MAX; + return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC; +} + #ifdef CONFIG_INET DECLARE_STATIC_KEY_FALSE(tcf_frag_xmit_count); #endif diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index e949280eb800..565860cccba6 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, p_parm = nla_data(tb[TCA_GACT_PROB]); if (p_parm->ptype >= MAX_RAND) return -EINVAL; + if (!tcf_action_valid(p_parm->paction)) { + NL_SET_ERR_MSG(extack, + "invalid fallback control action"); + return -EINVAL; + } if (TC_ACT_EXT_CMP(p_parm->paction, TC_ACT_GOTO_CHAIN)) { NL_SET_ERR_MSG(extack, "goto chain not allowed on fallback"); diff --git a/net/sched/act_police.c b/net/sched/act_police.c index b16468a98c55..ce08f6840ef7 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -128,6 +128,12 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, if (tb[TCA_POLICE_RESULT]) { tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]); + if (!tcf_action_valid(tcfp_result)) { + NL_SET_ERR_MSG(extack, + "invalid fallback control action"); + err = -EINVAL; + goto failure; + } if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) { NL_SET_ERR_MSG(extack, "goto chain not allowed on fallback"); -- 2.43.0