[Devel] [PATCH RHEL9 COMMIT] cgroup: close a race window on cgrp removal

Konstantin Khorenko khorenko at virtuozzo.com
Mon Dec 27 20:55:28 MSK 2021


The commit is pushed to "branch-rh9-5.14.vz9.1.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh9-5.14.0-4.vz9.10.37
------>
commit 5810f157c9ad66b6a453ed95870842935aa3ab21
Author: Nikita Yushchenko <nikita.yushchenko at virtuozzo.com>
Date:   Mon Dec 27 20:55:28 2021 +0300

    cgroup: close a race window on cgrp removal
    
    Cgroup removal via rmdir() can race with preparation to call release
    agent for the same cgroup (triggered e.g. by a recent exit of the last
    cgroup's task). Although quite unlikely, that can result into cgrp
    object reaching kfree() while it is still being used by
    cgroup1_release_agent().
    
    This scenario is a possible reason for use-after-free caught by KASAN:
    
        BUG: KASAN: use-after-free in cgroup_path_ns_locked+0xcd/0x110
        Read of size 8 at addr ffff888134b102b8 by task kworker/u8:0/769807
    ...
        dump_stack_lvl+0x56/0x7b
        print_address_description.constprop.8+0x21/0x150
        ? cgroup_path_ns_locked+0xcd/0x110
        ? cgroup_path_ns_locked+0xcd/0x110
        kasan_report.cold.14+0x7f/0x11b
        ? do_raw_spin_lock+0x250/0x290
        ? cgroup_path_ns_locked+0xcd/0x110
        cgroup_path_ns_locked+0xcd/0x110
        cgroup_path_ns+0x42/0x70
        cgroup1_release_agent+0x27e/0x680
    ...
    
    Close the race window by taking an extra reference to cgrp for the time
    while cgroup1_release_agent() uses it.
    
    https://jira.sw.ru/browse/PSBM-136884
    Fixes: 94b2fa5aa580 ("ve/cgroup: Move release_agent from system_wq to per-ve
    workqueues")
    
    Signed-off-by: Nikita Yushchenko <nikita.yushchenko at virtuozzo.com>
---
 kernel/cgroup/cgroup-v1.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index d58faf071e2c..443c5d02d3f4 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -875,6 +875,18 @@ void cgroup1_release_agent(struct work_struct *work)
 				  struct cgroup,
 				  release_list);
 		list_del_init(&cgrp->release_list);
+
+		/*
+		 * Once the lock gets released, concurrently running removal
+		 * of the same cgrp via rmdir() won't find the cgrp in
+		 * ve_rm_from_release_list() called from cgroup_destroy_locked()
+		 * and can progress up to end and put the last cgrp reference.
+		 * This can result into cgrp reaching kfree() before this thread
+		 * stops using it.
+		 */
+		if (WARN_ON(!cgroup_tryget(cgrp)))
+			continue;
+
 		spin_unlock_irqrestore(&ve->release_list_lock, flags);
 
 		rcu_read_lock();
@@ -907,6 +919,7 @@ void cgroup1_release_agent(struct work_struct *work)
 					    "%s %s failed: %d\n",
 					    agentbuf, pathbuf, ret);
 continue_locked:
+		cgroup_put(cgrp);
 		spin_lock_irqsave(&ve->release_list_lock, flags);
 	}
 	spin_unlock_irqrestore(&ve->release_list_lock, flags);


More information about the Devel mailing list