From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBA8943E4BA; Tue, 16 Jun 2026 16:17:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781626648; cv=none; b=iR2BXJd4K4jMS/Ol6zSiOM5UMOC3lnuKm9IMlgs8IaQzHffePawf6yNHFGZU1Vkf1nPO6vqdzbshl8Td6QJNyHnV0zKC6G0vWDSGtMAX3tvCH3D74RZqwdUNiNxkzA8TRG0aUJF2npVNLP33jV7rLs9Rhr4k0YzgKIsW0iP/yG8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781626648; c=relaxed/simple; bh=svf2PJH1Pv6Wv50Jup4n5U0W8cAtdTNsMKlxRMEPXnE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eobKk4Akiz0qr4aFDSqkVLQTsQo5K+DeBj5uVSaAIUEgOdbCgohCIKLdpIPmzWtK9CqsrfW9WjI+z4Kd0sw7LMVNiS8ndKNPaOniowtwpJTLr2cwb7Bx3QyoLWe98c01PxzpyRGpneNuL4LgOfXXUI5Eg84rCEnYhPyE9miFEcw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=hbeNIQD7; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="hbeNIQD7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C1DD41F00A3A; Tue, 16 Jun 2026 16:17:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1781626647; bh=yZvGPoIkkI2g83IsKQMYqmo3TH99SejGPAV1UfBWFag=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=hbeNIQD71l1eaZB8sVwMtyvV/oEpT7E8XnBR2FBbvCYMGStvYnEwj0A8msYJ/RRpA BQBsiNYxDNcb7+YJKV6HcwM/Z57YsGeI+k0aEjZK1p+o8prMMEqn4dxFrn/473+5ya sMtnSk2zOEwHtkN8lae9WIqCjo0Sqy7kMkczDa+w= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Jakub Kicinski , Kyle Zeng , Victor Nogueira , syzbot@syzkaller.appspotmail.com, Jamal Hadi Salim , Pedro Tammela , Eric Dumazet , Sasha Levin Subject: [PATCH 6.12 025/261] net/sched: act_api: use RCU with deferred freeing for action lifecycle Date: Tue, 16 Jun 2026 20:27:43 +0530 Message-ID: <20260616145046.147747328@linuxfoundation.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260616145044.869532709@linuxfoundation.org> References: <20260616145044.869532709@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.12-stable review patch. If anyone has any objections, please let me know. ------------------ From: Jamal Hadi Salim [ Upstream commit 5057e1aca011e51ef51498c940ef96f3d3e8a305 ] When NEWTFILTER and DELFILTER are run concurrently it is possible to create a race with an associated action. Let's illustrate with CPU0 running NEWTFILTER and CPU1 running DELFILTER: 0: mutex_lock() <-- holds the idr lock 0: rcu_read_lock() 0: p = idr_find(idr, index) <-- action p is valid (RCU protects IDR) 0: mutex_unlock() <-- releases the idr lock 1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held 1: idr_remove(idr, index) <-- Action removed from IDR 1: mutex_unlock() <-- mutex released allowing us to delete the action 1: tcf_action_cleanup(p); kfree(p) <-- Kfrees p immediately, no deferral 0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- ouch, UAF p points to freed memory This patch fixes the race condition between NEWTFILTER and DELFILTER by adding struct rcu_head to tc_action used in the deferral and introducing a call_rcu() in the delete path to defer the final kfree(). Note: this is a revert of commit d7fb60b9cafb ("net_sched: get rid of tcfa_rcu") but also modernization/simplification to directly use kfree_rcu(). Let's illustrate the new restored code path: 0: rcu_read_lock() 1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held 1: idr_remove(idr, index) 1: mutex_unlock() 1: call_rcu(&p->tcfa_rcu, tcf_action_rcu_free) <-- defer kfree after grace period 0: p = idr_find(idr, index) 0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- fails, refcnt already 0 1: rcu_read_unlock() <-- release so freeing can run after grace period After CPU1 calls idr_remove(), the object is no longer reachable through the IDR. CPU0's subsequent idr_find() will return NULL, and even if it still held a stale pointer, the immediate kfree() is now deferred until after the RCU grace period, so no UAF can occur. Fixes: d7fb60b9cafb ("net_sched: get rid of tcfa_rcu") Suggested-by: Jakub Kicinski Reported-by: Kyle Zeng Tested-by: Victor Nogueira Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Jamal Hadi Salim Tested-by: Kyle Zeng Reviewed-by: Pedro Tammela Reviewed-by: Eric Dumazet Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260531160812.68020-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/act_api.h | 1 + net/sched/act_api.c | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index d8103b2270d98f..539ea6693a2470 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -42,6 +42,7 @@ struct tc_action { struct tc_cookie __rcu *user_cookie; struct tcf_chain __rcu *goto_chain; u32 tcfa_flags; + struct rcu_head tcfa_rcu; u8 hw_stats; u8 used_hw_stats; bool used_hw_stats_valid; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index eecad65fec92ca..7d903f0607439d 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -112,11 +112,6 @@ struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, } EXPORT_SYMBOL(tcf_action_set_ctrlact); -/* XXX: For standalone actions, we don't need a RCU grace period either, because - * actions are always connected to filters and filters are already destroyed in - * RCU callbacks, so after a RCU grace period actions are already disconnected - * from filters. Readers later can not find us. - */ static void free_tcf(struct tc_action *p) { struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1); @@ -129,7 +124,7 @@ static void free_tcf(struct tc_action *p) if (chain) tcf_chain_put_by_act(chain); - kfree(p); + kfree_rcu(p, tcfa_rcu); } static void offload_action_hw_count_set(struct tc_action *act, -- 2.53.0