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 B228535292A; Tue, 16 Jun 2026 18:17:30 +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=1781633851; cv=none; b=O8+1kHw4qq1qg9a7ozy3s3KLOTWjErx8e2MUvF7thNBsyGR4yo4bqvcVbY0sXHg2om8LHCoxXuXSTep3+hJUQaHobgo1wFNsqTInWueetkqfmzLepC/meqEkXBPudEV3KSZQtbcEV46xZidvRL0yvoNuDBDwJztKFVJ9x7917sc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781633851; c=relaxed/simple; bh=2oVl+e5LxNhBR/b5c9jymNApGUVaGBRoCzJ0xp5+qKQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Us5MyTdpjyyMb1KGAcvc/a72jfNB/JnuYmmdIdwqpTwDFTXzQZxebgGLdKZ6+IHDVBMFwq2UunlSaCuuPIX8GVznAclXvybnh7WXSOHLFXCIBsdP4IPRErixqLTmXQsP4sLV4Vcq5JqfR5BOEVHBxchBrK86zN8tnDqw4Xw3600= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=qYzDCk7U; 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="qYzDCk7U" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6CD3D1F000E9; Tue, 16 Jun 2026 18:17:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1781633850; bh=4Q5bwEfXGBF3uCkns9KxhjMR8yQs1TzWZrEiVV7ZUIo=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=qYzDCk7UH0o50t7N8GdUce0JP5EJcm5mpMxK7m3FMuhuMd+Xpm2P6Z8IpP9l4uSch Wy2Y9fisgzb86To8Zpadq0ZCe7wbMJ3acCZB89ewPLaNqrlyKKmrr8eIRdnJIZ3shl fBKckhYN9SA4cGsQS1JT8iqPi1L+vDgcR3f/4oAw= 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 5.15 156/411] net/sched: act_api: use RCU with deferred freeing for action lifecycle Date: Tue, 16 Jun 2026 20:26:34 +0530 Message-ID: <20260616145108.763242870@linuxfoundation.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260616145100.376842714@linuxfoundation.org> References: <20260616145100.376842714@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 5.15-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 ab67d62ff31784..29d5dc201b5cc5 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -41,6 +41,7 @@ struct tc_action { struct tc_cookie __rcu *act_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 0b4deb33bdf7ad..20944a2e162e7f 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -109,11 +109,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); @@ -126,7 +121,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 tcf_action_cleanup(struct tc_action *p) -- 2.53.0