mm/damon: avoid division by zero from damos_quota_score()
Subsystems (3)
SJ Park
maintainerv2 cover mm/damon: avoid division by zero from damos_quota_score()
DAMON_SAMPLE_MTIER and DAMON_LRU_SORT allow the user to trigger division by zero in damos_quota_score(). Avoid it by adding parameters validation checks.
Changes from RFC
- RFC: https://lore.kernel.org/[email protected]
- Add a fix for DAMON_LRU_SORT.
SJ Park (2):
samples/damon/mtier: error out for zero quota goal target values mm/damon/lru_sort: error out for >10000 active_mem_bp
mm/damon/lru_sort.c | 2 ++ samples/damon/mtier.c | 3 +++ 2 files changed, 5 insertions(+)
base-commit: 51d61cb34d137b13957ce83455c1f596d39e8cfe
Signature · 1 line
2.47.3
SJ Park
maintainerRFC PATCH v2 1/2 samples/damon/mtier: error out for zero quota goal target values
damos_quota_score() can trigger division by zero if the target_value is zero. DAMON_SAMPLE_MTIER lets users set the target_value via node0_mem_{used,free}_bp parameters. It doesn't guard zero value case, though. As a result, users can trigger division by zero. Fix the issue by returning an error when the user tries to start DAMON with zero node0_mem_{used,free}_bp parameter values.
DAMON_SAMPLE_MTIER is just a sample module, but the consequence is quite bad. Also the zero node0_mem_free_bp parameter might look like a reasonable setup to some users. Hence, the issue might really happen in the real world.
One reliable way to reproduce the issue is like below:
# cd /sys/module/damon_sample_mtier/parameters
# echo 4096 > node0_start_addr
# echo 8192 > node0_end_addr
# echo 8192 > node1_start_addr
# echo 81920 > node1_end_addr
# echo 0 > node0_mem_free_bp
# echo Y > enabled
# dmesg -w
[...]
[18792.235916] Oops: divide error: 0000 [#1] SMP NOPTI
[...]
[18792.242787] RIP: 0010:damos_quota_score+0x6f/0x480
[...]This issue was discovered [1] by Sashiko.
[1] https://lore.kernel.org/[email protected]
--- samples/damon/mtier.c | 3 +++ 1 file changed, 3 insertions(+)
samples/damon/mtier.c +3 -0
@@ -156,6 +156,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)if (!scheme) goto free_out;damon_set_schemes (ctx, &scheme,1 ); +/* zero target value causes division by zero in damos_quota_store() */ +if (!node0_mem_used_bp || !node0_mem_free_bp) + goto free_out; quota_goal = damos_new_quota_goal( promote ? DAMOS_QUOTA_NODE_MEM_USED_BP : DAMOS_QUOTA_NODE_MEM_FREE_BP,
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [RFC PATCH v2 1/2] samples/damon/mtier: error out for zero quota goal target values
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Low] The newly added comment incorrectly refers to damos_quota_store() instead of damos_quota_score().
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the division-by-zero vulnerability is only patched at the module boundary in mtier.c. The unvalidated core API (damos_new_quota_goal and damos_quota_score) remains vulnerable and is still exploitable via other DAMON components. For instance, in mm/damon/lru_sort.c, an unchecked active_mem_bp module parameter can be set via sysfs such that it evaluates to 0, causing a kernel panic. --
--- Patch [1]: [RFC PATCH v2 1/2] samples/damon/mtier: error out for zero quota goal target values --- commit a29006e1d33ea981b557928c622fb8487e53dbc8
Author: SJ Park <[email protected]>samples/damon/mtier: error out for zero quota goal target values
This commit prevents a division-by-zero exception in damos_quota_score() by returning an error when users try to start DAMON with zero values for the node0_mem_used_bp or node0_mem_free_bp parameters.
samples/damon/mtier.c static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) · line 159
Quoted 9 lines
samples/damon/mtier.c +1
-0@@ -156,6 +156,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)if (!scheme) goto free_out;damon_set_schemes (ctx, &scheme,1 ); +/* zero target value causes division by zero in damos_quota_store() */
[Severity: Low]
This isn't a bug, but should this refer to damos_quota_score() instead of damos_quota_store()?
samples/damon/mtier.c static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) · line 162
Quoted 3 lines
samples/damon/mtier.c +2
-0+if (!node0_mem_used_bp || !node0_mem_free_bp) + goto free_out; quota_goal = damos_new_quota_goal(
[Severity: High]
This isn't a bug introduced by this patch, but while this change handles the zero target value at the module boundary in mtier.c, does the unvalidated core API leave other DAMON components vulnerable?
For example, looking at damon_lru_sort_add_quota_goals() in mm/damon/lru_sort.c:
goal = damos_new_quota_goal(DAMOS_QUOTA_INACTIVE_MEM_BP,
10000 - active_mem_bp + 2);
If active_mem_bp is set to 10002 via sysfs, this evaluates to 0.
Because damos_new_quota_goal() in mm/damon/core.c accepts the target_value without validation, this zero value propagates to damos_quota_score() where mult_frac() performs a division by goal->target_value.
Could this cause a division by zero exception leading to a kernel panic? Would it be safer to add validation directly inside damos_new_quota_goal() or damos_quota_score() to protect all consumers?
Quoted 3 lines
samples/damon/mtier.c +0
-0promote ?DAMOS_QUOTA_NODE_MEM_USED_BP : DAMOS_QUOTA_NODE_MEM_FREE_BP,promote ?
node0_mem_used_bp:node0_mem_free_bp);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
SJ Park
maintainerRe: [RFC PATCH v2 1/2] samples/damon/mtier: error out for zero quota goal target values
Quoted 20 lines · On Sun, 02 Aug 2026 16:32:38 +0000 [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Low] The newly added comment incorrectly refers todamos_quota_store()instead ofdamos_quota_score().Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the division-by-zero vulnerability is only patched at the module boundary inmtier.c. The unvalidated core API (damos_new_quota_goalanddamos_quota_score) remains vulnerable and is still exploitable via other DAMON components. For instance, inmm/damon/lru_sort.c, an uncheckedactive_mem_bpmodule parameter can be set via sysfs such that it evaluates to 0, causing a kernel panic. ----- Patch [1]: [RFC PATCH v2 1/2]
samples/damon/mtier: error out for zero quota goal target values --- commita29006e1d33ea981b557928c622fb8487e53dbc8
Author: SJ Park <[email protected]>samples/damon/mtier: error out for zero quota goal target valuesThis commit prevents a division-by-zero exception in
damos_quota_score()by returning an error when users try to start DAMON with zero values for thenode0_mem_used_bpornode0_mem_free_bpparameters.
Quoted 13 lines
samples/damon/mtier.c static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) · line 159
Quoted 9 lines
samples/damon/mtier.c +1
-0@@ -156,6 +156,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)if (!scheme) goto free_out;damon_set_schemes (ctx, &scheme,1 ); +/* zero target value causes division by zero in damos_quota_store() */ [Severity: Low]
This isn't a bug, but should this refer todamos_quota_score()instead ofdamos_quota_store()?
Good catch, it is a typo. I will fix.
Quoted 18 lines
samples/damon/mtier.c static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) · line 162
Quoted 4 lines
samples/damon/mtier.c +2
-0+if (!node0_mem_used_bp || !node0_mem_free_bp) + goto free_out; quota_goal = damos_new_quota_goal([Severity: High]
This isn't a bug introduced by this patch, but while this change handles the zero target value at the module boundary inmtier.c, does the unvalidated core API leave other DAMON components vulnerable?For example, looking at
damon_lru_sort_add_quota_goals()inmm/damon/lru_sort.c:goal = damos_new_quota_goal(DAMOS_QUOTA_INACTIVE_MEM_BP,
10000 -active_mem_bp+ 2);If
active_mem_bpis set to 10002 via sysfs, this evaluates to 0.
Quoted 7 lines
Because
damos_new_quota_goal()inmm/damon/core.caccepts thetarget_valuewithout validation, this zero value propagates todamos_quota_score()wheremult_frac()performs a division bygoal->target_value.Could this cause a division by zero exception leading to a kernel panic? Would it be safer to add validation directly inside
damos_new_quota_goal()ordamos_quota_score()to protect all consumers?
The next patch of this series is fixing the lru_sort bug.
Quoted 7 lines
Quoted 4 lines
samples/damon/mtier.c +0
-0promote ?DAMOS_QUOTA_NODE_MEM_USED_BP : DAMOS_QUOTA_NODE_MEM_FREE_BP,promote ?
node0_mem_used_bp:node0_mem_free_bp);Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Thanks,
SJ
SJ Park
maintainerRFC PATCH v2 2/2 mm/damon/lru_sort: error out for >10000 active_mem_bp
In reply to SJ Parkdamos_quota_score() can trigger division by zero if the target value is zero. DAMON_LRU_SORT lets users set the target value for the hot memory scheme via active_mem_bp parameter. It avoids setting it as the target value if the parameter value is zero. However, it also sets the cold memory scheme with a target value that is calculated as '10000 - active_mem_bp + 2'. Hence, if a user sets active_mem_bp 10002, the cold memory scheme's quota goal target value can be zero. As a result, division by zero can be triggered. Fix by returning an error when the user tries to start DAMON with >10000 active_mem_bp parameter value.
It makes no sense to set active_mem_bp with 10002. It also requires module parameters write permission to reproduce the issue. That said, the consequence is quite bad.
One reliable way to reproduce the issue is like below:
# cd /sys/module/damon_lru_sort/parameters
# echo 1000 > wmarks_high
# echo 995 > wmarks_mid
# echo 0 > wmarks_low
# echo 10002 > active_mem_bp
# echo Y > enabled
# dmesg -w
[...]
[ 597.421247] Oops: divide error: 0000 [#1] SMP NOPTI
[ 597.428848] RIP: 0010:damos_quota_score+0x6f/0x480This issue was discovered [1] by Sashiko.
[1] https://lore.kernel.org/[email protected]
--- mm/damon/lru_sort.c | 2 ++ 1 file changed, 2 insertions(+)
mm/damon/lru_sort.c +2 -0
@@ -233,6 +233,8 @@ static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme,if (!active_mem_bp)return 0 ; +if (10000 < active_mem_bp) +return -EINVAL ; goal =damos_new_quota_goal (DAMOS_QUOTA_ACTIVE_MEM_BP , active_mem_bp);if (!goal)return -ENOMEM ;
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [RFC PATCH v2 2/2] mm/damon/lru_sort: error out for >10000 active_mem_bp
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] The newly added validation check for active_mem_bp can be bypassed via a Time-Of-Check to Time-Of-Use (TOCTOU) race condition, resulting in division by zero.
Pre-existing issues:
- [High] The function damos_get_in_active_mem_bp() triggers a division by zero if the system tracks no active or inactive LRU pages.
- [High] Writing to the commit_inputs module parameter while DAMON is initialized but not running indefinitely suspends the writing process. --
--- Patch [2]: [RFC PATCH v2 2/2] mm/damon/lru_sort: error out for >10000 active_mem_bp --- commit e683bbcbac1ca0611d9909a3bb95074271d3aa04
Author: SJ Park <[email protected]>
mm/damon/lru_sort: error out for >10000 active_mem_bp
damos_quota_score() can trigger division by zero if the target value is zero. DAMON_LRU_SORT lets users set the target value for the hot memory scheme via active_mem_bp parameter. It avoids setting it as the target value if the parameter value is zero. However, it also sets the cold memory scheme with a target value that is calculated as '10000 - active_mem_bp + 2'. Hence, if a user sets active_mem_bp 10002, the cold memory scheme's quota goal target value can be zero. As a result, division by zero can be triggered. Fix by returning an error when the user tries to start DAMON with >10000 active_mem_bp parameter value.
mm/damon/lru_sort.c static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme, · line 238
Quoted 11 lines
mm/damon/lru_sort.c +2
-0@@ -233,6 +233,8 @@ static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme,if (!active_mem_bp)return 0 ; +if (10000 < active_mem_bp) +return -EINVAL ; goal =damos_new_quota_goal (DAMOS_QUOTA_ACTIVE_MEM_BP , active_mem_bp);
[Severity: High]
Since active_mem_bp is a global module parameter, can its value change between the bounds check and the call to damos_new_quota_goal()?
If a concurrent write updates active_mem_bp to 10002 immediately after the check completes, the cold memory scheme's quota goal target value could still end up being zero, resulting in the division by zero this patch aims to prevent.
Would caching the value locally with READ_ONCE() avoid this race?
mm/damon/lru_sort.c static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme, · line 240
Quoted 2 lines
mm/damon/lru_sort.c +0
-0if (!goal)return -ENOMEM ;
While reviewing this code, I also noticed a couple of pre-existing issues.
This isn't a bug introduced by this patch, but in mm/damon/core.c:
mm/damon/core.c:damos_get_in_active_mem_bp() {
...
active = global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_ANON) +
global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE);
inactive = global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_ANON) +
global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE);
total = active + inactive;
if (active_ratio)
return mult_frac(active, 10000, total);
return mult_frac(inactive, 10000, total);
}[Severity: High]
Can mult_frac() trigger a division by zero if total is 0?
If the system tracks no active or inactive LRU pages, or if per-CPU counter drifts clamp the global state to 0, total will evaluate to 0 and be used as the denominator.
This is also a pre-existing issue, but in mm/damon/lru_sort.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);
...
}[Severity: High]
Does damon_call() require the kdamond thread to be actively running?
If the module is initialized but DAMON has not been enabled, it appears damon_call() will queue the request and wait indefinitely on a completion that will never be signaled by the stopped thread, causing the writing process to hang in an uninterruptible sleep state.
Could we check damon_is_running(ctx) before calling damon_call()?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
SJ Park
maintainerRe: [RFC PATCH v2 2/2] mm/damon/lru_sort: error out for >10000 active_mem_bp
Quoted 44 lines · On Sun, 02 Aug 2026 16:36:51 +0000 [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] The newly added validation check foractive_mem_bpcan be bypassed via a Time-Of-Check to Time-Of-Use (TOCTOU) race condition, resulting in division by zero.Pre-existing issues:
- [High] The functiondamos_get_in_active_mem_bp()triggers a division by zero if the system tracks no active or inactive LRU pages.
- [High] Writing to thecommit_inputsmodule parameter while DAMON is initialized but not running indefinitely suspends the writing process. ----- Patch [2]: [RFC PATCH v2 2/2]
mm/damon/lru_sort: error out for >10000active_mem_bp--- commite683bbcbac1ca0611d9909a3bb95074271d3aa04
Author: SJ Park <[email protected]>
mm/damon/lru_sort: error out for >10000active_mem_bp
damos_quota_score()can trigger division by zero if the target value is zero.DAMON_LRU_SORTlets users set the target value for the hot memory scheme viaactive_mem_bpparameter. It avoids setting it as the target value if the parameter value is zero. However, it also sets the cold memory scheme with a target value that is calculated as '10000 -active_mem_bp+ 2'. Hence, if a user setsactive_mem_bp10002, the cold memory scheme's quota goal target value can be zero. As a result, division by zero can be triggered. Fix by returning an error when the user tries to start DAMON with >10000active_mem_bpparameter value.mm/damon/lru_sort.c static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme, · line 238
Quoted 11 lines
mm/damon/lru_sort.c +2
-0@@ -233,6 +233,8 @@ static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme,if (!active_mem_bp)return 0 ; +if (10000 < active_mem_bp) +return -EINVAL ; goal =damos_new_quota_goal (DAMOS_QUOTA_ACTIVE_MEM_BP , active_mem_bp);[Severity: High]
Sinceactive_mem_bpis a global module parameter, can its value change between the bounds check and the call todamos_new_quota_goal()?
Quoted 6 lines
If a concurrent write updates
active_mem_bpto 10002 immediately after the check completes, the cold memory scheme's quota goal target value could still end up being zero, resulting in the division by zero this patch aims to prevent.Would caching the value locally with
READ_ONCE()avoid this race?
Such race cannot happen to my understanding, thanks to the param_lock.
Quoted 20 lines
mm/damon/lru_sort.c static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme, · line 240
Quoted 3 lines
mm/damon/lru_sort.c +0
-0if (!goal)return -ENOMEM ;While reviewing this code, I also noticed a couple of pre-existing issues.
This isn't a bug introduced by this patch, but in
mm/damon/core.c:mm/damon/core.c:damos_get_in_active_mem_bp() { ... active = global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_ANON) + global_node_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE); inactive = global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_ANON) + global_node_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE); total = active + inactive; if (active_ratio) return mult_frac(active, 10000, total); return mult_frac(inactive, 10000, total); }
Quoted 6 lines
[Severity: High]
Canmult_frac()trigger a division by zero if total is 0?If the system tracks no active or inactive LRU pages, or if per-CPU counter drifts clamp the global state to 0, total will evaluate to 0 and be used as the denominator.
Known issue. I'm working on it.
Quoted 20 lines
This is also a pre-existing issue, but in
mm/damon/lru_sort.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); ... }[Severity: High]
Doesdamon_call()require the kdamond thread to be actively running?
Quoted 6 lines
If the module is initialized but DAMON has not been enabled, it appears
damon_call()will queue the request and wait indefinitely on a completion that will never be signaled by the stopped thread, causing the writing process to hang in an uninterruptible sleep state.Could we check
damon_is_running(ctx)before callingdamon_call()?
Nice finding. I will separately work on this.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Thanks,
SJ