[PATCH] sched/topology: fix memory leaks in allocation failure pat=
From: cpumsconfig
Date: Thu Jul 09 2026 - 09:58:41 EST
Fix three memory leaks in the scheduler topology code when memory
allocation fails:
1. sched_init_numa(): When per-node masks[i] or inner mask allocation
fails, previously allocated masks[0..i-1] and the masks array itself
are not freed before return. The separately allocated domain_distances
also leaks.
2. __sdt_alloc(): When per-CPU sd/sg/sgc allocation fails in the inner
loop, already allocated per-CPU pointers for completed CPUs leak.
Fix by using a unified fail label that calls __sdt_free().
3. __sds_alloc(): When percpu d->sds allocation succeeds but inner
kzalloc_node fails, d->sds leaks. Fix by calling __sds_free().
Signed-off-by: cpumsconfig <monios114514@xxxxxxxxxxx>
---
kernel/sched/topology.c | 99 ++++++++++++++++++++++-------------------
1 file changed, 53 insertions(+), 46 deletions(-)
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 622e2e019..fb5200b58 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -2320,12 +2320,10 @@ void sched_init_numa(int offline_node)
int *distances, *domain_distances;
struct cpumask ***masks;
=20
- /* Record the NUMA distances from SLIT table */
if (sched_record_numa_dist(offline_node, numa_node_dist, &distances,
&nr_node_levels))
return;
=20
- /* Record modified NUMA distances for building sched domains */
if (modified_sched_node_distance()) {
if (sched_record_numa_dist(offline_node, arch_sched_node_distance,
&domain_distances, &nr_levels)) {
@@ -2340,45 +2338,43 @@ void sched_init_numa(int offline_node)
WRITE_ONCE(sched_max_numa_distance, distances[nr_node_levels - 1]);
WRITE_ONCE(sched_numa_node_levels, nr_node_levels);
=20
- /*
- * 'nr_levels' contains the number of unique distances
- *
- * The sched_domains_numa_distance[] array includes the actual distance
- * numbers.
- */
-
- /*
- * Here, we should temporarily reset sched_domains_numa_levels to 0.
- * If it fails to allocate memory for array sched_domains_numa_masks[][],
- * the array will contain less then 'nr_levels' members. This could be
- * dangerous when we use it to iterate array sched_domains_numa_masks[][]
- * in other functions.
- *
- * We reset it to 'nr_levels' at the end of this function.
- */
rcu_assign_pointer(sched_domains_numa_distance, domain_distances);
=20
sched_domains_numa_levels =3D 0;
=20
masks =3D kzalloc(sizeof(void *) * nr_levels, GFP_KERNEL);
if (!masks)
- return;
+ goto free_distance;
=20
- /*
- * Now for each level, construct a mask per node which contains all
- * CPUs of nodes that are that many hops away from us.
- */
for (i =3D 0; i < nr_levels; i++) {
masks[i] =3D kzalloc(nr_node_ids * sizeof(void *), GFP_KERNEL);
- if (!masks[i])
- return;
+ if (!masks[i]) {
+ for (i =3D i - 1; i >=3D 0; i--) {
+ if (!masks[i])
+ continue;
+ for_each_cpu_node_but(j, offline_node)
+ kfree(masks[i][j]);
+ kfree(masks[i]);
+ }
+ kfree(masks);
+ goto free_distance;
+ }
=20
for_each_cpu_node_but(j, offline_node) {
struct cpumask *mask =3D kzalloc(cpumask_size(), GFP_KERNEL);
int k;
=20
- if (!mask)
- return;
+ if (!mask) {
+ for (i =3D i; i >=3D 0; i--) {
+ if (!masks[i])
+ continue;
+ for_each_cpu_node_but(j, offline_node)
+ kfree(masks[i][j]);
+ kfree(masks[i]);
+ }
+ kfree(masks);
+ goto free_distance;
+ }
=20
masks[i][j] =3D mask;
=20
@@ -2398,28 +2394,28 @@ void sched_init_numa(int offline_node)
}
rcu_assign_pointer(sched_domains_numa_masks, masks);
=20
- /* Compute default topology size */
for (i =3D 0; sched_domain_topology[i].mask; i++);
=20
tl =3D kzalloc((i + nr_levels + 1) *
sizeof(struct sched_domain_topology_level), GFP_KERNEL);
- if (!tl)
- return;
+ if (!tl) {
+ rcu_assign_pointer(sched_domains_numa_masks, NULL);
+ for (i =3D nr_levels - 1; i >=3D 0; i--) {
+ if (!masks[i])
+ continue;
+ for_each_cpu_node_but(j, offline_node)
+ kfree(masks[i][j]);
+ kfree(masks[i]);
+ }
+ kfree(masks);
+ goto free_distance;
+ }
=20
- /*
- * Copy the default topology bits..
- */
for (i =3D 0; sched_domain_topology[i].mask; i++)
tl[i] =3D sched_domain_topology[i];
=20
- /*
- * Add the NUMA identity distance, aka single NODE.
- */
tl[i++] =3D SDTL_INIT(sd_numa_mask, NULL, NODE);
=20
- /*
- * .. and append 'j' levels of NUMA goodness.
- */
for (j =3D 1; j < nr_levels; i++, j++) {
tl[i] =3D SDTL_INIT(sd_numa_mask, cpu_numa_flags, NUMA);
tl[i].numa_level =3D j;
@@ -2431,6 +2427,11 @@ void sched_init_numa(int offline_node)
sched_domains_numa_levels =3D nr_levels;
=20
init_numa_topology_type(offline_node);
+ return;
+
+free_distance:
+ if (domain_distances !=3D distances)
+ kfree(domain_distances);
}
=20
=20
@@ -2670,15 +2671,15 @@ static int __sdt_alloc(const struct cpumask *cpu_ma=
p)
=20
sdd->sd =3D alloc_percpu(struct sched_domain *);
if (!sdd->sd)
- return -ENOMEM;
+ goto fail;
=20
sdd->sg =3D alloc_percpu(struct sched_group *);
if (!sdd->sg)
- return -ENOMEM;
+ goto fail;
=20
sdd->sgc =3D alloc_percpu(struct sched_group_capacity *);
if (!sdd->sgc)
- return -ENOMEM;
+ goto fail;
=20
for_each_cpu(j, cpu_map) {
struct sched_domain *sd;
@@ -2688,14 +2689,14 @@ static int __sdt_alloc(const struct cpumask *cpu_ma=
p)
sd =3D kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
GFP_KERNEL, cpu_to_node(j));
if (!sd)
- return -ENOMEM;
+ goto fail;
=20
*per_cpu_ptr(sdd->sd, j) =3D sd;
=20
sg =3D kzalloc_node(sizeof(struct sched_group) + cpumask_size(),
GFP_KERNEL, cpu_to_node(j));
if (!sg)
- return -ENOMEM;
+ goto fail;
=20
sg->next =3D sg;
=20
@@ -2704,7 +2705,7 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
sgc =3D kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size=
(),
GFP_KERNEL, cpu_to_node(j));
if (!sgc)
- return -ENOMEM;
+ goto fail;
=20
sgc->id =3D j;
=20
@@ -2713,6 +2714,10 @@ static int __sdt_alloc(const struct cpumask *cpu_map=
)
}
=20
return 0;
+
+fail:
+ __sdt_free(cpu_map);
+ return -ENOMEM;
}
=20
static void __sdt_free(const struct cpumask *cpu_map)
@@ -2760,8 +2765,10 @@ static int __sds_alloc(struct s_data *d, const struc=
t cpumask *cpu_map)
=20
sds =3D kzalloc_node(sizeof(struct sched_domain_shared),
GFP_KERNEL, cpu_to_node(j));
- if (!sds)
+ if (!sds) {
+ __sds_free(d, cpu_map);
return -ENOMEM;
+ }
=20
*per_cpu_ptr(d->sds, j) =3D sds;
}
--=20
2.54.0.windows.1