sched/topology: fix potential memoryleak in sched_init_numa()

Message ID 20230621063817.3753617-1-linmiaohe@huawei.com
State New
Headers
Series sched/topology: fix potential memoryleak in sched_init_numa() |

Commit Message

Miaohe Lin June 21, 2023, 6:38 a.m. UTC
  When sched_init_numa() fails to allocate enough memory for sched domains
numa masks, it forgot to free the allocated memory leading to memoryleak.
Add a helper to help release the resource.

Fixes: cb83b629bae0 ("sched/numa: Rewrite the CONFIG_NUMA sched domain support")
Signed-off-by: Miaohe Lin <linmiaohe@huawei.com>
---
 kernel/sched/topology.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)
  

Comments

Abel Wu June 21, 2023, 8:19 a.m. UTC | #1
On 6/21/23 2:38 PM, Miaohe Lin wrote:
> When sched_init_numa() fails to allocate enough memory for sched domains
> numa masks, it forgot to free the allocated memory leading to memoryleak.
> Add a helper to help release the resource.
> 
> Fixes: cb83b629bae0 ("sched/numa: Rewrite the CONFIG_NUMA sched domain support")
> Signed-off-by: Miaohe Lin <linmiaohe@huawei.com>
> ---
>   kernel/sched/topology.c | 22 ++++++++++++++++++++--
>   1 file changed, 20 insertions(+), 2 deletions(-)
> 
> diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
> index 290509383419..dcec4d653ae3 100644
> --- a/kernel/sched/topology.c
> +++ b/kernel/sched/topology.c
> @@ -1807,6 +1807,20 @@ static void init_numa_topology_type(int offline_node)
>   
>   #define NR_DISTANCE_VALUES (1 << DISTANCE_BITS)
>   
> +static void sched_free_numa_mask(struct cpumask ***masks, int nr_levels)
> +{
> +	int i, j;
> +
> +	for (i = 0; i < nr_levels; i++) {
> +		if (!masks[i])
> +			continue;
> +		for_each_node(j)
> +			kfree(masks[i][j]);
> +		kfree(masks[i]);
> +	}
> +	kfree(masks);
> +}
> +
>   void sched_init_numa(int offline_node)
>   {
>   	struct sched_domain_topology_level *tl;
> @@ -1886,15 +1900,19 @@ void sched_init_numa(int offline_node)
>   	 */
>   	for (i = 0; i < nr_levels; i++) {
>   		masks[i] = kzalloc(nr_node_ids * sizeof(void *), GFP_KERNEL);
> -		if (!masks[i])
> +		if (!masks[i]) {
> +			sched_free_numa_mask(masks, nr_levels);
>   			return;
> +		}
>   
>   		for_each_cpu_node_but(j, offline_node) {
>   			struct cpumask *mask = kzalloc(cpumask_size(), GFP_KERNEL);
>   			int k;
>   
> -			if (!mask)
> +			if (!mask) {
> +				sched_free_numa_mask(masks, nr_levels);
>   				return;
> +			}
>   
>   			masks[i][j] = mask;
>   

Allocation can also fail in @tl (topology level), and if that is the
case, masks[][] IMHO also needs be freed. So I think it might be better
if call sched_reset_numa() at proper place.
  

Patch

diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 290509383419..dcec4d653ae3 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1807,6 +1807,20 @@  static void init_numa_topology_type(int offline_node)
 
 #define NR_DISTANCE_VALUES (1 << DISTANCE_BITS)
 
+static void sched_free_numa_mask(struct cpumask ***masks, int nr_levels)
+{
+	int i, j;
+
+	for (i = 0; i < nr_levels; i++) {
+		if (!masks[i])
+			continue;
+		for_each_node(j)
+			kfree(masks[i][j]);
+		kfree(masks[i]);
+	}
+	kfree(masks);
+}
+
 void sched_init_numa(int offline_node)
 {
 	struct sched_domain_topology_level *tl;
@@ -1886,15 +1900,19 @@  void sched_init_numa(int offline_node)
 	 */
 	for (i = 0; i < nr_levels; i++) {
 		masks[i] = kzalloc(nr_node_ids * sizeof(void *), GFP_KERNEL);
-		if (!masks[i])
+		if (!masks[i]) {
+			sched_free_numa_mask(masks, nr_levels);
 			return;
+		}
 
 		for_each_cpu_node_but(j, offline_node) {
 			struct cpumask *mask = kzalloc(cpumask_size(), GFP_KERNEL);
 			int k;
 
-			if (!mask)
+			if (!mask) {
+				sched_free_numa_mask(masks, nr_levels);
 				return;
+			}
 
 			masks[i][j] = mask;