Skip to content

[rlc-9/5.14.0-687.29.1.el9_8] net/sched: act_api: use RCU with deferred freeing for action lifecycle - #1489

Open
ciq-kernel-automation[bot] wants to merge 1 commit into
rlc-9/5.14.0-687.29.1.el9_8from
{bmastbergen}_rlc-9/5.14.0-687.29.1.el9_8
Open

[rlc-9/5.14.0-687.29.1.el9_8] net/sched: act_api: use RCU with deferred freeing for action lifecycle#1489
ciq-kernel-automation[bot] wants to merge 1 commit into
rlc-9/5.14.0-687.29.1.el9_8from
{bmastbergen}_rlc-9/5.14.0-687.29.1.el9_8

Conversation

@ciq-kernel-automation

Copy link
Copy Markdown

Summary

This PR has been automatically created after successful completion of all CI stages.

Commit Message(s)

net/sched: act_api: use RCU with deferred freeing for action lifecycle

cve CVE-2026-53264
commit-author Jamal Hadi Salim <jhs@mojatatu.com>
commit 5057e1aca011e51ef51498c940ef96f3d3e8a305
upstream-diff Upstream adds a struct rcu_head tcfa_rcu field to struct
  tc_action and uses kfree_rcu(p, tcfa_rcu). Since tc_action is embedded
  in every action type struct (e.g. tcf_gact, tcf_mirred, etc.), adding a
  field changes the layout of all those structs, breaking kABI. Instead,
  use kfree_rcu_mightsleep(p) which defers the free until after an RCU
  grace period without requiring an embedded rcu_head. All callers of
  free_tcf() are in process context, satisfying the might_sleep()
  requirement.

Test Results

✅ Build Stage

Architecture Build Time Total Time
x86_64 32m 27s 33m 25s
aarch64 19m 38s 20m 24s

✅ Boot Verification

✅ Kernel Selftests

Architecture Passed Failed Compared Against Status
x86_64 203 52 rlc-9/5.14.0-687.29.1.el9_8 ⚠️ No baseline available
aarch64 152 51 rlc-9/5.14.0-687.29.1.el9_8 ⚠️ No baseline available

✅ LTP Results

Architecture Passed Failed Compared Against Status
x86_64 1456 81 rlc-9/5.14.0-687.29.1.el9_8 ⚠️ No baseline available
aarch64 1429 82 rlc-9/5.14.0-687.29.1.el9_8 ⚠️ No baseline available

🤖 This PR was automatically generated by GitHub Actions
Run ID: 30463631707

cve CVE-2026-53264
commit-author Jamal Hadi Salim <jhs@mojatatu.com>
commit 5057e1a
upstream-diff Upstream adds a struct rcu_head tcfa_rcu field to struct
  tc_action and uses kfree_rcu(p, tcfa_rcu). Since tc_action is embedded
  in every action type struct (e.g. tcf_gact, tcf_mirred, etc.), adding a
  field changes the layout of all those structs, breaking kABI. Instead,
  use kfree_rcu_mightsleep(p) which defers the free until after an RCU
  grace period without requiring an embedded rcu_head. All callers of
  free_tcf() are in process context, satisfying the might_sleep()
  requirement.

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 d7fb60b ("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: d7fb60b ("net_sched: get rid of tcfa_rcu")
	Suggested-by: Jakub Kicinski <kuba@kernel.org>
	Reported-by: Kyle Zeng <kylebot@openai.com>
	Tested-by: Victor Nogueira <victor@mojatatu.com>
	Tested-by: syzbot@syzkaller.appspotmail.com
	Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
	Tested-by: Kyle Zeng <kylebot@openai.com>
	Reviewed-by: Pedro Tammela <pctammela@mojatatu.com>
	Reviewed-by: Eric Dumazet <edumazet@google.com>
	Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Link: https://patch.msgid.link/20260531160812.68020-1-jhs@mojatatu.com
	Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit 5057e1a)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
@ciq-kernel-automation ciq-kernel-automation Bot added the created-by-kernelci Tag PRs that were automatically created when a user branch was pushed to the repo (kernelCI) label Jul 29, 2026
@github-actions

Copy link
Copy Markdown

🤖 Validation Checks In Progress Workflow run: https://github.com/ctrliq/kernel-src-tree/actions/runs/30482718790

@github-actions

Copy link
Copy Markdown

🔍 Interdiff Analysis

  • ⚠️ PR commit a55f813bcdb (net/sched: act_api: use RCU with deferred freeing for action lifecycle) → upstream 5057e1aca011
    Differences found:
================================================================================
*    DELTA DIFFERENCES - code changes that differ between the patches          *
================================================================================

--- b/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -124,7 +124,7 @@
 	if (chain)
 		tcf_chain_put_by_act(chain);
 
-	kfree_rcu_mightsleep(p);
+	kfree_rcu(p, tcfa_rcu);
 }
 
 static void offload_action_hw_count_set(struct tc_action *act,

================================================================================
*    ONLY IN PATCH2 - files not modified by patch1                             *
================================================================================

--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -45,6 +45,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;

This is an automated interdiff check for backported commits.

@github-actions

Copy link
Copy Markdown

Validation checks completed successfully View full results: https://github.com/ctrliq/kernel-src-tree/actions/runs/30482718790

@kerneltoast kerneltoast left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling synchronize_rcu() under the hood instead of call_rcu() (by using kfree_rcu_sleepable()) is a valid solution and looks fine since it's not called from a hot path where that heavy sleep could hurt performance.

🚢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by-kernelci Tag PRs that were automatically created when a user branch was pushed to the repo (kernelCI)

Development

Successfully merging this pull request may close these issues.

2 participants