On Thu, Jun 11, 2026 at 09:11:02AM +0530, Aboorva Devarajan wrote:
> The test ignores the return value of fork(), so both the parent and
> the (newly created) child run the COW verification loops and then
> call hmm_buffer_free() before returning into the kselftest harness,
> which _exit()s each side.  This duplicated teardown sequence has
> been observed to manifest as a SIGSEGV in the test child, e.g.:
> 
>   hmm-tests[360141]: segfault (11) at 0 nip 10006964 lr 1000ac3c code 1
>   in hmm-tests[6964,10000000+30000]
>

Thanks for catching this!

> Fix this by adopting the same fork()-then-wait pattern already used
> by the nearby anon_write_child / anon_write_child_shared tests in
> this file: the child performs the COW verification and then _exit(0)s
> so it does not run the test teardown, while the parent independently
> verifies COW, waits for the child, and only then frees the buffer.
> 
> Fixes: b659baea75469 ("mm: selftests for exclusive device memory")
> Signed-off-by: Aboorva Devarajan <[email protected]>
> ---
>  tools/testing/selftests/mm/hmm-tests.c | 31 +++++++++++++++++++++++---
>  1 file changed, 28 insertions(+), 3 deletions(-)
> 
> diff --git a/tools/testing/selftests/mm/hmm-tests.c 
> b/tools/testing/selftests/mm/hmm-tests.c
> index 8f4f82467043..e4c49699f3f7 100644
> --- a/tools/testing/selftests/mm/hmm-tests.c
> +++ b/tools/testing/selftests/mm/hmm-tests.c
> @@ -1884,6 +1884,8 @@ TEST_F(hmm, exclusive_cow)
>       unsigned long i;
>       int *ptr;
>       int ret;
> +     pid_t pid;
> +     int status;
>  
>       npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
>       ASSERT_NE(npages, 0);
> @@ -1912,14 +1914,37 @@ TEST_F(hmm, exclusive_cow)
>       ASSERT_EQ(ret, 0);
>       ASSERT_EQ(buffer->cpages, npages);
>  
> -     fork();
> +     pid = fork();
> +     if (pid == -1)
> +             ASSERT_EQ(pid, 0);
>  
> -     /* Fault pages back to system memory and check them. */
> +     if (pid == 0) {
> +             /*
> +              * Child verifies COW independently, then _exit(0)s so it does
> +              * not run the test teardown.  A failed ASSERT_* here makes the
> +              * harness abort() the child, so the parent sees
> +              * !WIFEXITED(status) below and fails in turn.
> +              */
> +             for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> +                     ASSERT_EQ(ptr[i]++, i);
> +
> +             for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> +                     ASSERT_EQ(ptr[i], i + 1);
> +
> +             _exit(0);
> +     }
> +
> +     /* Parent: also increment to verify COW works for both processes. */
>       for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
>               ASSERT_EQ(ptr[i]++, i);
>  
>       for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> -             ASSERT_EQ(ptr[i], i+1);
> +             ASSERT_EQ(ptr[i], i + 1);
> +
> +     /* Parent: wait for child and then free the buffer. */
> +     ASSERT_EQ(waitpid(pid, &status, 0), pid);
> +     ASSERT_TRUE(WIFEXITED(status));
> +     ASSERT_EQ(WEXITSTATUS(status), 0);
>  
>       hmm_buffer_free(buffer);
>  }

Acked-by: Balbir Singh <[email protected]>

Reply via email to