VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46025

CVE-2026-46025

Description

In the Linux kernel, the following vulnerability has been resolved:

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

Patch series "mm/damon/core: fix damon_call()/damos_walk() vs kdmond exit race".

damon_call() and damos_walk() can leak memory and/or deadlock when they race with kdamond terminations. Fix those.

This patch (of 2);

When kdamond_fn() main loop is finished, the function cancels all remaining damon_call() requests and unset the damon_ctx->kdamond so that API callers and API functions themselves can know the context is terminated. damon_call() adds the caller's request to the queue first. After that, it shows if the kdamond of the damon_ctx is still running (damon_ctx->kdamond is set). Only if the kdamond is running, damon_call() starts waiting for the kdamond's handling of the newly added request.

The damon_call() requests registration and damon_ctx->kdamond unset are protected by different mutexes, though. Hence, damon_call() could race with damon_ctx->kdamond unset, and result in deadlocks.

For example, let's suppose kdamond successfully finished the damon_call() requests cancelling. Right after that, damon_call() is called for the context. It registers the new request, and shows the context is still running, because damon_ctx->kdamond unset is not yet done. Hence the damon_call() caller starts waiting for the handling of the request. However, the kdamond is already on the termination steps, so it never handles the new request. As a result, the damon_call() caller threads infinitely waits.

Fix this by introducing another damon_ctx field, namely call_controls_obsolete. It is protected by the damon_ctx->call_controls_lock, which protects damon_call() requests registration. Initialize (unset) it in kdamond_fn() before letting damon_start() returns and set it just before the cancelling of remaining damon_call() requests is executed. damon_call() reads the obsolete field under the lock and avoids adding a new request.

After this change, only requests that are guaranteed to be handled or cancelled are registered. Hence the after-registration DAMON context termination check is no longer needed. Remove it together.

Note that the deadlock will not happen when damon_call() is called for repeat mode request. In tis case, damon_call() returns instead of waiting for the handling when the request registration succeeds and it shows the kdamond is running. However, if the request also has dealloc_on_cancel, the request memory would be leaked.

The issue is found by sashiko [1].

Affected products

1

Patches

6
e6a053a6f4b5

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSeongJae ParkMar 27, 2026Fixed in 7.0.4via kernel-cna
2 files changed · +15 32
  • include/linux/damon.h+1 0 modified
    diff --git a/include/linux/damon.h b/include/linux/damon.h
    index be3d198043ff9f..fe4fc20e6db9df 100644
    --- a/include/linux/damon.h
    +++ b/include/linux/damon.h
    @@ -805,6 +805,7 @@ struct damon_ctx {
     
     	/* lists of &struct damon_call_control */
     	struct list_head call_controls;
    +	bool call_controls_obsolete;
     	struct mutex call_controls_lock;
     
     	struct damos_walk_control *walk_control;
    
  • mm/damon/core.c+14 32 modified
    diff --git a/mm/damon/core.c b/mm/damon/core.c
    index 3e1890d64d067a..5d77462166620a 100644
    --- a/mm/damon/core.c
    +++ b/mm/damon/core.c
    @@ -1464,35 +1464,6 @@ int damon_kdamond_pid(struct damon_ctx *ctx)
     	return pid;
     }
     
    -/*
    - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to
    - *				      an inactive context.
    - * @ctx:	The inactive DAMON context.
    - * @control:	Control variable of the call request.
    - *
    - * This function is called in a case that @control is added to @ctx but @ctx is
    - * not running (inactive).  See if @ctx handled @control or not, and cleanup
    - * @control if it was not handled.
    - *
    - * Returns 0 if @control was handled by @ctx, negative error code otherwise.
    - */
    -static int damon_call_handle_inactive_ctx(
    -		struct damon_ctx *ctx, struct damon_call_control *control)
    -{
    -	struct damon_call_control *c;
    -
    -	mutex_lock(&ctx->call_controls_lock);
    -	list_for_each_entry(c, &ctx->call_controls, list) {
    -		if (c == control) {
    -			list_del(&control->list);
    -			mutex_unlock(&ctx->call_controls_lock);
    -			return -EINVAL;
    -		}
    -	}
    -	mutex_unlock(&ctx->call_controls_lock);
    -	return 0;
    -}
    -
     /**
      * damon_call() - Invoke a given function on DAMON worker thread (kdamond).
      * @ctx:	DAMON context to call the function for.
    @@ -1510,6 +1481,10 @@ static int damon_call_handle_inactive_ctx(
      * synchronization.  The return value of the function will be saved in
      * &damon_call_control->return_code.
      *
    + * Note that this function should be called only after damon_start() with the
    + * @ctx has succeeded.  Otherwise, this function could fall into an indefinite
    + * wait.
    + *
      * Return: 0 on success, negative error code otherwise.
      */
     int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
    @@ -1520,10 +1495,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
     	INIT_LIST_HEAD(&control->list);
     
     	mutex_lock(&ctx->call_controls_lock);
    +	if (ctx->call_controls_obsolete) {
    +		mutex_unlock(&ctx->call_controls_lock);
    +		return -ECANCELED;
    +	}
     	list_add_tail(&control->list, &ctx->call_controls);
     	mutex_unlock(&ctx->call_controls_lock);
    -	if (!damon_is_running(ctx))
    -		return damon_call_handle_inactive_ctx(ctx, control);
     	if (control->repeat)
     		return 0;
     	wait_for_completion(&control->completion);
    @@ -2751,6 +2728,9 @@ static int kdamond_fn(void *data)
     
     	pr_debug("kdamond (%d) starts\n", current->pid);
     
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = false;
    +	mutex_unlock(&ctx->call_controls_lock);
     	complete(&ctx->kdamond_started);
     	kdamond_init_ctx(ctx);
     
    @@ -2855,6 +2835,9 @@ done:
     	damon_destroy_targets(ctx);
     
     	kfree(ctx->regions_score_histogram);
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = true;
    +	mutex_unlock(&ctx->call_controls_lock);
     	kdamond_call(ctx, true);
     	damos_walk_cancel(ctx);
     
    -- 
    cgit 1.3-korg
    
    
    
2691332ad88b

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSeongJae ParkMar 27, 2026Fixed in 6.18.27via kernel-cna
2 files changed · +15 32
  • include/linux/damon.h+1 0 modified
    diff --git a/include/linux/damon.h b/include/linux/damon.h
    index 1a8a79d7e4e8d6..6fe6f7fcf83d8f 100644
    --- a/include/linux/damon.h
    +++ b/include/linux/damon.h
    @@ -781,6 +781,7 @@ struct damon_ctx {
     
     	/* lists of &struct damon_call_control */
     	struct list_head call_controls;
    +	bool call_controls_obsolete;
     	struct mutex call_controls_lock;
     
     	struct damos_walk_control *walk_control;
    
  • mm/damon/core.c+14 32 modified
    diff --git a/mm/damon/core.c b/mm/damon/core.c
    index 87b6c9c2d64717..3fb199a47fa9e9 100644
    --- a/mm/damon/core.c
    +++ b/mm/damon/core.c
    @@ -1431,35 +1431,6 @@ bool damon_is_running(struct damon_ctx *ctx)
     	return running;
     }
     
    -/*
    - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to
    - *				      an inactive context.
    - * @ctx:	The inactive DAMON context.
    - * @control:	Control variable of the call request.
    - *
    - * This function is called in a case that @control is added to @ctx but @ctx is
    - * not running (inactive).  See if @ctx handled @control or not, and cleanup
    - * @control if it was not handled.
    - *
    - * Returns 0 if @control was handled by @ctx, negative error code otherwise.
    - */
    -static int damon_call_handle_inactive_ctx(
    -		struct damon_ctx *ctx, struct damon_call_control *control)
    -{
    -	struct damon_call_control *c;
    -
    -	mutex_lock(&ctx->call_controls_lock);
    -	list_for_each_entry(c, &ctx->call_controls, list) {
    -		if (c == control) {
    -			list_del(&control->list);
    -			mutex_unlock(&ctx->call_controls_lock);
    -			return -EINVAL;
    -		}
    -	}
    -	mutex_unlock(&ctx->call_controls_lock);
    -	return 0;
    -}
    -
     /**
      * damon_call() - Invoke a given function on DAMON worker thread (kdamond).
      * @ctx:	DAMON context to call the function for.
    @@ -1477,6 +1448,10 @@ static int damon_call_handle_inactive_ctx(
      * synchronization.  The return value of the function will be saved in
      * &damon_call_control->return_code.
      *
    + * Note that this function should be called only after damon_start() with the
    + * @ctx has succeeded.  Otherwise, this function could fall into an indefinite
    + * wait.
    + *
      * Return: 0 on success, negative error code otherwise.
      */
     int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
    @@ -1487,10 +1462,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
     	INIT_LIST_HEAD(&control->list);
     
     	mutex_lock(&ctx->call_controls_lock);
    +	if (ctx->call_controls_obsolete) {
    +		mutex_unlock(&ctx->call_controls_lock);
    +		return -ECANCELED;
    +	}
     	list_add_tail(&control->list, &ctx->call_controls);
     	mutex_unlock(&ctx->call_controls_lock);
    -	if (!damon_is_running(ctx))
    -		return damon_call_handle_inactive_ctx(ctx, control);
     	if (control->repeat)
     		return 0;
     	wait_for_completion(&control->completion);
    @@ -2640,6 +2617,9 @@ static int kdamond_fn(void *data)
     
     	pr_debug("kdamond (%d) starts\n", current->pid);
     
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = false;
    +	mutex_unlock(&ctx->call_controls_lock);
     	complete(&ctx->kdamond_started);
     	kdamond_init_ctx(ctx);
     
    @@ -2749,6 +2729,9 @@ done:
     	if (ctx->ops.cleanup)
     		ctx->ops.cleanup(ctx);
     	kfree(ctx->regions_score_histogram);
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = true;
    +	mutex_unlock(&ctx->call_controls_lock);
     	kdamond_call(ctx, true);
     
     	pr_debug("kdamond (%d) finishes\n", current->pid);
    -- 
    cgit 1.3-korg
    
    
    
55da81663b96

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSeongJae ParkMar 27, 2026Fixed in 7.1-rc1via kernel-cna
2 files changed · +15 32
  • include/linux/damon.h+1 0 modified
    diff --git a/include/linux/damon.h b/include/linux/damon.h
    index d9a3babbafc167..5129de70e7b70b 100644
    --- a/include/linux/damon.h
    +++ b/include/linux/damon.h
    @@ -818,6 +818,7 @@ struct damon_ctx {
     
     	/* lists of &struct damon_call_control */
     	struct list_head call_controls;
    +	bool call_controls_obsolete;
     	struct mutex call_controls_lock;
     
     	struct damos_walk_control *walk_control;
    
  • mm/damon/core.c+14 32 modified
    diff --git a/mm/damon/core.c b/mm/damon/core.c
    index db6c67e52d2b86..9bcda2765ac97f 100644
    --- a/mm/damon/core.c
    +++ b/mm/damon/core.c
    @@ -1573,35 +1573,6 @@ int damon_kdamond_pid(struct damon_ctx *ctx)
     	return pid;
     }
     
    -/*
    - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to
    - *				      an inactive context.
    - * @ctx:	The inactive DAMON context.
    - * @control:	Control variable of the call request.
    - *
    - * This function is called in a case that @control is added to @ctx but @ctx is
    - * not running (inactive).  See if @ctx handled @control or not, and cleanup
    - * @control if it was not handled.
    - *
    - * Returns 0 if @control was handled by @ctx, negative error code otherwise.
    - */
    -static int damon_call_handle_inactive_ctx(
    -		struct damon_ctx *ctx, struct damon_call_control *control)
    -{
    -	struct damon_call_control *c;
    -
    -	mutex_lock(&ctx->call_controls_lock);
    -	list_for_each_entry(c, &ctx->call_controls, list) {
    -		if (c == control) {
    -			list_del(&control->list);
    -			mutex_unlock(&ctx->call_controls_lock);
    -			return -EINVAL;
    -		}
    -	}
    -	mutex_unlock(&ctx->call_controls_lock);
    -	return 0;
    -}
    -
     /**
      * damon_call() - Invoke a given function on DAMON worker thread (kdamond).
      * @ctx:	DAMON context to call the function for.
    @@ -1619,6 +1590,10 @@ static int damon_call_handle_inactive_ctx(
      * synchronization.  The return value of the function will be saved in
      * &damon_call_control->return_code.
      *
    + * Note that this function should be called only after damon_start() with the
    + * @ctx has succeeded.  Otherwise, this function could fall into an indefinite
    + * wait.
    + *
      * Return: 0 on success, negative error code otherwise.
      */
     int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
    @@ -1629,10 +1604,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
     	INIT_LIST_HEAD(&control->list);
     
     	mutex_lock(&ctx->call_controls_lock);
    +	if (ctx->call_controls_obsolete) {
    +		mutex_unlock(&ctx->call_controls_lock);
    +		return -ECANCELED;
    +	}
     	list_add_tail(&control->list, &ctx->call_controls);
     	mutex_unlock(&ctx->call_controls_lock);
    -	if (!damon_is_running(ctx))
    -		return damon_call_handle_inactive_ctx(ctx, control);
     	if (control->repeat)
     		return 0;
     	wait_for_completion(&control->completion);
    @@ -2952,6 +2929,9 @@ static int kdamond_fn(void *data)
     
     	pr_debug("kdamond (%d) starts\n", current->pid);
     
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = false;
    +	mutex_unlock(&ctx->call_controls_lock);
     	complete(&ctx->kdamond_started);
     	kdamond_init_ctx(ctx);
     
    @@ -3062,6 +3042,9 @@ done:
     	damon_destroy_targets(ctx);
     
     	kfree(ctx->regions_score_histogram);
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = true;
    +	mutex_unlock(&ctx->call_controls_lock);
     	kdamond_call(ctx, true);
     	damos_walk_cancel(ctx);
     
    -- 
    cgit 1.3-korg
    
    
    
2691332ad88b

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

2 files changed · +15 32
  • include/linux/damon.h+1 0 modified
    diff --git a/include/linux/damon.h b/include/linux/damon.h
    index 1a8a79d7e4e8d6..6fe6f7fcf83d8f 100644
    --- a/include/linux/damon.h
    +++ b/include/linux/damon.h
    @@ -781,6 +781,7 @@ struct damon_ctx {
     
     	/* lists of &struct damon_call_control */
     	struct list_head call_controls;
    +	bool call_controls_obsolete;
     	struct mutex call_controls_lock;
     
     	struct damos_walk_control *walk_control;
    
  • mm/damon/core.c+14 32 modified
    diff --git a/mm/damon/core.c b/mm/damon/core.c
    index 87b6c9c2d64717..3fb199a47fa9e9 100644
    --- a/mm/damon/core.c
    +++ b/mm/damon/core.c
    @@ -1431,35 +1431,6 @@ bool damon_is_running(struct damon_ctx *ctx)
     	return running;
     }
     
    -/*
    - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to
    - *				      an inactive context.
    - * @ctx:	The inactive DAMON context.
    - * @control:	Control variable of the call request.
    - *
    - * This function is called in a case that @control is added to @ctx but @ctx is
    - * not running (inactive).  See if @ctx handled @control or not, and cleanup
    - * @control if it was not handled.
    - *
    - * Returns 0 if @control was handled by @ctx, negative error code otherwise.
    - */
    -static int damon_call_handle_inactive_ctx(
    -		struct damon_ctx *ctx, struct damon_call_control *control)
    -{
    -	struct damon_call_control *c;
    -
    -	mutex_lock(&ctx->call_controls_lock);
    -	list_for_each_entry(c, &ctx->call_controls, list) {
    -		if (c == control) {
    -			list_del(&control->list);
    -			mutex_unlock(&ctx->call_controls_lock);
    -			return -EINVAL;
    -		}
    -	}
    -	mutex_unlock(&ctx->call_controls_lock);
    -	return 0;
    -}
    -
     /**
      * damon_call() - Invoke a given function on DAMON worker thread (kdamond).
      * @ctx:	DAMON context to call the function for.
    @@ -1477,6 +1448,10 @@ static int damon_call_handle_inactive_ctx(
      * synchronization.  The return value of the function will be saved in
      * &damon_call_control->return_code.
      *
    + * Note that this function should be called only after damon_start() with the
    + * @ctx has succeeded.  Otherwise, this function could fall into an indefinite
    + * wait.
    + *
      * Return: 0 on success, negative error code otherwise.
      */
     int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
    @@ -1487,10 +1462,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
     	INIT_LIST_HEAD(&control->list);
     
     	mutex_lock(&ctx->call_controls_lock);
    +	if (ctx->call_controls_obsolete) {
    +		mutex_unlock(&ctx->call_controls_lock);
    +		return -ECANCELED;
    +	}
     	list_add_tail(&control->list, &ctx->call_controls);
     	mutex_unlock(&ctx->call_controls_lock);
    -	if (!damon_is_running(ctx))
    -		return damon_call_handle_inactive_ctx(ctx, control);
     	if (control->repeat)
     		return 0;
     	wait_for_completion(&control->completion);
    @@ -2640,6 +2617,9 @@ static int kdamond_fn(void *data)
     
     	pr_debug("kdamond (%d) starts\n", current->pid);
     
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = false;
    +	mutex_unlock(&ctx->call_controls_lock);
     	complete(&ctx->kdamond_started);
     	kdamond_init_ctx(ctx);
     
    @@ -2749,6 +2729,9 @@ done:
     	if (ctx->ops.cleanup)
     		ctx->ops.cleanup(ctx);
     	kfree(ctx->regions_score_histogram);
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = true;
    +	mutex_unlock(&ctx->call_controls_lock);
     	kdamond_call(ctx, true);
     
     	pr_debug("kdamond (%d) finishes\n", current->pid);
    -- 
    cgit 1.3-korg
    
    
    
e6a053a6f4b5

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

2 files changed · +15 32
  • include/linux/damon.h+1 0 modified
    diff --git a/include/linux/damon.h b/include/linux/damon.h
    index be3d198043ff9f..fe4fc20e6db9df 100644
    --- a/include/linux/damon.h
    +++ b/include/linux/damon.h
    @@ -805,6 +805,7 @@ struct damon_ctx {
     
     	/* lists of &struct damon_call_control */
     	struct list_head call_controls;
    +	bool call_controls_obsolete;
     	struct mutex call_controls_lock;
     
     	struct damos_walk_control *walk_control;
    
  • mm/damon/core.c+14 32 modified
    diff --git a/mm/damon/core.c b/mm/damon/core.c
    index 3e1890d64d067a..5d77462166620a 100644
    --- a/mm/damon/core.c
    +++ b/mm/damon/core.c
    @@ -1464,35 +1464,6 @@ int damon_kdamond_pid(struct damon_ctx *ctx)
     	return pid;
     }
     
    -/*
    - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to
    - *				      an inactive context.
    - * @ctx:	The inactive DAMON context.
    - * @control:	Control variable of the call request.
    - *
    - * This function is called in a case that @control is added to @ctx but @ctx is
    - * not running (inactive).  See if @ctx handled @control or not, and cleanup
    - * @control if it was not handled.
    - *
    - * Returns 0 if @control was handled by @ctx, negative error code otherwise.
    - */
    -static int damon_call_handle_inactive_ctx(
    -		struct damon_ctx *ctx, struct damon_call_control *control)
    -{
    -	struct damon_call_control *c;
    -
    -	mutex_lock(&ctx->call_controls_lock);
    -	list_for_each_entry(c, &ctx->call_controls, list) {
    -		if (c == control) {
    -			list_del(&control->list);
    -			mutex_unlock(&ctx->call_controls_lock);
    -			return -EINVAL;
    -		}
    -	}
    -	mutex_unlock(&ctx->call_controls_lock);
    -	return 0;
    -}
    -
     /**
      * damon_call() - Invoke a given function on DAMON worker thread (kdamond).
      * @ctx:	DAMON context to call the function for.
    @@ -1510,6 +1481,10 @@ static int damon_call_handle_inactive_ctx(
      * synchronization.  The return value of the function will be saved in
      * &damon_call_control->return_code.
      *
    + * Note that this function should be called only after damon_start() with the
    + * @ctx has succeeded.  Otherwise, this function could fall into an indefinite
    + * wait.
    + *
      * Return: 0 on success, negative error code otherwise.
      */
     int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
    @@ -1520,10 +1495,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
     	INIT_LIST_HEAD(&control->list);
     
     	mutex_lock(&ctx->call_controls_lock);
    +	if (ctx->call_controls_obsolete) {
    +		mutex_unlock(&ctx->call_controls_lock);
    +		return -ECANCELED;
    +	}
     	list_add_tail(&control->list, &ctx->call_controls);
     	mutex_unlock(&ctx->call_controls_lock);
    -	if (!damon_is_running(ctx))
    -		return damon_call_handle_inactive_ctx(ctx, control);
     	if (control->repeat)
     		return 0;
     	wait_for_completion(&control->completion);
    @@ -2751,6 +2728,9 @@ static int kdamond_fn(void *data)
     
     	pr_debug("kdamond (%d) starts\n", current->pid);
     
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = false;
    +	mutex_unlock(&ctx->call_controls_lock);
     	complete(&ctx->kdamond_started);
     	kdamond_init_ctx(ctx);
     
    @@ -2855,6 +2835,9 @@ done:
     	damon_destroy_targets(ctx);
     
     	kfree(ctx->regions_score_histogram);
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = true;
    +	mutex_unlock(&ctx->call_controls_lock);
     	kdamond_call(ctx, true);
     	damos_walk_cancel(ctx);
     
    -- 
    cgit 1.3-korg
    
    
    
55da81663b96

mm/damon/core: fix damon_call() vs kdamond_fn() exit race

2 files changed · +15 32
  • include/linux/damon.h+1 0 modified
    diff --git a/include/linux/damon.h b/include/linux/damon.h
    index d9a3babbafc167..5129de70e7b70b 100644
    --- a/include/linux/damon.h
    +++ b/include/linux/damon.h
    @@ -818,6 +818,7 @@ struct damon_ctx {
     
     	/* lists of &struct damon_call_control */
     	struct list_head call_controls;
    +	bool call_controls_obsolete;
     	struct mutex call_controls_lock;
     
     	struct damos_walk_control *walk_control;
    
  • mm/damon/core.c+14 32 modified
    diff --git a/mm/damon/core.c b/mm/damon/core.c
    index db6c67e52d2b86..9bcda2765ac97f 100644
    --- a/mm/damon/core.c
    +++ b/mm/damon/core.c
    @@ -1573,35 +1573,6 @@ int damon_kdamond_pid(struct damon_ctx *ctx)
     	return pid;
     }
     
    -/*
    - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to
    - *				      an inactive context.
    - * @ctx:	The inactive DAMON context.
    - * @control:	Control variable of the call request.
    - *
    - * This function is called in a case that @control is added to @ctx but @ctx is
    - * not running (inactive).  See if @ctx handled @control or not, and cleanup
    - * @control if it was not handled.
    - *
    - * Returns 0 if @control was handled by @ctx, negative error code otherwise.
    - */
    -static int damon_call_handle_inactive_ctx(
    -		struct damon_ctx *ctx, struct damon_call_control *control)
    -{
    -	struct damon_call_control *c;
    -
    -	mutex_lock(&ctx->call_controls_lock);
    -	list_for_each_entry(c, &ctx->call_controls, list) {
    -		if (c == control) {
    -			list_del(&control->list);
    -			mutex_unlock(&ctx->call_controls_lock);
    -			return -EINVAL;
    -		}
    -	}
    -	mutex_unlock(&ctx->call_controls_lock);
    -	return 0;
    -}
    -
     /**
      * damon_call() - Invoke a given function on DAMON worker thread (kdamond).
      * @ctx:	DAMON context to call the function for.
    @@ -1619,6 +1590,10 @@ static int damon_call_handle_inactive_ctx(
      * synchronization.  The return value of the function will be saved in
      * &damon_call_control->return_code.
      *
    + * Note that this function should be called only after damon_start() with the
    + * @ctx has succeeded.  Otherwise, this function could fall into an indefinite
    + * wait.
    + *
      * Return: 0 on success, negative error code otherwise.
      */
     int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
    @@ -1629,10 +1604,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
     	INIT_LIST_HEAD(&control->list);
     
     	mutex_lock(&ctx->call_controls_lock);
    +	if (ctx->call_controls_obsolete) {
    +		mutex_unlock(&ctx->call_controls_lock);
    +		return -ECANCELED;
    +	}
     	list_add_tail(&control->list, &ctx->call_controls);
     	mutex_unlock(&ctx->call_controls_lock);
    -	if (!damon_is_running(ctx))
    -		return damon_call_handle_inactive_ctx(ctx, control);
     	if (control->repeat)
     		return 0;
     	wait_for_completion(&control->completion);
    @@ -2952,6 +2929,9 @@ static int kdamond_fn(void *data)
     
     	pr_debug("kdamond (%d) starts\n", current->pid);
     
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = false;
    +	mutex_unlock(&ctx->call_controls_lock);
     	complete(&ctx->kdamond_started);
     	kdamond_init_ctx(ctx);
     
    @@ -3062,6 +3042,9 @@ done:
     	damon_destroy_targets(ctx);
     
     	kfree(ctx->regions_score_histogram);
    +	mutex_lock(&ctx->call_controls_lock);
    +	ctx->call_controls_obsolete = true;
    +	mutex_unlock(&ctx->call_controls_lock);
     	kdamond_call(ctx, true);
     	damos_walk_cancel(ctx);
     
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing synchronization between damon_call() request registration and kdamond termination allows a new request to be queued after the kdamond has already cancelled all pending requests, leading to an indefinite wait."

Attack vector

An attacker or any caller that can invoke damon_call() on a DAMON context that is being terminated can trigger a deadlock. The race occurs because damon_call() registers a new request under call_controls_lock, then checks damon_ctx->kdamond (protected by a different lock) to decide whether to wait for completion. If kdamond_fn() has already cancelled all requests but not yet unset damon_ctx->kdamond, the new request is registered and the caller waits forever since the kdamond will never handle it [patch_id=2660341].

Affected code

The race is in `mm/damon/core.c` in the functions `damon_call()` and `kdamond_fn()`. The `damon_call()` function registers a request under `call_controls_lock` and then checks `damon_is_running()` (which reads `damon_ctx->kdamond` under a different lock) to decide whether to wait. The `kdamond_fn()` function cancels pending requests and unsets `damon_ctx->kdamond` without coordination between the two locks [patch_id=2660341].

What the fix does

The patch introduces a new boolean field `call_controls_obsolete` in `struct damon_ctx` (in `include/linux/damon.h`), protected by `call_controls_lock`. In `kdamond_fn()`, this flag is set to `false` at start and set to `true` just before cancelling remaining requests. In `damon_call()`, the flag is checked under the lock before adding a new request; if obsolete, the function returns `-ECANCELED` immediately. This ensures no new request is ever queued after the kdamond has begun its cancellation sequence. The old `damon_call_handle_inactive_ctx()` function and the post-registration `damon_is_running()` check are removed as they are no longer needed [patch_id=2660341].

Preconditions

  • inputThe attacker must be able to invoke damon_call() on a DAMON context that is concurrently being terminated by kdamond_fn().
  • configThe race window exists between kdamond cancelling requests and unsetting damon_ctx->kdamond.

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.