From: Suren Baghdasaryan <surenb@google.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: david@kernel.org, ljs@kernel.org, jannh@google.com,
liam@infradead.org, willy@infradead.org, paulmck@kernel.org,
pfalcato@suse.de, usama.arif@linux.dev, vbabka@kernel.org,
linux-fsdevel@vger.kernel.org, yi1.lai@intel.com,
oe-lkp@lists.linux.dev, lkp@intel.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 1/1] selftests/proc: use RSS in /proc/pid/smaps_rollup tearing tests
Date: Mon, 2 Nov 2026 07:43:17 -0800 [thread overview]
Message-ID: <CAJuCfpGmPLBRhHa_dmyP10QzyRyvcWMYDLU0+J9t-RCLBsTaWA@mail.gmail.com> (raw)
In-Reply-To: <20261101110100.90796bf33ef50307a7698dca@linux-foundation.org>
On Sun, Nov 1, 2026 at 11:01 AM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Sun, 1 Nov 2026 07:45:03 -0800 Suren Baghdasaryan <surenb@google.com> wrote:
>
> > Pss metrics can be affected by map count even if VMA tree stays unchanged,
> > therefore tests checking VMA tree integrity can't use them. Use Rss instead
> > and pre-populate the VMAs so that Rss changes if a VMA gets skipped or
> > double-counted.
> >
> > To ensure all test VMAs are populated with anonymous pages, remain
> > mergeable across VMA operations, and do not have their Rss changed by
> > memory reclaim:
> > - Toggle PROT_EXEC instead of PROT_WRITE between adjacent test VMAs so
> > all test VMAs remain writable and MAP_POPULATE faults in anonymous pages
> > (rather than the shared zero page, which is not accounted in Rss).
> > - Use mprotect() instead of mmap(..., MAP_FIXED) in split_vma() and
> > merge_vma() so the first page of the split VMA stays resident.
> > mprotect(..., PROT_NONE) splits the VMA without unmapping the first page
> > (pte_present() remains true for PAGE_NONE PTEs), and
> > mprotect(..., self->mod_info->prot) merges it back. Both parts of the
> > split VMA retain their non-zero Rss.
> > - Lock all present pages in the child with mlockall(MCL_CURRENT |
> > MCL_FUTURE | MCL_ONFAULT) so memory pressure cannot reclaim pages and
> > alter Rss during the test.
> > - In remap_vma(), unlock next_addr before mremap(..., MREMAP_DONTUNMAP)
> > so next_addr is not split, use mmap(..., MAP_FIXED) in patch_vma() to
> > merge the middle VMA back into addr, and exclude smaps_rollup tests
> > from test_maps_tearing_from_remap since remapping moves and unmaps the
> > page from next_addr.
>
> Thanks. This is proving tricky.
>
> My evolving reviewbot still isn't happy:
>
> : This fixes the v1 RSS issues, but two things remain.
> :
> : Alternating PROT_EXEC breaks with READ_IMPLIES_EXEC: both mappings
> : become RWX and can merge. A syscall test produced two VMAs instead
> : of 16. Could we populate everything writable, then mprotect the odd
> : mappings read-only? That preserved all 16 VMAs and avoids zero pages.
> :
> : Also, MCL_ONFAULT doesn't bypass RLIMIT_MEMLOCK. If mlockall() fails,
> : the child asserts before CHILD_READY and the parent waits until
> : timeout. Please arrange a coordinated skip for insufficient allowance.
Good point. That part of your fix looks good to me.
> :
> : The patched userspace test passed all six cases on an existing
> : x86-64 6.18.57 kernel, with a PID-namespace adjustment. That kernel
> : predates ca5480fb53fa, so this checks selftest behavior, not the
> : intended kernel's procfs changes. Separate syscall tests on that
> : kernel confirmed the EXEC-merging and memlock failure cases.
I'm guessing your bot was using personality() to make the tests fail.
Again, I'm not sure we should handle such use case. Should we?
>
> I said "no, do it yourself" and it came up with the below.
>
>
>
>
> From: Andrew Morton <akpm@linux-foundation.org>
> Subject: selftests/proc: handle personality and memlock constraints
> Date: Sun, 01 Nov 2026 10:55:37 -0800
>
> Alternating PROT_EXEC does not keep the test mappings distinct when
> READ_IMPLIES_EXEC is set: both protection combinations become RWX and
> adjacent mappings can merge, breaking the fixture's layout assumptions.
>
> Populate each mapping writable, then make odd mappings read-only. This
> retains anonymous pages accounted in RSS while keeping adjacent VMAs
> distinct regardless of READ_IMPLIES_EXEC. Restore the corresponding
> WRITE-based protection selection in the parent.
>
> Restore the whole three-page destination after remapping, rather than
> replacing only its middle page and relying on the remaining anonymous VMAs
> to merge. The remap test no longer compares RSS.
>
> mlockall(MCL_CURRENT) checks the whole virtual size against the locked
> memory allowance even with MCL_ONFAULT. An insufficient allowance makes
> the child exit before CHILD_READY, leaving the parent waiting until the
> test times out.
>
> Pass the locking error through the existing shared state and signal
> CHILD_READY on failure. The parent reaps the child and releases the
> shared state before skipping resource or permission failures. Check this
> status before opening the child's proc files, as it may already have
> exited. Unexpected locking errors remain test failures.
>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> Cc: Suren Baghdasaryan <surenb@google.com>
> Cc: David Hildenbrand <david@kernel.org>
> Cc: Jann Horn <jannh@google.com>
> Cc: kernel test robot <yi1.lai@intel.com>
> Cc: Liam R. Howlett <liam@infradead.org>
> Cc: Lorenzo Stoakes <ljs@kernel.org>
> Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
> Cc: Paul E. McKenney <paulmck@kernel.org>
> Cc: Pedro Falcato <pfalcato@suse.de>
> Cc: Usama Arif <usama.arif@linux.dev>
> Cc: Vlastimil Babka <vbabka@kernel.org>
> ---
> On top of Suren's v2 "selftests/proc: use RSS in
> /proc/pid/smaps_rollup tearing tests".
>
> Tested the userspace test on an existing x86-64 Linux 6.18.57 kernel,
> using a separate PID-namespace adaptation which is not in this patch:
> - All six cases passed normally and with READ_IMPLIES_EXEC enabled,
> using DURATION=1.
> - All six cases skipped promptly with RLIMIT_MEMLOCK set to 64KiB or
> zero, exercising ENOMEM and EPERM respectively.
> This does not validate the intended kernel's procfs changes.
>
> tools/testing/selftests/proc/proc-maps-race.c | 49 +++++++++++-----
> 1 file changed, 36 insertions(+), 13 deletions(-)
>
> --- a/tools/testing/selftests/proc/proc-maps-race.c~proc-maps-race-followup
> +++ a/tools/testing/selftests/proc/proc-maps-race.c
> @@ -174,6 +174,7 @@ typedef bool (*vma_mod_result_check_op)(
> struct line_content *restored_first_line);
>
> struct vma_modifier_info {
> + int mlock_error;
> int vma_count;
> void *addr;
> int prot;
> @@ -696,7 +697,8 @@ static inline bool remap_vma(FIXTURE_DAT
>
> static inline bool patch_vma(FIXTURE_DATA(proc_maps_race) *self)
> {
> - return mmap(self->mod_info->addr + self->page_size, self->page_size,
> + /* Replace all three pieces without relying on anon_vma merging. */
> + return mmap(self->mod_info->addr, self->page_size * 3,
> self->mod_info->prot,
> MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
> }
> @@ -757,22 +759,31 @@ FIXTURE_SETUP(proc_maps_race)
> self->pid = fork();
> if (!self->pid) {
> /* Child process modifying the address space */
> - int prot = PROT_READ | PROT_WRITE;
> int i;
>
> for (i = 0; i < mod_info->vma_count; i++) {
> - mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3, prot,
> + mod_info->child_mapped_addr[i] = mmap(NULL, self->page_size * 3,
> + PROT_READ | PROT_WRITE,
> MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
> ASSERT_NE(mod_info->child_mapped_addr[i], MAP_FAILED);
> - /* change protection in adjacent maps to prevent merging */
> - prot ^= PROT_EXEC;
> + /*
> + * Populate writable to get anonymous pages, then alternate
> + * write permission even with READ_IMPLIES_EXEC enabled.
> + */
> + if (i % 2)
> + ASSERT_EQ(mprotect(mod_info->child_mapped_addr[i],
> + self->page_size * 3, PROT_READ), 0);
> }
> /*
> * Lock all present pages in memory so memory reclaim cannot
> * change RSS during the test, and use MCL_ONFAULT so
> * mremap() expansion does not populate new pages.
> */
> - ASSERT_EQ(mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT), 0);
> + if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT)) {
> + mod_info->mlock_error = errno;
> + signal_state(mod_info, CHILD_READY);
> + _exit(0);
> + }
> signal_state(mod_info, CHILD_READY);
> wait_for_state(mod_info, PARENT_READY);
> while (true) {
> @@ -799,6 +810,21 @@ FIXTURE_SETUP(proc_maps_race)
> exit(0);
> }
>
> + /* Check setup before opening proc files: the child may have exited. */
> + wait_for_state(mod_info, CHILD_READY);
> + if (mod_info->mlock_error) {
> + int error = mod_info->mlock_error;
> + int status;
> +
> + ASSERT_EQ(waitpid(self->pid, &status, 0), self->pid);
> + pthread_cond_destroy(&mod_info->sync_cond);
> + pthread_mutex_destroy(&mod_info->sync_lock);
> + munmap(mod_info, self->shared_mem_size);
> + if (error == ENOMEM || error == EPERM || error == EAGAIN)
> + SKIP(return, "Cannot lock test mappings: %s", strerror(error));
> + ASSERT_EQ(error, 0);
> + }
> +
> switch (self->maps_file) {
> case MAPS:
> sprintf(fname, "/proc/%d/maps", self->pid);
> @@ -815,9 +841,6 @@ FIXTURE_SETUP(proc_maps_race)
> self->maps_fd = open(fname, O_RDONLY);
> ASSERT_NE(self->maps_fd, -1);
>
> - /* Wait for the child to map the VMAs */
> - wait_for_state(mod_info, CHILD_READY);
> -
> /* Read first two pages */
> self->page1.data = malloc(self->page_size);
> ASSERT_NE(self->page1.data, NULL);
> @@ -842,10 +865,10 @@ FIXTURE_SETUP(proc_maps_race)
> for (int i = 0; i < mod_info->vma_count; i++) {
> if (mod_info->child_mapped_addr[i] == (void *)self->last_line.start_addr) {
> mod_info->addr = mod_info->child_mapped_addr[i];
> - mod_info->prot = PROT_READ | PROT_WRITE;
> - /* Odd VMAs have exec permission */
> - if ((i % 2) != 0)
> - mod_info->prot |= PROT_EXEC;
> + mod_info->prot = PROT_READ;
> + /* Even VMAs have write permission. */
> + if ((i % 2) == 0)
> + mod_info->prot |= PROT_WRITE;
> } else if (mod_info->child_mapped_addr[i] == (void *)self->first_line.start_addr) {
> mod_info->next_addr = mod_info->child_mapped_addr[i];
> }
> _
>
next prev parent reply other threads:[~2026-11-02 15:43 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-01 15:45 Suren Baghdasaryan
2026-11-01 19:01 ` Andrew Morton
2026-11-02 13:08 ` Pedro Falcato
2026-11-02 15:31 ` Suren Baghdasaryan
2026-11-02 15:43 ` Suren Baghdasaryan [this message]
2026-11-02 16:01 ` Andrew Morton
2026-11-02 16:11 ` Suren Baghdasaryan
2026-11-02 16:15 ` Suren Baghdasaryan
2026-11-02 17:01 ` Andrew Morton
2026-11-02 17:21 ` Suren Baghdasaryan
2026-11-02 13:26 ` Pedro Falcato
2026-11-02 15:59 ` Suren Baghdasaryan
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=CAJuCfpGmPLBRhHa_dmyP10QzyRyvcWMYDLU0+J9t-RCLBsTaWA@mail.gmail.com \
--to=surenb@google.com \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=jannh@google.com \
--cc=liam@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ljs@kernel.org \
--cc=lkp@intel.com \
--cc=oe-lkp@lists.linux.dev \
--cc=paulmck@kernel.org \
--cc=pfalcato@suse.de \
--cc=usama.arif@linux.dev \
--cc=vbabka@kernel.org \
--cc=willy@infradead.org \
--cc=yi1.lai@intel.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®