Skip to contentSkip to conversation

mm/damon/modules: detect and use fresh status

PATCH Applied SeongJae Park 2 participants 10 messages Apr 19

Subsystems (3)
lore
Share & tools
lore

SeongJae Park

author maintainer
#0 lore raw

v3 cover mm/damon/modules: detect and use fresh status

DAMON modules including DAMON_RECLAIM, DAMON_LRU_SORT and DAMON_STAT commonly expose the kdamond running status via their parameters. Under certain scenarios including wrong user inputs and memory allocation failures, those parameter values can be stale. It can confuse users. For DAMON_RECLAIM and DAMON_LRU_SORT, it even makes the kdamond unable to be restarted before the system reboot.

The problem comes from the fact that there are multiple events for the status changes and it is difficult to follow up all the scenarios. Fix the issue by detecting and using the status on demand, instead of using a cached status that is difficult to be updated.

Patches 1-3 fix the bugs in DAMON_RECLAIM, DAMON_LRU_SORT and DAMON_STAT in the order.

Changes from RFC v2.1
- rfc v2.1: https://lore.kernel.org/[email protected]
- Rebase to latest mm-new.
Changes from RFC v2
- rfc v2: https://lore.kernel.org/[email protected]
- Set kdamond_pid set callbacks.
- Support multiple enabled parameters setup on boot commandline.
- Acknowledge the third patch was discovered by Sashiko.
Changes from v2
- v2: https://lore.kernel.org/[email protected]
- Add RFC tag back, for sashiko review.
- Detect and use fresh status instead of trying to catch up all scenarios.
- Change Liew from the responsible author to a credit-deserved co-developer.
- Move authorship responsibility to SJ.
- Add DAMON_STAT fix.
- RFC of the fix was posted separately
(https://lore.kernel.org/[email protected]), and only commit message wordsmithing is added in this version.
Changes from RFC
- rfc: https://lore.kernel.org/[email protected]
- Remove RFC tag.
- Remove 'damon_thread_status' structure and damon_update_thread_status() (SJ pointed out this was too much extension of core API for a problem that can be fixed more simply).
- Add a fallback in damon_{lru_sort, reclaim}_turn() 'N' path. If damon_stop() fails but kdamond is not running, forcefully reset the parameters.
- Reset 'enabled' and 'kdamond_pid' when damon_commit_ctx() fails in damon_{lru_sort, reclaim}_apply_parameters() (kdamond will terminate eventually in this case).

SeongJae Park (3):
mm/damon/reclaim: detect and use fresh enabled and kdamond_pid values mm/damon/lru_sort: detect and use fresh enabled and kdamond_pid values mm/damon/stat: detect and use fresh enabled value

 mm/damon/lru_sort.c | 85 +++++++++++++++++++++++++++++----------------
 mm/damon/reclaim.c  | 85 +++++++++++++++++++++++++++++----------------
 mm/damon/stat.c     | 30 ++++++++++------
 3 files changed, 130 insertions(+), 70 deletions(-)

base-commit: a51b50e60d2dae1f66329f147bde8723e6b1031e

Signature · 1 line
2.47.3
author maintainer
#1 lore raw

PATCH v3 1/3 mm/damon/reclaim: detect and use fresh enabled and kdamond_pid values

DAMON_RECLAIM updates 'enabled' and 'kdamond_pid' parameter values, which represents the running status of its kdamond, when the user explicitly requests start/stop of the kdamond. The kdamond can, however, be stopped in events other than the explicit user request in the following three events.

1. ctx->regions_score_histogram allocation failure at beginning of the execution,
2. damon_commit_ctx() failure due to invalid user input, and
3. damon_commit_ctx() failure due to its internal allocation failures.

Hence, if the kdamond is stopped by the above three events, the values of the status parameters can be stale. Users could show the stale values and be confused. This is already bad, but the real consequence is worse. DAMON_RECLAIM avoids unnecessary damon_start() and damon_stop() calls based on the 'enabled' parameter value. And the update of 'enabled' parameter value depends on the damon_start() and damon_stop() call results. Hence, once the kdamond has stopped by the unintentional events, the user cannot restart the kdamond before the system reboot. For example, the issue can be reproduced via below steps.

    # cd /sys/module/damon_reclaim/parameters
    #
    # # start DAMON_RECLAIM
    # echo Y > enabled
    # ps -ef | grep kdamond
    root         806       2  0 17:53 ?        00:00:00 [kdamond.0]
    root         808     803  0 17:53 pts/4    00:00:00 grep kdamond
    #
    # # commit wrong input to stop kdamond withou explicit stop request
    # echo 3 > addr_unit
    # echo Y > commit_inputs
    bash: echo: write error: Invalid argument
    #
    # # confirm kdamond is stopped
    # ps -ef | grep kdamond
    root         811     803  0 17:53 pts/4    00:00:00 grep kdamond
    #
    # # users casn now show stable status
    # cat enabled
    Y
    # cat kdamond_pid
    806
    #
    # # even after fixing the wrong parameter,
    # # kdamond cannot be restarted.
    # echo 1 > addr_unit
    # echo Y > enabled
    # ps -ef | grep kdamond
    root         815     803  0 17:54 pts/4    00:00:00 grep kdamond

The problem will only rarely happen in real and common setups for the following reasons. The allocation failures are unlikely in such setups since those allocations are arguably too small to fail. Also sane users on real production environments may not commit wrong input parameters. But once it happens, the consequence is quite bad. And the bug is a bug.

The issue stems from the fact that there are multiple events that can change the status, and following all the events is challenging. Dynamically detect and use the fresh status for the parameters when those are requested.

Fixes e035c280f6df ("mm/damon/reclaim: support online inputs update")
Cc <[email protected]> # 5.19.x
---
 mm/damon/reclaim.c | 85 ++++++++++++++++++++++++++++++----------------
 1 file changed, 55 insertions(+), 30 deletions(-)
mm/damon/reclaim.c +55 -30
index 86da147786583..fe7fce26cf6ce 100644
@@ -144,15 +144,6 @@ static unsigned long addr_unit __read_mostly = 1;
 static bool skip_anon __read_mostly;
 module_param(skip_anon, bool, 0600);
 
-/*
- * PID of the DAMON thread
- *
- * If DAMON_RECLAIM is enabled, this becomes the PID of the worker thread.
- * Else, -1.
- */
-static int kdamond_pid __read_mostly = -1;
-module_param(kdamond_pid, int, 0400);
-
 static struct damos_stat damon_reclaim_stat;
 DEFINE_DAMON_MODULES_DAMOS_STATS_PARAMS(damon_reclaim_stat,
 		reclaim_tried_regions, reclaimed_regions, quota_exceeds);
@@ -288,12 +279,8 @@ static int damon_reclaim_turn(bool on)
 {
 	int err;
 
-	if (!on) {
-		err = damon_stop(&ctx, 1);
-		if (!err)
-			kdamond_pid = -1;
-		return err;
-	}
+	if (!on)
+		return damon_stop(&ctx, 1);
 
 	err = damon_reclaim_apply_parameters();
 	if (err)
@@ -302,9 +289,6 @@ static int damon_reclaim_turn(bool on)
 	err = damon_start(&ctx, 1, true);
 	if (err)
 		return err;
-	kdamond_pid = damon_kdamond_pid(ctx);
-	if (kdamond_pid < 0)
-		return kdamond_pid;
 	return damon_call(ctx, &call_control);
 }
 
@@ -332,42 +316,83 @@ module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600);
 MODULE_PARM_DESC(addr_unit,
 	"Scale factor for DAMON_RECLAIM to ops address conversion (default: 1)");
 
+static bool damon_reclaim_enabled(void)
+{
+	if (!ctx)
+		return false;
+	return damon_is_running(ctx);
+}
+
 static int damon_reclaim_enabled_store(const char *val,
 		const struct kernel_param *kp)
 {
-	bool is_enabled = enabled;
-	bool enable;
 	int err;
 
-	err = kstrtobool(val, &enable);
+	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enable)
+	if (damon_reclaim_enabled() == enabled)
 		return 0;
 
 	/* Called before init function.  The function will handle this. */
 	if (!damon_initialized())
-		goto set_param_out;
+		return 0;
 
-	err = damon_reclaim_turn(enable);
-	if (err)
-		return err;
+	return damon_reclaim_turn(enabled);
+}
 
-set_param_out:
-	enabled = enable;
-	return err;
+static int damon_reclaim_enabled_load(char *buffer,
+		const struct kernel_param *kp)
+{
+	return sprintf(buffer, "%c\n", damon_reclaim_enabled() ? 'Y' : 'N');
 }
 
 static const struct kernel_param_ops enabled_param_ops = {
 	.set = damon_reclaim_enabled_store,
-	.get = param_get_bool,
+	.get = damon_reclaim_enabled_load,
 };
 
 module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
 MODULE_PARM_DESC(enabled,
 	"Enable or disable DAMON_RECLAIM (default: disabled)");
 
+static int damon_reclaim_kdamond_pid_store(const char *val,
+		const struct kernel_param *kp)
+{
+	/*
+	 * kdamond_pid is read-only, but kernel command line could write it.
+	 * Do nothing here.
+	 */
+	return 0;
+}
+
+static int damon_reclaim_kdamond_pid_load(char *buffer,
+		const struct kernel_param *kp)
+{
+	int kdamond_pid = -1;
+
+	if (ctx) {
+		kdamond_pid = damon_kdamond_pid(ctx);
+		if (kdamond_pid < 0)
+			kdamond_pid = -1;
+	}
+	return sprintf(buffer, "%d\n", kdamond_pid);
+}
+
+static const struct kernel_param_ops kdamond_pid_param_ops = {
+	.set = damon_reclaim_kdamond_pid_store,
+	.get = damon_reclaim_kdamond_pid_load,
+};
+
+/*
+ * PID of the DAMON thread
+ *
+ * If DAMON_RECLAIM is enabled, this becomes the PID of the worker thread.
+ * Else, -1.
+ */
+module_param_cb(kdamond_pid, &kdamond_pid_param_ops, NULL, 0400);
+
 static int __init damon_reclaim_init(void)
 {
 	int err;
Signature · 1 line
2.47.3
#2 lore raw

Re: [PATCH v3 1/3] mm/damon/reclaim: detect and use fresh enabled and kdamond_pid values

Sashiko AI review found 1 potential issue(s): - [High] Writing to the `enabled` sysfs parameter can cause a NULL pointer dereference if `damon_reclaim_init()` failed during boot. --

Sashiko AI review found 1 potential issue(s):
- [High] Writing to the enabled sysfs parameter can cause a NULL pointer dereference if damon_reclaim_init() failed during boot. --

commit d88cdacb9c44c0e85a70a745ea16a47c24b53355
Author: SeongJae Park <[email protected]>

mm/damon/reclaim: detect and use fresh enabled and kdamond_pid values

This commit fixes an issue where the 'enabled' and 'kdamond_pid' parameter values can become stale if the kdamond thread stops unintentionally. It updates the parameter accessors to dynamically detect and return the fresh status from the DAMON context.

Cc <[email protected]> # 5.19.x
Quoted 4 lines
mm/damon/reclaim.c +0 -0
index 86da147786583..fe7fce26cf6ce 100644

[ ... ]

Quoted 26 lines
mm/damon/reclaim.c +9 -4
@@ -332,42 +316,83 @@ module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600);
 MODULE_PARM_DESC(addr_unit,
 	"Scale factor for DAMON_RECLAIM to ops address conversion (default: 1)");
 
+static bool damon_reclaim_enabled(void)
+{
+	if (!ctx)
+		return false;
+	return damon_is_running(ctx);
+}
+
 static int damon_reclaim_enabled_store(const char *val,
 		const struct kernel_param *kp)
 {
-	bool is_enabled = enabled;
-	bool enable;
 	int err;
 
-	err = kstrtobool(val, &enable);
+	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enable)
+	if (damon_reclaim_enabled() == enabled)
 		return 0;

mm/damon/reclaim.c module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600); · line 342

Quoted 10 lines
mm/damon/reclaim.c +2 -4
 
 	/* Called before init function.  The function will handle this. */
 	if (!damon_initialized())
-		goto set_param_out;
+		return 0;
 
-	err = damon_reclaim_turn(enable);
-	if (err)
-		return err;
+	return damon_reclaim_turn(enabled);

Can this result in a NULL pointer dereference if damon_reclaim_init() fails during boot?

This issue existed previously and appears to still be present. If the init function fails (for example, due to a memory allocation failure), the global ctx pointer remains NULL.

If a user later writes 'Y' to the enabled sysfs parameter, the following sequence seems possible:

damon_reclaim_enabled_store()
damon_reclaim_enabled() returns false because ctx is NULL damon_initialized() returns true because the core framework initialized damon_reclaim_turn(true)
damon_reclaim_apply_parameters()
damon_commit_ctx(ctx, param_ctx)

And then inside damon_commit_ctx():

int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)

{
    ...
    dst->maybe_corrupted = true;
    ...
}

Since dst is the global ctx pointer, could this crash the system if a local user writes to the sysfs parameter when ctx was never allocated?

Quoted 4 lines
mm/damon/reclaim.c +1 -3
-set_param_out:
-	enabled = enable;
-	return err;
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
author maintainer
#3 lore raw

Re: [PATCH v3 1/3] mm/damon/reclaim: detect and use fresh enabled and kdamond_pid values

Quoted 2 lines · On Sun, 19 Apr 2026 16:48:47 +0000 [email protected] wrote:

Sashiko AI review found 1 potential issue(s):
- [High] Writing to the enabled sysfs parameter can cause a NULL pointer dereference if damon_reclaim_init() failed during boot.

As I replied [1] on the previous version, it is not caused by this patch. I'm separately working on it.

[1] https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]

author maintainer
#4 lore raw

PATCH v3 2/3 mm/damon/lru_sort: detect and use fresh enabled and kdamond_pid values

In reply to SeongJae Park

DAMON_LRU_SORT updates 'enabled' and 'kdamond_pid' parameter values, which represents the running status of its kdamond, when the user explicitly requests start/stop of the kdamond. The kdamond can, however, be stopped in events other than the explicit user request in the following three events.

1. ctx->regions_score_histogram allocation failure at beginning of the execution,
2. damon_commit_ctx() failure due to invalid user input, and
3. damon_commit_ctx() failure due to its internal allocation failures.

Hence, if the kdamond is stopped by the above three events, the values of the status parameters can be stale. Users could show the stale values and be confused. This is already bad, but the real consequence is worse. DAMON_LRU_SORT avoids unnecessary damon_start() and damon_stop() calls based on the 'enabled' parameter value. And the update of 'enabled' parameter value depends on the damon_start() and damon_stop() call results. Hence, once the kdamond has stopped by the unintentional events, the user cannot restart the kdamond before the system reboot. For example, the issue can be reproduced via below steps.

    # cd /sys/module/damon_lru_sort/parameters
    #
    # # start DAMON_LRU_SORT
    # echo Y > enabled
    # ps -ef | grep kdamond
    root         806       2  0 17:53 ?        00:00:00 [kdamond.0]
    root         808     803  0 17:53 pts/4    00:00:00 grep kdamond
    #
    # # commit wrong input to stop kdamond withou explicit stop request
    # echo 3 > addr_unit
    # echo Y > commit_inputs
    bash: echo: write error: Invalid argument
    #
    # # confirm kdamond is stopped
    # ps -ef | grep kdamond
    root         811     803  0 17:53 pts/4    00:00:00 grep kdamond
    #
    # # users casn now show stable status
    # cat enabled
    Y
    # cat kdamond_pid
    806
    #
    # # even after fixing the wrong parameter,
    # # kdamond cannot be restarted.
    # echo 1 > addr_unit
    # echo Y > enabled
    # ps -ef | grep kdamond
    root         815     803  0 17:54 pts/4    00:00:00 grep kdamond

The problem will only rarely happen in real and common setups for the following reasons. The allocation failures are unlikely in such setups since those allocations are arguably too small to fail. Also sane users on real production environments may not commit wrong input parameters. But once it happens, the consequence is quite bad. And the bug is a bug.

The issue stems from the fact that there are multiple events that can change the status, and following all the events is challenging. Dynamically detect and use the fresh status for the parameters when those are requested.

Fixes 40e983cca927 ("mm/damon: introduce DAMON-based LRU-lists Sorting")
Cc <[email protected]> # 6.0.x
---
 mm/damon/lru_sort.c | 85 +++++++++++++++++++++++++++++----------------
 1 file changed, 55 insertions(+), 30 deletions(-)
mm/damon/lru_sort.c +55 -30
index 554559d729760..8494040b1ee48 100644
@@ -161,15 +161,6 @@ module_param(monitor_region_end, ulong, 0600);
  */
 static unsigned long addr_unit __read_mostly = 1;
 
-/*
- * PID of the DAMON thread
- *
- * If DAMON_LRU_SORT is enabled, this becomes the PID of the worker thread.
- * Else, -1.
- */
-static int kdamond_pid __read_mostly = -1;
-module_param(kdamond_pid, int, 0400);
-
 static struct damos_stat damon_lru_sort_hot_stat;
 DEFINE_DAMON_MODULES_DAMOS_STATS_PARAMS(damon_lru_sort_hot_stat,
 		lru_sort_tried_hot_regions, lru_sorted_hot_regions,
@@ -386,12 +377,8 @@ static int damon_lru_sort_turn(bool on)
 {
 	int err;
 
-	if (!on) {
-		err = damon_stop(&ctx, 1);
-		if (!err)
-			kdamond_pid = -1;
-		return err;
-	}
+	if (!on)
+		return damon_stop(&ctx, 1);
 
 	err = damon_lru_sort_apply_parameters();
 	if (err)
@@ -400,9 +387,6 @@ static int damon_lru_sort_turn(bool on)
 	err = damon_start(&ctx, 1, true);
 	if (err)
 		return err;
-	kdamond_pid = damon_kdamond_pid(ctx);
-	if (kdamond_pid < 0)
-		return kdamond_pid;
 	return damon_call(ctx, &call_control);
 }
 
@@ -430,42 +414,83 @@ module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600);
 MODULE_PARM_DESC(addr_unit,
 	"Scale factor for DAMON_LRU_SORT to ops address conversion (default: 1)");
 
+static bool damon_lru_sort_enabled(void)
+{
+	if (!ctx)
+		return false;
+	return damon_is_running(ctx);
+}
+
 static int damon_lru_sort_enabled_store(const char *val,
 		const struct kernel_param *kp)
 {
-	bool is_enabled = enabled;
-	bool enable;
 	int err;
 
-	err = kstrtobool(val, &enable);
+	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enable)
+	if (damon_lru_sort_enabled() == enabled)
 		return 0;
 
 	/* Called before init function.  The function will handle this. */
 	if (!damon_initialized())
-		goto set_param_out;
+		return 0;
 
-	err = damon_lru_sort_turn(enable);
-	if (err)
-		return err;
+	return damon_lru_sort_turn(enabled);
+}
 
-set_param_out:
-	enabled = enable;
-	return err;
+static int damon_lru_sort_enabled_load(char *buffer,
+		const struct kernel_param *kp)
+{
+	return sprintf(buffer, "%c\n", damon_lru_sort_enabled() ? 'Y' : 'N');
 }
 
 static const struct kernel_param_ops enabled_param_ops = {
 	.set = damon_lru_sort_enabled_store,
-	.get = param_get_bool,
+	.get = damon_lru_sort_enabled_load,
 };
 
 module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
 MODULE_PARM_DESC(enabled,
 	"Enable or disable DAMON_LRU_SORT (default: disabled)");
 
+static int damon_lru_sort_kdamond_pid_store(const char *val,
+		const struct kernel_param *kp)
+{
+	/*
+	 * kdamond_pid is read-only, but kernel command line could write it.
+	 * Do nothing here.
+	 */
+	return 0;
+}
+
+static int damon_lru_sort_kdamond_pid_load(char *buffer,
+		const struct kernel_param *kp)
+{
+	int kdamond_pid = -1;
+
+	if (ctx) {
+		kdamond_pid = damon_kdamond_pid(ctx);
+		if (kdamond_pid < 0)
+			kdamond_pid = -1;
+	}
+	return sprintf(buffer, "%d\n", kdamond_pid);
+}
+
+static const struct kernel_param_ops kdamond_pid_param_ops = {
+	.set = damon_lru_sort_kdamond_pid_store,
+	.get = damon_lru_sort_kdamond_pid_load,
+};
+
+/*
+ * PID of the DAMON thread
+ *
+ * If DAMON_LRU_SORT is enabled, this becomes the PID of the worker thread.
+ * Else, -1.
+ */
+module_param_cb(kdamond_pid, &kdamond_pid_param_ops, NULL, 0400);
+
 static int __init damon_lru_sort_init(void)
 {
 	int err;
Signature · 1 line
2.47.3
#5 lore raw

Re: [PATCH v3 2/3] mm/damon/lru_sort: detect and use fresh enabled and kdamond_pid values

Sashiko AI review found 1 potential issue(s): - [High] Loading the module with the `enabled=Y` parameter causes a NULL pointer dereference. --

Sashiko AI review found 1 potential issue(s):
- [High] Loading the module with the enabled=Y parameter causes a NULL pointer dereference. --

commit e25c111d55f5d7c99165dbc8b59a5261d640ada5
Author: SeongJae Park <[email protected]>
mm/damon/lru_sort: detect and use fresh enabled and kdamond_pid values

This commit updates DAMON_LRU_SORT to dynamically detect and use the current running status of the kdamond thread for the enabled and kdamond_pid parameters. This prevents users from seeing stale values or being unable to restart kdamond if it stops due to unintentional events.

Quoted 4 lines
mm/damon/lru_sort.c +0 -0
index 554559d729760..8494040b1ee48 100644

[ ... ]

Quoted 16 lines

static int damon_lru_sort_enabled_store(const char *val,

mm/damon/lru_sort.c +2 -4
 		const struct kernel_param *kp)
 {
-	bool is_enabled = enabled;
-	bool enable;
 	int err;
 
-	err = kstrtobool(val, &enable);
+	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enable)
+	if (damon_lru_sort_enabled() == enabled)
 		return 0;
 

mm/damon/lru_sort.c module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600); · line 441

Quoted 10 lines
mm/damon/lru_sort.c +3 -4
 	/* Called before init function.  The function will handle this. */
 	if (!damon_initialized())
-		goto set_param_out;
+		return 0;
 
-	err = damon_lru_sort_turn(enable);
-	if (err)
-		return err;
+	return damon_lru_sort_turn(enabled);
+}

This isn't a bug introduced by this patch, but does this code cause a NULL pointer dereference if the module is dynamically loaded with enabled=Y?

When the module is loaded as a dynamically loadable module with the enabled=Y parameter, the kernel's module loader parses the parameters before damon_lru_sort_init() runs.

This invokes damon_lru_sort_enabled_store(). Because damon_core (a module dependency) is already loaded, damon_initialized() evaluates to true, bypassing the safety guard.

The function proceeds to call damon_lru_sort_turn(true), which eventually calls damon_lru_sort_apply_parameters(). This invokes:

    damon_commit_ctx(ctx, param_ctx);

At this point, the global ctx pointer is still NULL because it is only allocated later in damon_lru_sort_init(). damon_commit_ctx() unconditionally dereferences the pointer:

    dst->maybe_corrupted = true;

causing a direct NULL pointer dereference and a kernel panic.

Should this check use !ctx instead of !damon_initialized() to properly defer execution to the init function?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author maintainer
#6 lore raw

Re: [PATCH v3 2/3] mm/damon/lru_sort: detect and use fresh enabled and kdamond_pid values

Quoted 2 lines · On Sun, 19 Apr 2026 17:28:17 +0000 [email protected] wrote:

Sashiko AI review found 1 potential issue(s):
- [High] Loading the module with the enabled=Y parameter causes a NULL pointer dereference.

This is invalid. Please read below for more details.

Quoted 39 lines

--

commit e25c111d55f5d7c99165dbc8b59a5261d640ada5
Author: SeongJae Park <[email protected]>
mm/damon/lru_sort: detect and use fresh enabled and kdamond_pid values

This commit updates DAMON_LRU_SORT to dynamically detect and use the current running status of the kdamond thread for the enabled and kdamond_pid parameters. This prevents users from seeing stale values or being unable to restart kdamond if it stops due to unintentional events.

Quoted 4 lines
mm/damon/lru_sort.c +0 -0
index 554559d729760..8494040b1ee48 100644

[ ... ]

Quoted 21 lines

static int damon_lru_sort_enabled_store(const char *val,

mm/damon/lru_sort.c +3 -5
 		const struct kernel_param *kp)
 {
-	bool is_enabled = enabled;
-	bool enable;
 	int err;
 
-	err = kstrtobool(val, &enable);
+	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enable)
+	if (damon_lru_sort_enabled() == enabled)
 		return 0;
 
 	/* Called before init function.  The function will handle this. */
 	if (!damon_initialized())
-		goto set_param_out;
+		return 0;
Quoted 8 lines

mm/damon/lru_sort.c module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600); · line 441

Quoted 5 lines
mm/damon/lru_sort.c +2 -3
-	err = damon_lru_sort_turn(enable);
-	if (err)
-		return err;
+	return damon_lru_sort_turn(enabled);
+}

This isn't a bug introduced by this patch, but does this code cause a NULL pointer dereference if the module is dynamically loaded with enabled=Y?

DAMON_LRU_SORT is builtin module. It cannot be dynmically loaded.

Quoted 14 lines

When the module is loaded as a dynamically loadable module with the enabled=Y parameter, the kernel's module loader parses the parameters before damon_lru_sort_init() runs.

This invokes damon_lru_sort_enabled_store(). Because damon_core (a module dependency) is already loaded, damon_initialized() evaluates to true, bypassing the safety guard.

The function proceeds to call damon_lru_sort_turn(true), which eventually calls damon_lru_sort_apply_parameters(). This invokes:

    damon_commit_ctx(ctx, param_ctx);
Quoted 7 lines

At this point, the global ctx pointer is still NULL because it is only allocated later in damon_lru_sort_init(). damon_commit_ctx() unconditionally dereferences the pointer:

    dst->maybe_corrupted = true;

causing a direct NULL pointer dereference and a kernel panic.

Because DAMON_LRU_SORT cannot be dynamically loaded, the scenario cannot happen.

Nonetheless, a similar issue can happen when the init function fails, which is unlikely, but theoretically possible. But that's not caused by this patch, so no blocker of this patch. I'm separately working on it.

Thanks,
SJ

[...]

author maintainer
#7 lore raw

PATCH v3 3/3 mm/damon/stat: detect and use fresh enabled value

In reply to SeongJae Park

DAMON_STAT updates 'enabled' parameter value, which represents the running status of its kdamond, when the user explicitly requests start/stop of the kdamond. The kdamond can, however, be stopped even if the user explicitly requested the stop, if ctx->regions_score_histogram allocation failure at beginning of the execution of the kdamond. Hence, if the kdamond is stopped by the allocation failure, the value of the parameter can be stale.

Users could show the stale value and be confused. The problem will only rarely happen in real and common setups because the allocation is arguably too small to fail. Also, unlike the similar bugs that are now fixed in DAMON_RECLAIM and DAMON_LRU_SORT, kdamond can be restarted in this case, because DAMON_STAT force-updates the enabled parameter value for user inputs. The bug is a bug, though.

The issue stems from the fact that there are multiple events that can change the status, and following all the events is challenging. Dynamically detect and use the fresh status for the parameters when those are requested.

The issue was dicovered [1] by Sashiko.

[1] https://lore.kernel.org/[email protected]

Fixes 369c415e6073 ("mm/damon: introduce DAMON_STAT module")
Cc <[email protected]> # 6.17.x
---
 mm/damon/stat.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)
mm/damon/stat.c +20 -10
index 99ba346f9e325..3951b762cbddf 100644
@@ -19,14 +19,17 @@
 static int damon_stat_enabled_store(
 		const char *val, const struct kernel_param *kp);
 
+static int damon_stat_enabled_load(char *buffer,
+		const struct kernel_param *kp);
+
 static const struct kernel_param_ops enabled_param_ops = {
 	.set = damon_stat_enabled_store,
-	.get = param_get_bool,
+	.get = damon_stat_enabled_load,
 };
 
 static bool enabled __read_mostly = IS_ENABLED(
 	CONFIG_DAMON_STAT_ENABLED_DEFAULT);
-module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
+module_param_cb(enabled, &enabled_param_ops, NULL, 0600);
 MODULE_PARM_DESC(enabled, "Enable of disable DAMON_STAT");
 
 static unsigned long estimated_memory_bandwidth __read_mostly;
@@ -273,17 +276,23 @@ static void damon_stat_stop(void)
 	damon_stat_context = NULL;
 }
 
+static bool damon_stat_enabled(void)
+{
+	if (!damon_stat_context)
+		return false;
+	return damon_is_running(damon_stat_context);
+}
+
 static int damon_stat_enabled_store(
 		const char *val, const struct kernel_param *kp)
 {
-	bool is_enabled = enabled;
 	int err;
 
 	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enabled)
+	if (damon_stat_enabled() == enabled)
 		return 0;
 
 	if (!damon_initialized())
@@ -293,16 +302,17 @@ static int damon_stat_enabled_store(
 		 */
 		return 0;
 
-	if (enabled) {
-		err = damon_stat_start();
-		if (err)
-			enabled = false;
-		return err;
-	}
+	if (enabled)
+		return damon_stat_start();
 	damon_stat_stop();
 	return 0;
 }
 
+static int damon_stat_enabled_load(char *buffer, const struct kernel_param *kp)
+{
+	return sprintf(buffer, "%c\n", damon_stat_enabled() ? 'Y' : 'N');
+}
+
 static int __init damon_stat_init(void)
 {
 	int err = 0;
Signature · 1 line
2.47.3
#8 lore raw

Re: [PATCH v3 3/3] mm/damon/stat: detect and use fresh enabled value

Sashiko AI review found 2 potential issue(s): - [Critical] Passing `enabled=Y` during module load triggers a double-start, failing initialization and causing a Use-After-Free kernel panic. - [High]…

Sashiko AI review found 2 potential issue(s):
- [Critical] Passing enabled=Y during module load triggers a double-start, failing initialization and causing a Use-After-Free kernel panic.
- [High] Resource leak of damon_stat_context when explicitly disabling the module after an internal kdamond crash.
--

commit 16eae8a1bf380cdb61d4dd6b62187dd09f8ce859
Author: SeongJae Park <[email protected]>

mm/damon/stat: detect and use fresh enabled value

This commit updates the DAMON_STAT module to dynamically detect the running status of its kdamond thread rather than relying on a cached parameter value.

Quoted 4 lines
mm/damon/stat.c +0 -0
index 99ba346f9e325..3951b762cbddf 100644

[ ... ]

Quoted 14 lines
mm/damon/stat.c +7 -0
@@ -273,17 +276,23 @@ static void damon_stat_stop(void)
 	damon_stat_context = NULL;
 }
 
+static bool damon_stat_enabled(void)
+{
+	if (!damon_stat_context)
+		return false;
+	return damon_is_running(damon_stat_context);
+}
+
 static int damon_stat_enabled_store(
 		const char *val, const struct kernel_param *kp)
 {

mm/damon/stat.c static void damon_stat_stop(void) · line 296

Quoted 10 lines
mm/damon/stat.c +1 -2
-	bool is_enabled = enabled;
 	int err;
 
 	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enabled)
+	if (damon_stat_enabled() == enabled)
 		return 0;

If kdamond terminates asynchronously (for example, due to an allocation failure), damon_stat_enabled() will return false.

If a user subsequently disables the module by writing 'N' to the enabled parameter, this condition (false == false) evaluates to true and the function returns 0.

Does this skip the call to damon_stat_stop() and leak the damon_stat_context since damon_destroy_ctx() won't be called?

Quoted 4 lines
mm/damon/stat.c +0 -0
 	if (!damon_initialized())
@@ -293,16 +302,17 @@ static int damon_stat_enabled_store(
 		 */

mm/damon/stat.c static int damon_stat_enabled_store( · line 306

Quoted 10 lines
mm/damon/stat.c +2 -6
 		return 0;
 
-	if (enabled) {
-		err = damon_stat_start();
-		if (err)
-			enabled = false;
-		return err;
-	}
+	if (enabled)
+		return damon_stat_start();

During module load via "insmod damon_stat.ko enabled=Y", parse_args() calls this store function. Because damon_stat_enabled() dynamically returns false at this point, the code proceeds to call damon_stat_start() to start the kdamond thread.

Shortly after, the module initialization function damon_stat_init() runs, sees enabled == true, and calls damon_stat_start() again. This second call returns -EAGAIN because the context is already running.

The module initialization then fails, causing the kernel to unload the module and free its memory.

Since the kdamond thread is still actively running in the background, can this cause a Use-After-Free when the thread accesses the freed module memory?

While this issue previously existed if CONFIG_DAMON_STAT_ENABLED_DEFAULT=n, does this patch introduce the regression for the default configuration as well?

Quoted 3 lines
mm/damon/stat.c +0 -0
 	damon_stat_stop();
 	return 0;
 }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author maintainer
#9 lore raw

Re: [PATCH v3 3/3] mm/damon/stat: detect and use fresh enabled value

Quoted 3 lines · On Sun, 19 Apr 2026 17:51:20 +0000 [email protected] wrote:

Sashiko AI review found 2 potential issue(s):
- [Critical] Passing enabled=Y during module load triggers a double-start, failing initialization and causing a Use-After-Free kernel panic.
- [High] Resource leak of damon_stat_context when explicitly disabling the module after an internal kdamond crash.

Both are wrong. Please read below for more details.

Quoted 46 lines

--

commit 16eae8a1bf380cdb61d4dd6b62187dd09f8ce859
Author: SeongJae Park <[email protected]>

mm/damon/stat: detect and use fresh enabled value

This commit updates the DAMON_STAT module to dynamically detect the running status of its kdamond thread rather than relying on a cached parameter value.

Quoted 4 lines
mm/damon/stat.c +0 -0
index 99ba346f9e325..3951b762cbddf 100644

[ ... ]

Quoted 14 lines
mm/damon/stat.c +7 -0
@@ -273,17 +276,23 @@ static void damon_stat_stop(void)
 	damon_stat_context = NULL;
 }
 
+static bool damon_stat_enabled(void)
+{
+	if (!damon_stat_context)
+		return false;
+	return damon_is_running(damon_stat_context);
+}
+
 static int damon_stat_enabled_store(
 		const char *val, const struct kernel_param *kp)
 {

mm/damon/stat.c static void damon_stat_stop(void) · line 296

Quoted 10 lines
mm/damon/stat.c +1 -2
-	bool is_enabled = enabled;
 	int err;
 
 	err = kstrtobool(val, &enabled);
 	if (err)
 		return err;
 
-	if (is_enabled == enabled)
+	if (damon_stat_enabled() == enabled)
 		return 0;

If kdamond terminates asynchronously (for example, due to an allocation failure), damon_stat_enabled() will return false.

Quoted 6 lines

If a user subsequently disables the module by writing 'N' to the enabled parameter, this condition (false == false) evaluates to true and the function returns 0.

Does this skip the call to damon_stat_stop() and leak the damon_stat_context since damon_destroy_ctx() won't be called?

I previously explained [1] it is not leaked.

Quoted 28 lines
Quoted 5 lines
mm/damon/stat.c +0 -0
 	if (!damon_initialized())
@@ -293,16 +302,17 @@ static int damon_stat_enabled_store(
 		 */

mm/damon/stat.c static int damon_stat_enabled_store( · line 306

Quoted 10 lines
mm/damon/stat.c +2 -6
 		return 0;
 
-	if (enabled) {
-		err = damon_stat_start();
-		if (err)
-			enabled = false;
-		return err;
-	}
+	if (enabled)
+		return damon_stat_start();

During module load via "insmod damon_stat.ko enabled=Y", parse_args() calls this store function. Because damon_stat_enabled() dynamically returns false at this point, the code proceeds to call damon_stat_start() to start the kdamond thread.

Shortly after, the module initialization function damon_stat_init() runs, sees enabled == true, and calls damon_stat_start() again. This second call returns -EAGAIN because the context is already running.

The module initialization then fails, causing the kernel to unload the module and free its memory.

Quoted 7 lines

Since the kdamond thread is still actively running in the background, can this cause a Use-After-Free when the thread accesses the freed module memory?

While this issue previously existed if CONFIG_DAMON_STAT_ENABLED_DEFAULT=n, does this patch introduce the regression for the default configuration as well?

DAMON_STAT is a builtin module. Users cannot load it dynamically, and therefore the above scenario cannot happen.

[1] https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]