__damon_va_prepare_access_check() picks a random byte address within the region and stores it in r->sampling_addr. There are two users of r->sampling_addr in vaddr.c that pass it into a page table walk, and both use it as the address of a page. damon_va_mkold(mm, r->sampling_addr) damon_va_walk_page_range(mm, addr, addr + 1) damon_mkold_pmd_entry() damon_ptep_mkold(pte, vma, addr) ptep_test_and_clear_young(vma, addr, pte) mmu_notifier_clear_young(mm, addr, addr + PAGE_SIZE) damon_va_young(mm, r->sampling_addr, &folio_sz) damon_va_walk_page_range(mm, addr, addr + 1) damon_young_pmd_entry() ptep_get(pte) mmu_notifier_test_young(walk->mm, addr) test_and_clear_young_ptes(), which backs ptep_test_and_clear_young() on arm64, documents @addr as "Address the first page is mapped at". For arm64, before commit 6f0e1142173a ("arm64: mm: support batch clearing of the young flag for large folios"), the contpte helper walked exactly CONT_PTES entries from the aligned-down page table pointer and used @addr only to pass down to each entry, so an unaligned value was harmless: ptep = contpte_align_down(ptep); addr = ALIGN_DOWN(addr, CONT_PTE_SIZE); for (i = 0; i < CONT_PTES; i++, ptep++, addr += PAGE_SIZE) Now the range to walk is derived from @addr instead: end = addr + nr * PAGE_SIZE, rounded up to CONT_PTE_SIZE. For a sample in the last page of a contpte block, the sub-page offset puts end just past the block boundary, so the round-up lands a whole block further and the walk clears PTE_AF in CONT_PTES entries beyond the sampled block. Seen on an arm64 guest running the DAMON selftests as random slab and page table corruption. Align the sampled address down to a page boundary. It is the address of the page to sample, so this matches its intended meaning and fixes both users in vaddr.c. Fixes: 3f49584b262c ("mm/damon: implement primitives for the virtual memory address spaces") Cc: stable@vger.kernel.org Signed-off-by: Nathan Gao --- mm/damon/vaddr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index 2c1c1952c008d..e26e426a56af2 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -360,7 +360,8 @@ static void __damon_va_prepare_access_check(struct mm_struct *mm, struct damon_region *r, struct damon_ctx *ctx) { - r->sampling_addr = damon_rand(ctx, r->ar.start, r->ar.end); + r->sampling_addr = PAGE_ALIGN_DOWN(damon_rand(ctx, r->ar.start, + r->ar.end)); damon_va_mkold(mm, r->sampling_addr); } -- 2.50.1