Skip to content

[rlc-10/6.12.0-211.37.1.el10_2] net/sched: act_api: use RCU with deferred freeing for action lifecycle - #1490

Open
ciq-kernel-automation[bot] wants to merge 1 commit into
rlc-10/6.12.0-211.37.1.el10_2from
{bmastbergen}_rlc-10/6.12.0-211.37.1.el10_2
Open

[rlc-10/6.12.0-211.37.1.el10_2] net/sched: act_api: use RCU with deferred freeing for action lifecycle#1490
ciq-kernel-automation[bot] wants to merge 1 commit into
rlc-10/6.12.0-211.37.1.el10_2from
{bmastbergen}_rlc-10/6.12.0-211.37.1.el10_2

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 27m 22s 28m 8s
aarch64 24m 6s 24m 44s

✅ Boot Verification

✅ Kernel Selftests

Architecture Passed Failed Compared Against Status
x86_64 424 68 rlc-10/6.12.0-211.37.1.el10_2 ⚠️ No baseline available
aarch64 373 62 rlc-10/6.12.0-211.37.1.el10_2 ⚠️ No baseline available

✅ LTP Results

Architecture Passed Failed Compared Against Status
x86_64 1478 80 rlc-10/6.12.0-211.37.1.el10_2 ⚠️ No baseline available
aarch64 1451 81 rlc-10/6.12.0-211.37.1.el10_2 ⚠️ No baseline available

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

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 30, 2026
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.

1 participant