All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: sashiko-reviews@lists.linux.dev
Cc: damon@lists.linux.dev
Subject: Re: [PATCH 1/2] mm/damon/core: cover discrete System RAM areas with per-range regions
Date: Mon, 27 Jul 2026 19:04:32 +0800	[thread overview]
Message-ID: <ca868252-8643-48de-81db-a6f6505397a6@linux.dev> (raw)
In-Reply-To: <20260727100731.BC8B01F000E9@smtp.kernel.org>


On 7/27/26 6:07 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] Time-of-check to time-of-use (TOCTOU) race condition in `damon_find_system_rams()` leads to a heap out-of-bounds write during concurrent memory hotplug.
> - [High] DAMON initialization fails with `-EINVAL` on systems with sub-page System RAM gaps due to alignment overlap in `damon_set_regions()`.
> --
>
> --- Patch [1]: [PATCH 1/2] mm/damon/core: cover discrete System RAM areas with per-range regions ---
> commit 20fb01bf6e8e4cefc6016dfc073fb77c95ca3511
> Author: Jiayuan Chen <jiayuan.chen@shopee.com>
>
> mm/damon/core: cover discrete System RAM areas with per-range regions
>
> This commit changes how DAMON sets default monitoring regions for System RAM.
> Instead of a single region covering all RAM (and the holes between), it now
> creates separate regions for each discrete System RAM area. This excludes
> large memory holes from being monitored.
>
>> diff --git a/mm/damon/core.c b/mm/damon/core.c
>> index 644daf5a16560..365c43271c0c6 100644
>> --- a/mm/damon/core.c
>> +++ b/mm/damon/core.c
> [ ... ]
>
>> +static int damon_find_system_rams(struct damon_addr_range **ranges_out,
>> +				  unsigned long addr_unit)
>> +{
>> +	struct damon_system_ram_range_walk_arg arg = { .addr_unit = addr_unit };
>> +	struct damon_addr_range *ranges;
>>   
>> +	/* First pass: count the coalesced ranges. */
>>   	walk_system_ram_res(0, -1, &arg, damon_system_ram_walk_fn);
>> -	if (!arg.walked)
>> -		return false;
>> -	*start = damon_res_to_core_addr(arg.res.start, addr_unit);
>> -	*end = damon_res_to_core_addr(arg.res.end + 1, addr_unit);
>> -	if (*end <= *start)
>> -		return false;
>> -	return true;
>> +	if (!arg.nr_ranges)
>> +		return 0;
>> +	ranges = kvmalloc_objs(*ranges, arg.nr_ranges, GFP_KERNEL);
>> +	if (!ranges)
>> +		return -ENOMEM;
>> +	/* Second pass: fill in the coalesced ranges. */
>> +	arg.ranges = ranges;
>> +	arg.nr_ranges = 0;
>> +	arg.has_prev = false;
>> +	walk_system_ram_res(0, -1, &arg, damon_system_ram_walk_fn);
> [Severity: High]
> Can this race with memory hotplug events and cause an out-of-bounds write?
>
> walk_system_ram_res() is called twice without holding memory hotplug locks.
> If a new System RAM region is hotplugged between or during the second pass,
> the number of visited regions could exceed the allocated buffer size.
>
> If that happens, damon_system_ram_walk_fn() would write past the end of the
> kvmalloc-allocated buffer:
>
> mm/damon/core.c:damon_system_ram_walk_fn() {
> 	...
> 	if (a->ranges) {
> 		a->ranges[a->nr_ranges].start = start;
> 		a->ranges[a->nr_ranges].end = end;
> 	}
> 	a->nr_ranges++;
> 	...
> }

You're right. The two walk_system_ram_res() calls should be wrapped with

get_online_mems()/put_online_mems() so the resource set cannot change

between the counting and filling passes.



>> +	*ranges_out = ranges;
>> +	return arg.nr_ranges;
>>   }
> [ ... ]
>
>> @@ -3984,14 +4021,25 @@ int damon_set_region_system_rams_default(struct damon_target *t,
>>   			unsigned long addr_unit, unsigned long min_region_sz)
>>   {
>>   	struct damon_addr_range addr_range;
>> +	struct damon_addr_range *ranges;
>> +	int nr_ranges, err;
>>   
>> -	if (!*start && !*end &&
>> -		!damon_find_system_rams_range(start, end, addr_unit))
>> -		return -EINVAL;
>> +	if (*start || *end) {
>> +		addr_range.start = *start;
>> +		addr_range.end = *end;
>> +		return damon_set_regions(t, &addr_range, 1, min_region_sz);
>> +	}
>>   
>> -	addr_range.start = *start;
>> -	addr_range.end = *end;
>> -	return damon_set_regions(t, &addr_range, 1, min_region_sz);
>> +	nr_ranges = damon_find_system_rams(&ranges, addr_unit);
>> +	if (nr_ranges < 0)
>> +		return nr_ranges;
>> +	if (!nr_ranges)
>> +		return -EINVAL;
>> +	*start = ranges[0].start;
>> +	*end = ranges[nr_ranges - 1].end;
>> +	err = damon_set_regions(t, ranges, nr_ranges, min_region_sz);
> [Severity: High]
> Could this fail with -EINVAL during initialization on systems with sub-page
> memory gaps?
>
> If the system has System RAM fragmented by small non-RAM holes (like sub-page
> BIOS or EFI reserved regions), damon_system_ram_walk_fn() will create separate
> discrete regions.
>
> When these regions are passed to damon_set_regions(), it aligns the boundaries
> to min_region_sz (typically PAGE_SIZE):
>
> mm/damon/core.c:damon_set_regions() {
> 	for (i = 0; i < nr_ranges; i++) {
> 		...
> 		start = ALIGN_DOWN(ranges[i].start, min_region_sz);
> 		end = ALIGN(ranges[i].end, min_region_sz);
> 		if (start >= end)
> 			return -EINVAL;
> 		if (i > 0 && last_end > start)
> 			return -EINVAL;
> 		last_end = end;
> 	}
> 	...
> }
>
> If the gap between two RAM areas is smaller than PAGE_SIZE, the ALIGN of the
> first region's end will evaluate to a higher address than the ALIGN_DOWN of
> the second region's start. Does this cause the strictly-ascending overlap
> check (last_end > start) to trigger and reject valid configurations?


this is a real issue. I overlooked the case where System RAM boundaries 
are not page-size aligned


>> +	kvfree(ranges);
>> +	return err;
>>   }

  reply	other threads:[~2026-07-27 11:04 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-27  9:54 [PATCH 1/2] mm/damon/core: cover discrete System RAM areas with per-range regions Jiayuan Chen
2026-07-27  9:54 ` [PATCH 2/2] mm/damon/sysfs-schemes: report the number of tried regions Jiayuan Chen
2026-07-27 14:34   ` SJ Park
2026-07-27 10:07 ` [PATCH 1/2] mm/damon/core: cover discrete System RAM areas with per-range regions sashiko-bot
2026-07-27 11:04   ` Jiayuan Chen [this message]
2026-07-27 14:26 ` SJ Park

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ca868252-8643-48de-81db-a6f6505397a6@linux.dev \
    --to=jiayuan.chen@linux.dev \
    --cc=damon@lists.linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.