From c83cb5a2d2ff02bb42f1db4a559ccf4d432b0419 Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Wed, 31 Aug 2022 15:03:56 -0600 Subject: [PATCH] src/api.c: support /proc mounted with subset=pid /proc filesystem can be mounted with subset=pid as one of its mount options. This option hides all the top-level files and directories, those are not related to processes. The cgroup v1 filesystem depends on the /proc/cgroups to populate the cgroups controllers and will fail during the cgroup_init() phase, when not available, whereas cgroup v2 considers this as a deprecated file and recommends reading the list of controller from /cgroup.controllers[1]. Support this valid /proc mount point only when the system is booted with the unified mode and will fail to initialize in the case we find the cgroup v1 mounted, i.e, the system booted with legacy or hybrid mode. [1] https://docs.kernel.org/admin-guide/cgroup-v2.html#deprecated-v1-core-features Fixes: https://github.com/libcgroup/libcgroup/issues/234 Reported-by: @0n-s (github username) Signed-off-by: Kamalesh Babulal Signed-off-by: Tom Hromatka --- src/api.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/src/api.c b/src/api.c index 4cda82c8..51190ad4 100644 --- a/src/api.c +++ b/src/api.c @@ -1330,6 +1330,73 @@ static void cgroup_free_cg_mount_table(void) memset(&cg_cgroup_v2_empty_mount_paths, 0, sizeof(cg_cgroup_v2_empty_mount_paths)); } +/* + * Parses the mount options of the given mount point and checks for the + * option in the list of mount options and sets is_set accordingly. + * @mnt: Mount point name to search for mount points. + * @mnt_opt: Mount option to be searched. + * @is_set: Set to 1, when mount option is found, 0 otherwise. + * + * Returns 0, in case of success and ECGOTHER on failure. + */ +static int check_mount_point_opt(const char *mnt, const char *mnt_opt, int * const is_set) +{ + struct mntent *ent, *temp_ent = NULL; + char mntent_buffer[4 * FILENAME_MAX]; + char *mntopt = NULL; + char mnt_opt_delim; + FILE *proc_mount; + int ret = 0; + + if (!mnt || !mnt_opt || !is_set) + return ECGINVAL; + + proc_mount = setmntent(mnt, "r"); + if (!proc_mount) { + cgroup_err("cannot open %s: %s\n", mnt, strerror(errno)); + last_errno = errno; + ret = ECGOTHER; + goto err; + } + + temp_ent = (struct mntent *) malloc(sizeof(struct mntent)); + if (!temp_ent) { + last_errno = errno; + ret = ECGOTHER; + goto err; + } + + ent = getmntent_r(proc_mount, temp_ent, mntent_buffer, sizeof(mntent_buffer)); + if (!ent) { + last_errno = errno; + ret = ECGOTHER; + goto err; + } + + *is_set = 0; + while((mntopt = hasmntopt(ent, mnt_opt))) { + mnt_opt_delim = mntopt[strlen(mnt_opt)]; + if (mnt_opt_delim == '\0' || mnt_opt_delim == ',') { + *is_set = 1; + break; + } + } + + if (*is_set == 0) { + cgroup_dbg("%s, not found in the list of ", mnt_opt); + cgroup_cont("mount options of %s\n", mnt); + } + +err: + if (proc_mount) + endmntent(proc_mount); + + if (temp_ent) + free(temp_ent); + + return ret; +} + /* * Reads /proc/cgroups and populates the controllers/subsys_name. This * function should be called with cg_mount_table_lock taken. @@ -1338,16 +1405,26 @@ static int cgroup_populate_controllers(char *controllers[CG_CONTROLLER_MAX]) { int hierarchy, num_cgroups, enabled; char subsys_name[FILENAME_MAX]; + char mnt_opt[] = "subset=pid"; FILE *proc_cgroup; char *buf = NULL; + int mnt_opt_set; int ret = 0; - int i, err; + int err, i = 0; proc_cgroup = fopen("/proc/cgroups", "re"); if (!proc_cgroup) { - cgroup_err("cannot open /proc/cgroups: %s\n", strerror(errno)); - last_errno = errno; - ret = ECGOTHER; + cgroup_warn("cannot open /proc/cgroups: %s\n", strerror(errno)); + ret = check_mount_point_opt("/proc/self/mounts", mnt_opt, &mnt_opt_set); + if (ret) + goto err; + + if (!mnt_opt_set) + ret = ECGINVAL; + /* + * /proc, mounted with subset=pid is valid. cgroup v2 doesn't + * depend on /proc/cgroups to parse the available controllers. + */ goto err; } @@ -1369,7 +1446,6 @@ static int cgroup_populate_controllers(char *controllers[CG_CONTROLLER_MAX]) goto err; } - i = 0; while (!feof(proc_cgroup)) { /* * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(), @@ -1438,6 +1514,13 @@ static int cgroup_populate_mount_points(char *controllers[CG_CONTROLLER_MAX]) sizeof(mntent_buffer))) != NULL) { if (strcmp(ent->mnt_type, "cgroup") == 0) { + if (controllers[0] == NULL) { + cgroup_err("cgroup v1 requires /proc/cgroups, check if /proc "); + cgroup_cont("is mounted with subset=pid option.\n"); + ret = ECGINVAL; + goto err; + } + ret = cgroup_process_v1_mnt(controllers, ent, &found_mnt); if (ret) goto err; -- 2.47.3