mm/damon/{reclaim,lru_sort}: fix commit_inputs infinite hang
Subsystems (3)
SJ Park
maintainercover mm/damon/{reclaim,lru_sort}: fix commit_inputs infinite hang
Writing 'Y' to commit_inputs parameters of DAMON_RECLAIM and DAMON_LRU_SORT before the modules were ever turned on causes infinite hang. Fix those.
The issue was discovered [1] by Sashiko.
[1] https://lore.kernel.org/[email protected]
SJ Park (2):
mm/damon/reclaim: skip damon_call() if ctx has not started mm/damon/lru_sort: skip damon_call() if ctx has not started
mm/damon/lru_sort.c | 11 ++++++----- mm/damon/reclaim.c | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-)
base-commit: d6580a342bc8c459628d2b68b313e9c0668bca59
Signature · 1 line
2.47.3
SJ Park
maintainerRFC PATCH 1/2 mm/damon/reclaim: skip damon_call() if ctx has not started
DAMON_RECLAIM calls damon_call() for commit_inputs parameter user input if the DAMON context is initialized. The context could be initialized, but not yet successfully started. In the case, damon_call() could indefinitely hang. Read the comment on damon_call() for more detail. Fix the problem by memorizing if the DAMON context has ever successfully started, and skip damon_call() if it has not.
This issue can easily be reproduced by writing Y to commit_inputs on a system that DAMON_RECLAIM was not turned on before.
--- mm/damon/reclaim.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
mm/damon/reclaim.c +6 -5
@@ -271,6 +271,8 @@ static int damon_reclaim_commit_inputs_fn(void *arg)return damon_reclaim_apply_parameters (); } +static bool damon_reclaim_damon_has_started; +static int damon_reclaim_commit_inputs_store (const char *val,const struct kernel_param *kp) {
@@ -291,11 +293,8 @@ static int damon_reclaim_commit_inputs_store(const char *val,if (!commit_inputs_request)return 0 ; -/* -* Skip damon_call() if ctx is not initialized to avoid -* NULL pointer dereference. -*/ -if (!ctx) +/* Skip damon_call() if ctx has not successfully started. */ +if (!damon_reclaim_damon_has_started)return -EINVAL ; err =damon_call (ctx, &control);
@@ -344,6 +343,8 @@ static int damon_reclaim_turn(bool on) err =damon_start (&ctx,1 , true);if (err)return err; +if (!damon_reclaim_damon_has_started) + damon_reclaim_damon_has_started = true;return damon_call (ctx, &call_control); }
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [RFC PATCH 1/2] mm/damon/reclaim: skip damon_call() if ctx has not started
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the patch fixes an indefinite hang in mm/damon/reclaim.c when writing to commit_inputs before the DAMON context starts, while failing to apply the identical fix to mm/damon/lru_sort.c. This leaves damon_lru_sort vulnerable to the exact same bug. --
--- Patch [1]: [RFC PATCH 1/2] mm/damon/reclaim: skip damon_call() if ctx has not started --- commit 86e90c966d8636445486cfde6487a797b433cb75
Author: SJ Park <[email protected]>
mm/damon/reclaim: skip damon_call() if ctx has not started
This commit addresses an indefinite hang in DAMON reclaim when writing to commit_inputs before the context has fully started. It introduces a tracking flag to determine whether the DAMON context has successfully started in order to safely skip damon_call().
Quoted 4 lines
mm/damon/reclaim.c +0
-0
[ ... ]
Quoted 4 lines
mm/damon/reclaim.c +0
-0@@ -291,11 +293,8 @@ static int damon_reclaim_commit_inputs_store(const char *val,if (!commit_inputs_request)return 0 ;
mm/damon/reclaim.c static int damon_reclaim_commit_inputs_store(const char *val, · line 300
Quoted 10 lines
mm/damon/reclaim.c +2
-5-/* -* Skip damon_call() if ctx is not initialized to avoid -* NULL pointer dereference. -*/ -if (!ctx) +/* Skip damon_call() if ctx has not successfully started. */ +if (!damon_reclaim_damon_has_started)return -EINVAL ; err =damon_call (ctx, &control);
[Severity: High]
This isn't a bug introduced by this patch, but does the same vulnerability exist in mm/damon/lru_sort.c?
It looks like damon_lru_sort_commit_inputs_store() uses the exact same logic that was fixed here in reclaim.c:
mm/damon/lru_sort.c:damon_lru_sort_commit_inputs_store() {
...
/*
* Skip damon_call() if ctx is not initialized to avoid
* NULL pointer dereference.
*/
if (!ctx)
return -EINVAL;
err = damon_call(ctx, &control);
...
}If a user writes to commit_inputs for damon_lru_sort before the thread has started, could damon_call() blindly invoke wait_for_completion() and hang indefinitely without a running thread to signal it?
Should a similar fix be applied to mm/damon/lru_sort.c to prevent a task from hanging in an uninterruptible sleep state?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
SJ Park
maintainerRe: [RFC PATCH 1/2] mm/damon/reclaim: skip damon_call() if ctx has not started
Quoted 59 lines · On Sun, 02 Aug 2026 19:04:30 +0000 [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the patch fixes an indefinite hang inmm/damon/reclaim.cwhen writing tocommit_inputsbefore the DAMON context starts, while failing to apply the identical fix tomm/damon/lru_sort.c. This leavesdamon_lru_sortvulnerable to the exact same bug. ----- Patch [1]: [RFC PATCH 1/2]
mm/damon/reclaim: skipdamon_call()if ctx has not started --- commit86e90c966d8636445486cfde6487a797b433cb75
Author: SJ Park <[email protected]>
mm/damon/reclaim: skipdamon_call()if ctx has not startedThis commit addresses an indefinite hang in DAMON reclaim when writing to
commit_inputsbefore the context has fully started. It introduces a tracking flag to determine whether the DAMON context has successfully started in order to safely skipdamon_call().Cc <[email protected]> # 7.2.xQuoted 4 lines
mm/damon/reclaim.c +0
-0[ ... ]
Quoted 4 lines
mm/damon/reclaim.c +0
-0@@ -291,11 +293,8 @@ static int damon_reclaim_commit_inputs_store(const char *val,if (!commit_inputs_request)return 0 ;mm/damon/reclaim.c static int damon_reclaim_commit_inputs_store(const char *val, · line 300
Quoted 10 lines
mm/damon/reclaim.c +2
-5-/* -* Skip damon_call() if ctx is not initialized to avoid -* NULL pointer dereference. -*/ -if (!ctx) +/* Skip damon_call() if ctx has not successfully started. */ +if (!damon_reclaim_damon_has_started)return -EINVAL ; err =damon_call (ctx, &control);[Severity: High]
This isn't a bug introduced by this patch, but does the same vulnerability exist inmm/damon/lru_sort.c?It looks like
damon_lru_sort_commit_inputs_store()uses the exact same logic that was fixed here inreclaim.c:mm/damon/lru_sort.c:damon_lru_sort_commit_inputs_store() { ... /* * Skip damon_call() if ctx is not initialized to avoid * NULL pointer dereference. */ if (!ctx) return -EINVAL; err = damon_call(ctx, &control); ... }
Quoted 6 lines
If a user writes to
commit_inputsfordamon_lru_sortbefore the thread has started, coulddamon_call()blindly invokewait_for_completion()and hang indefinitely without a running thread to signal it?Should a similar fix be applied to
mm/damon/lru_sort.cto prevent a task from hanging in an uninterruptible sleep state?
Yes, and the next patch of this series is fixing it.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Thanks,
SJ
SJ Park
maintainerRFC PATCH 2/2 mm/damon/lru_sort: skip damon_call() if ctx has not started
In reply to SJ ParkDAMON_LRU_SORT calls damon_call() for commit_inputs parameter user input if the DAMON context is initialized. The context could be initialized, but not yet successfully started. In the case, damon_call() could indefinitely hang. Read the comment on damon_call() for more detail. Fix the problem by memorizing if the DAMON context has ever successfully started, and skip damon_call() if it has not.
This issue can easily be reproduced by writing Y to the commit_inputs parameter file on a system that DAMON_LRU_SORT has not turned on before.
--- mm/damon/lru_sort.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
mm/damon/lru_sort.c +6 -5
@@ -346,6 +346,8 @@ static int damon_lru_sort_commit_inputs_fn(void *arg)return damon_lru_sort_apply_parameters (); } +static bool damon_lru_sort_damon_has_started; +static int damon_lru_sort_commit_inputs_store (const char *val,const struct kernel_param *kp) {
@@ -366,11 +368,8 @@ static int damon_lru_sort_commit_inputs_store(const char *val,if (!commit_inputs_request)return 0 ; -/* -* Skip damon_call() if ctx is not initialized to avoid -* NULL pointer dereference. -*/ -if (!ctx) +/* Skip damon_call() if ctx has not successfully started. */ +if (!damon_lru_sort_damon_has_started)return -EINVAL ; err =damon_call (ctx, &control);
@@ -423,6 +422,8 @@ static int damon_lru_sort_turn(bool on) err =damon_start (&ctx,1 , true);if (err)return err; +if (!damon_lru_sort_damon_has_started) + damon_lru_sort_damon_has_started = true;return damon_call (ctx, &call_control); }
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [RFC PATCH 2/2] mm/damon/lru_sort: skip damon_call() if ctx has not started
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] A TOCTOU race between damon_start() and damon_call() can cause damon_call() to loop indefinitely. --
--- Patch [2]: [RFC PATCH 2/2] mm/damon/lru_sort: skip damon_call() if ctx has not started --- commit 9a410362ed5a4418640b6bf73c4ee49300c9dc16
Author: SJ Park <[email protected]>
mm/damon/lru_sort: skip damon_call() if ctx has not started
This commit prevents damon_call() from hanging when the DAMON context is initialized but not yet started. It introduces a boolean flag to track whether the context has ever successfully started, and skips the call if it has not.
Quoted 4 lines
mm/damon/lru_sort.c +0
-0
[ ... ]
Quoted 4 lines
mm/damon/lru_sort.c +0
-0@@ -364,11 +366,8 @@ static int damon_lru_sort_commit_inputs_store(const char *val,if (!commit_inputs_request)return 0 ;
mm/damon/lru_sort.c static int damon_lru_sort_commit_inputs_store(const char *val, · line 373
Quoted 10 lines
mm/damon/lru_sort.c +2
-5-/* -* Skip damon_call() if ctx is not initialized to avoid -* NULL pointer dereference. -*/ -if (!ctx) +/* Skip damon_call() if ctx has not successfully started. */ +if (!damon_lru_sort_damon_has_started)return -EINVAL ; err =damon_call (ctx, &control);
[Severity: High]
This is a pre-existing issue, but could there be a TOCTOU race here between damon_start() and damon_call() that causes damon_call() to loop indefinitely?
If a user starts DAMON (setting damon_lru_sort_damon_has_started = true), stops it (leaving call_controls_obsolete = true), and then concurrently starts DAMON again while calling damon_call() via commit_inputs, can damon_call() get stuck?
Looking at mm/damon/core.c, __damon_start() sets ctx->kdamond before the new thread clears ctx->call_controls_obsolete:
mm/damon/core.c:__damon_start() {
...
ctx->kdamond = kthread_run(kdamond_fn, ctx, "kdamond.%d", nr_running_ctxs);
...
}If damon_call() runs concurrently, it checks call_controls_obsolete without holding kdamond_lock, sees the stale true value, and jumps to the canceled label:
mm/damon/core.c:damon_call() {
...
if (ctx->call_controls_obsolete) {
...
goto canceled;
}
...
canceled:
while (damon_is_running(ctx))
schedule_timeout_idle(msecs_to_jiffies(100));
return -ECANCELED;
}Since damon_is_running() returns true (because ctx->kdamond is set), and the new healthy thread will run indefinitely, will this task loop forever in TASK_IDLE?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
SJ Park
maintainerRe: [RFC PATCH 2/2] mm/damon/lru_sort: skip damon_call() if ctx has not started
Quoted 40 lines · On Sun, 02 Aug 2026 19:08:37 +0000 [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] A TOCTOU race betweendamon_start()anddamon_call()can causedamon_call()to loop indefinitely. ----- Patch [2]: [RFC PATCH 2/2]
mm/damon/lru_sort: skipdamon_call()if ctx has not started --- commit9a410362ed5a4418640b6bf73c4ee49300c9dc16
Author: SJ Park <[email protected]>
mm/damon/lru_sort: skipdamon_call()if ctx has not startedThis commit prevents
damon_call()from hanging when the DAMON context is initialized but not yet started. It introduces a boolean flag to track whether the context has ever successfully started, and skips the call if it has not.Quoted 4 lines
mm/damon/lru_sort.c +0
-0[ ... ]
Quoted 4 lines
mm/damon/lru_sort.c +0
-0@@ -364,11 +366,8 @@ static int damon_lru_sort_commit_inputs_store(const char *val,if (!commit_inputs_request)return 0 ;mm/damon/lru_sort.c static int damon_lru_sort_commit_inputs_store(const char *val, · line 373
Quoted 10 lines
mm/damon/lru_sort.c +2
-5-/* -* Skip damon_call() if ctx is not initialized to avoid -* NULL pointer dereference. -*/ -if (!ctx) +/* Skip damon_call() if ctx has not successfully started. */ +if (!damon_lru_sort_damon_has_started)return -EINVAL ; err =damon_call (ctx, &control);[Severity: High]
This is a pre-existing issue, but could there be a TOCTOU race here betweendamon_start()anddamon_call()that causesdamon_call()to loop indefinitely?
Quoted 4 lines
If a user starts DAMON (setting
damon_lru_sort_damon_has_started= true), stops it (leavingcall_controls_obsolete= true), and then concurrently starts DAMON again while callingdamon_call()viacommit_inputs, candamon_call()get stuck?
There is no such concurrent calls.
Quoted 27 lines
Looking at
mm/damon/core.c,__damon_start()setsctx->kdamondbefore the new thread clearsctx->call_controls_obsolete:mm/damon/core.c:__damon_start() { ... ctx->kdamond = kthread_run(kdamond_fn, ctx, "kdamond.%d", nr_running_ctxs); ... }If
damon_call()runs concurrently, it checkscall_controls_obsoletewithout holdingkdamond_lock, sees the stale true value, and jumps to the canceled label:mm/damon/core.c:damon_call() { ... if (ctx->call_controls_obsolete) { ... goto canceled; } ... canceled: while (damon_is_running(ctx)) schedule_timeout_idle(msecs_to_jiffies(100)); return -ECANCELED; }
Quoted 3 lines
Since
damon_is_running()returns true (becausectx->kdamondis set), and the new healthy thread will run indefinitely, will this task loop forever inTASK_IDLE?
So the issue cannot happen in real. But good point. Maybe the API or the documentation can be safer. I will add this to my TODO list.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Thanks,
SJ