[PATCH v5 1/2] mm/damon/core: prevent size quota overflow in the temporal goal tuner

From: SJ Park

Date: Mon Sep 28 2026 - 04:49:35 EST


From: Donggeun Yoo <donggeunyoo.kernel@xxxxxxxxx>

damos_goal_tune_esz_bp_temporal() converts the scheme's size quota
into basis points with "quota->esz_bp = quota->sz * 10000", both
unsigned long, and damos_set_effective_quota() divides the result
back by 10000. quotas/bytes is unbounded; bytes_store() hands it to
kstrtoul() as is.

On 32-bit the product wraps for any size quota above ULONG_MAX /
10000, that is 429496 bytes. A wrapped product below 10000 divides
to a zero effective quota: 429497 gives 0. damos_quota_is_full() is
then true on the first test of every charge window. Other wrapped
values are wrong without being zero: 500000 gives 70503.

Triggering this needs a scheme with a quota goal, the temporal goal
tuner, and a size quota above ULONG_MAX / 10000 -- 429496 bytes on
32-bit, 1844674407370955 on 64-bit. The scheme then makes no
progress for as long as the goal is unachieved, which is easy to
notice, and writing a smaller size quota restores it. Nothing is
corrupted and nothing leaks. This is unlikely to be hit on a tested
setup.

addr_unit does not cover this. It only scales the numbers a paddr
context writes to quotas/bytes, so a large enough scaled value wraps
just the same, and vaddr and fvaddr contexts take raw byte values.

Bound the multiply.

Fixes: af738a6a00c1 ("mm/damon/core: introduce DAMOS_QUOTA_GOAL_TUNER_TEMPORAL")
Cc: <stable@xxxxxxxxxxxxxxx> # 7.1.x
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@xxxxxxxxx>
Reviewed-by: SJ Park <sj@xxxxxxxxxx>
Signed-off-by: SJ Park <sj@xxxxxxxxxx>
---
mm/damon/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/damon/core.c b/mm/damon/core.c
index 0e375f4445fd..60e4233ed23c 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3291,7 +3291,7 @@ static void damos_goal_tune_esz_bp_temporal(struct damon_ctx *c,

if (score >= 10000)
quota->esz_bp = 0;
- else if (quota->sz)
+ else if (quota->sz && quota->sz <= ULONG_MAX / 10000)
quota->esz_bp = quota->sz * 10000;
else
quota->esz_bp = ULONG_MAX;
--
2.47.3