From 60686a33c3030e8eb60cd1d10ac44bc8987f3627 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 29 May 2026 11:23:23 +0100 Subject: [PATCH] udev: run workers in sibling cgroup and use cgroup.kill Since a1f4fd387603673a79a84ca4e5ce25b439b85fe6 udev processes run in an 'udev' subcgroup, to avoid killing control processes when clearing workers. But the main process is still in the same cgroup, so the atomic cgroup.kill cannot be used. If the main subcgroup exists, try to create a sibling 'workers' cgroup, and use it for workers processes, and use cgroup.kill if available. This is especially useful as rules can spawn arbitrary programs/scripts and TasksMax= is set to unlimited. --- src/udev/udev-manager.c | 41 +++++++++++++++++++++++++++++++++++------ src/udev/udev-manager.h | 2 +- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 44d2ee6400..ba23882a91 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -6,6 +6,7 @@ #include "sd-varlink.h" +#include "cgroup-setup.h" #include "cgroup-util.h" #include "common-signal.h" #include "daemon-util.h" @@ -21,6 +22,7 @@ #include "io-util.h" #include "list.h" #include "notify-recv.h" +#include "path-util.h" #include "pidref.h" #include "prioq.h" #include "process-util.h" @@ -226,7 +228,7 @@ Manager* manager_free(Manager *manager) { sd_event_source_unref(manager->kill_workers_event); sd_event_unref(manager->event); - free(manager->cgroup); + free(manager->workers_cgroup); return mfree(manager); } @@ -586,6 +588,14 @@ static int worker_spawn(Manager *manager, Event *event) { .manager_pid = manager_pid, }; + if (manager->workers_cgroup) { + r = cg_attach(manager->workers_cgroup, /* pid= */ 0); + if (r < 0) { + log_error_errno(r, "Failed to move worker into cgroup '%s': %m", manager->workers_cgroup); + _exit(EXIT_FAILURE); + } + } + if (setenv("NOTIFY_SOCKET", manager->worker_notify_socket_path, /* overwrite= */ true) < 0) { log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %m"); _exit(EXIT_FAILURE); @@ -1350,9 +1360,10 @@ static int on_post(sd_event_source *s, void *userdata) { if (!hashmap_isempty(manager->workers)) return 0; /* There still exist idle workers. */ - if (manager->cgroup && set_isempty(manager->synthesize_change_child_event_sources)) - /* cleanup possible left-over processes in our cgroup */ - (void) cg_kill(manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, /* killed_pids= */ NULL, /* log_kill= */ NULL, /* userdata= */ NULL); + if (manager->workers_cgroup && set_isempty(manager->synthesize_change_child_event_sources)) + /* cleanup possible left-over processes in the workers cgroup */ + if (cg_kill_kernel_sigkill(manager->workers_cgroup) == -EOPNOTSUPP) + (void) cg_kill(manager->workers_cgroup, SIGKILL, CGROUP_IGNORE_SELF, /* killed_pids= */ NULL, /* log_kill= */ NULL, /* userdata= */ NULL); return 0; } @@ -1480,12 +1491,30 @@ int manager_main(Manager *manager) { assert(manager); _cleanup_free_ char *cgroup = NULL; - r = cg_pid_get_path(0, &cgroup); + r = cg_pid_get_path(/* pid= */ 0, &cgroup); if (r < 0) log_debug_errno(r, "Failed to get cgroup, ignoring: %m"); else if (endswith(cgroup, "/udev")) { /* If we are in a subcgroup /udev/ we assume it was delegated to us */ log_debug("Running in delegated subcgroup '%s'.", cgroup); - manager->cgroup = TAKE_PTR(cgroup); + + /* Try to create a sibling 'workers' cgroup and spawn all workers inside it, so that we can + * use cgroup.kill to atomically clear all workers. */ + _cleanup_free_ char *workers_cgroup = NULL; + r = path_extract_directory(cgroup, &workers_cgroup); + if (r < 0) + return log_warning_errno(r, "Failed to extract parent of cgroup '%s': %m", cgroup); + + if (!path_extend(&workers_cgroup, "workers")) + return log_oom(); + + r = cg_create(workers_cgroup); + if (r < 0) + log_warning_errno(r, "Failed to create workers cgroup '%s', ignoring: %m", workers_cgroup); + else { + log_debug("Running workers in delegated subcgroup '%s'.", workers_cgroup); + manager->workers_cgroup = TAKE_PTR(workers_cgroup); + } + } r = manager_setup_event(manager); diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index 2eaaf5b0c3..c1b98c52d1 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -39,7 +39,7 @@ typedef struct Manager { Hashmap *workers; LIST_HEAD(Event, events); Event *last_event; - char *cgroup; + char *workers_cgroup; UdevRules *rules; Hashmap *properties;