]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
src/config: free namespace table on success and failure
authorKamalesh Babulal <kamalesh.babulal@oracle.com>
Fri, 16 Jan 2026 11:39:35 +0000 (17:09 +0530)
committerTom Hromatka <tom.hromatka@oracle.com>
Wed, 11 Mar 2026 14:11:36 +0000 (08:11 -0600)
The per-thread namespace table (cg_namespace_table[]) is populated by
config_order_namespace_table() / config_validate_namespaces() under
cg_mount_table_lock and then consumed by cg_build_path_locked().
Before this patch we reset the array with a bespoke loop + memset at the
front of config_order_namespace_table(), but error paths relied on the
call chain eventually unwinding into cgroup_free_config() to release
any partially populated entries. That worked, yet it meant the table
could briefly hold dangling pointers after an error while the lock was
already dropped, one subtle slip or new return path would have left stale
namespaces visible to the rest of the library.

This change introduces a small helper,
cgroup_config_free_namespaces_table(), that walks the thread-local
array, frees each string, and nulls the slot. We invoke it in three
places:

- right at the start of config_order_namespace_table(), so every run
  begins with a clean slate.
- from the error branches of both namespace helpers, guaranteeing that
  even a mid-stream failure leaves the table empty before the lock is
  released.
- and (unchanged) from cgroup_free_config(), so the happy path tears
  everything down once the config work finishes.

The end result is both cleaner and safer: we delete the redundant
set-to-NULL/memset sequence and the namespace table is always in a known
state no matter how the parser exits.

Signed-off-by: Kamalesh Babulal <kamalesh.babulal@oracle.com>
Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>
src/config.c

index d04d9d1abf689ef3b9852dfeebcb449e7bb4a6e0..da208f8249fe16fabf8750909890b8e8ceab8834 100644 (file)
@@ -963,6 +963,19 @@ static int cgroup_config_unmount_controllers(void)
        return 0;
 }
 
+/*
+ * Free namespaces table
+ */
+static void cgroup_config_free_namespaces_table(void)
+{
+       int i;
+
+       for (i = 0; i < CG_CONTROLLER_MAX; i++) {
+               free(cg_namespace_table[i]);
+               cg_namespace_table[i] = NULL;
+       }
+}
+
 static int config_validate_namespaces(void)
 {
        char *namespace = NULL;
@@ -1050,6 +1063,8 @@ static int config_validate_namespaces(void)
                i = subsys_count - 1;
        }
 out_error:
+       if (error)
+               cgroup_config_free_namespaces_table();
        pthread_rwlock_unlock(&cg_mount_table_lock);
 
        return error;
@@ -1073,11 +1088,7 @@ static int config_order_namespace_table(void)
        int i = 0;
 
        pthread_rwlock_wrlock(&cg_mount_table_lock);
-       /* Set everything to NULL */
-       for (i = 0; i < CG_CONTROLLER_MAX; i++)
-               cg_namespace_table[i] = NULL;
-
-       memset(cg_namespace_table, 0, CG_CONTROLLER_MAX * sizeof(cg_namespace_table[0]));
+       cgroup_config_free_namespaces_table();
 
        /* Now fill up the namespace table looking at the table we have otherwise. */
        for (i = 0; i < namespace_table_index; i++) {
@@ -1110,6 +1121,8 @@ static int config_order_namespace_table(void)
                }
        }
 error_out:
+       if (error)
+               cgroup_config_free_namespaces_table();
        pthread_rwlock_unlock(&cg_mount_table_lock);
 
        return error;