[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <86dbec8afa755c42641eaf6af676f1ef33e0e37b.1746305604.git.lorenzo.stoakes@oracle.com>
Date: Sat, 3 May 2025 22:12:33 +0100
From: Lorenzo Stoakes <lorenzo.stoakes@...cle.com>
To: Andrew Morton <akpm@...ux-foundation.org>
Cc: Vlastimil Babka <vbabka@...e.cz>, Jann Horn <jannh@...gle.com>,
"Liam R . Howlett" <Liam.Howlett@...cle.com>,
Suren Baghdasaryan <surenb@...gle.com>,
Matthew Wilcox <willy@...radead.org>,
David Hildenbrand <david@...hat.com>, Pedro Falcato <pfalcato@...e.de>,
Rik van Riel <riel@...riel.com>, Harry Yoo <harry.yoo@...cle.com>,
Zi Yan <ziy@...dia.com>, Baolin Wang <baolin.wang@...ux.alibaba.com>,
Nico Pache <npache@...hat.com>, Ryan Roberts <ryan.roberts@....com>,
Dev Jain <dev.jain@....com>, Jakub Matena <matenajakub@...il.com>,
Wei Yang <richard.weiyang@...il.com>, linux-mm@...ck.org,
linux-kernel@...r.kernel.org
Subject: [RFC PATCH v3 11/11] tools/testing/selftests: add MREMAP_RELOCATE_ANON fork tests
Add tests explicitly asserting that mremap() fails on forked VMAs whether
they have parent anon_vma's or whether they have child ones, as these are
cases where the folio might be mapped by multiple processes - in this case
we don't even try to relocate folio metadata, but rather simply disallow
the operation.
The tests use MREMAP_MUST_RELOCATE_ANON so we can detect the failure
correctly.
Were the mremap()'s to succeed, a merge would occur, so it remains
appropriate to keep these in the merge test suite.
We also explicitly test the anon_vma reuse case. This is one where a
munmap() occurs on a parent anon_vma which still has children. We keep the
empty anon_vma around, and then attempt to reuse it on the next fork of a
VMA whose anon_vma references this empty anon_vma.
Consider the first case over 3 forks:
FORK 3 TIMES ONE AFTER ANOTHER
==============================
Process 1
|-------------|
| avc* v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----|
| anon_vma ^
| |
v rb_root |
|---------------------|
| refcount = 1 |<--|
| num_children = 1 | |
| num_active_vmas = 1 |---| parent
|---------------------|
FORK
Process 1
|-------------|
| avc v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----|
| anon_vma ^
| |--------------------|
v rb_root | |
|---------------------| |
| refcount = 2 | (self-parent) |
| num_children = 2 |<--------------x----|
| num_active_vmas = 1 | | |
|---------------------| | |
|------------| |
Process 2 | |
| |
|-------------| | |
| avc v v |
|-----| vma |-----| |-----| |
| B |<------| avc |.| avc | |
|-----| |-----| |-----| |
| anon_vma ^ |
| | |
v rb_root | |
|---------------------| |
| refcount = 1 | |
| num_children = 0 |--------------------|
| num_active_vmas = 1 | parent
|---------------------|
FORK
Process 1
|-------------|
| avc v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----| |--------------------------|
| anon_vma ^ | |
| |--------------------| |
v rb_root | | |
|---------------------| | |
| refcount = 3 | (self-parent) | |
| num_children = 2 |<--------------x----| |
| num_active_vmas = 1 | | | |
|---------------------| | | |
|------------| | |
Process 2 | | |
| | |
|-------------| | | |
| avc v v | |
|-----| vma |-----| |-----| | |
| B |<------| avc |.| avc | | |
|-----| |-----| |-----| | |
| anon_vma ^ | |
| |-------------------------x--------| |
v rb_root | | | |
|---------------------| | | |
| refcount = 1 |<-------------------x----| | |
| num_children = 1 |--------------------| | | |
| num_active_vmas = 1 | parent | | |
|---------------------| | | |
| | |
Process 3 | | |
|----------------------x---| |
|-------------| | |--------------x-------|
| avc v v v |
|-----| vma |-----| |-----| |-----| |
| C |<------| avc |.| avc |.| avc | |
|-----| |-----| |-----| |-----| |
| anon_vma ^ |
| | |
v rb_root | |
|---------------------| |
| refcount = 1 | |
| num_children = 0 |-------------------------|
| num_active_vmas = 1 | parent
|---------------------|
FORK
Process 1
|-------------|
| avc v
|-----| vma |-----| |---------------------------------|
| A |<------| avc | | |
|-----| |-----| |--------------------------| |
| anon_vma ^ | | |
| |--------------------| | |
v rb_root | | | |
|---------------------| | | |
| refcount = 4 | (self-parent) | | |
| num_children = 2 |<--------------x----| | |
| num_active_vmas = 1 | | | | |
|---------------------| | | | |
|------------| | | |
Process 2 | | | |
| | | |
|-------------| | | | |
| avc v v | | |
|-----| vma |-----| |-----| | | |
| B |<------| avc |.| avc | | | |
|-----| |-----| |-----| |--------x------------x-------| |
| anon_vma ^ | | | | |
| |-------------------------x--------| | | |
v rb_root | | | | | |
|---------------------| | | | | |
| refcount = 1 |<-------------------x----| | | | |
| num_children = 1 |--------------------| | | | | |
| num_active_vmas = 1 | parent | | | | |
|---------------------| | | | | |
| | | | |
Process 3 | | | | |
|----------------------x---| | | |
|-------------| | |--------------x-------| | |
| avc v v v | | |
|-----| vma |-----| |-----| |-----| | | |
| C |<------| avc |.| avc |.| avc | | | |
|-----| |-----| |-----| |-----| | | |
| anon_vma ^ | | |
| |------------------------------x-----------| | |
v rb_root | | | | |
|---------------------| | | | |
| refcount = 1 |<------------------------x---| | | |
| num_children = 1 |-------------------------| | | | |
| num_active_vmas = 1 | parent | | | |
|---------------------| | | | |
| | | |
Process 4 | | | |
|--------------------------x-------| | |
|-------------| | |------------------x-----------| |
| avc v v v v----------x---------------|
|-----| vma |-----| |-----| |-----| |-----| |
| D |<------| avc |.| avc |.| avc |.| avc | |
|-----| |-----| |-----| |-----| |-----| |
| anon_vma ^ |
| | |
v rb_root | |
|---------------------| |
| refcount = 1 | |
| num_children = 0 |-----------------------------|
| num_active_vmas = 1 | parent
|---------------------|
We can see that at no point do we lack either a raised num_children count
or anon_vma_chain list count.
Equally with anon_vma reuse:
FORK 3 TIMES ONE AFTER ANOTHER, UNMAPPING AFTER FORK FOR ANON_VMA REUSE
=======================================================================
Process 1
|-------------|
| avc* v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----|
| anon_vma ^
| |
v rb_root |
|---------------------|
| refcount = 1 |<--|
| num_children = 1 | |
| num_active_vmas = 1 |---| parent
|---------------------|
FORK
Process 1
|-------------|
| avc v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----|
| anon_vma ^
| |--------------------|
v rb_root | |
|---------------------| |
| refcount = 2 | (self-parent) |
| num_children = 2 |<--------------x----|
| num_active_vmas = 1 | | |
|---------------------| | |
|------------| |
Process 2 | |
| |
|-------------| | |
| avc v v |
|-----| vma |-----| |-----| |
| B |<------| avc |.| avc | |
|-----| |-----| |-----| |
| anon_vma ^ |
| | |
v rb_root | |
|---------------------| |
| refcount = 1 | |
| num_children = 0 |--------------------|
| num_active_vmas = 1 | parent
|---------------------|
FORK
Process 1
|-------------|
| avc v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----| |--------------------------|
| anon_vma ^ | |
| |--------------------| |
v rb_root | | |
|---------------------| | |
| refcount = 3 | (self-parent) | |
| num_children = 2 |<--------------x----| |
| num_active_vmas = 1 | | | |
|---------------------| | | |
|------------| | |
Process 2 | | |
| | |
|-------------| | | |
| avc v v | |
|-----| vma |-----| |-----| | |
| B |<------| avc |.| avc | | |
|-----| |-----| |-----| | |
| anon_vma ^ | |
| |-------------------------x--------| |
v rb_root | | | |
|---------------------| | | |
| refcount = 1 |<-------------------x----| | |
| num_children = 1 |--------------------| | | |
| num_active_vmas = 1 | parent | | |
|---------------------| | | |
| | |
Process 3 | | |
|----------------------x---| |
|-------------| | |--------------x-------|
| avc v v v |
|-----| vma |-----| |-----| |-----| |
| C |<------| avc |.| avc |.| avc | |
|-----| |-----| |-----| |-----| |
| anon_vma ^ |
| | |
v rb_root | |
|---------------------| |
| refcount = 1 | |
| num_children = 0 |-------------------------|
| num_active_vmas = 1 | parent
|---------------------|
UNMAP B
Process 1
|-------------|
| avc v
|-----| vma |-----|
| A |<------| avc |
|-----| |-----| |--------------------------|
| anon_vma ^ | |
| |------------ |
v rb_root | |
|---------------------| |
| refcount = 3 | (self-parent) |
| num_children = 2 |<-------------------| |
| num_active_vmas = 1 | | |
|---------------------| | |
| |
Process 2 | |
| |
|-------------------------x--------| |
rb_root | | | |
|---------------------| | | |
| refcount = 1 |<-------------------x----| | | We keep empty
| num_children = 1 |--------------------| | | | anon_vma round.
| num_active_vmas = 0 | parent | | |
|---------------------| | | |
| | |
Process 3 | | |
|----------------------x---| |
|-------------| | |--------------x-------|
| avc v v v |
|-----| vma |-----| |-----| |-----| |
| C |<------| avc |.| avc |.| avc | |
|-----| |-----| |-----| |-----| |
| anon_vma ^ |
| | |
v rb_root | |
|---------------------| |
| refcount = 1 | |
| num_children = 0 |-------------------------|
| num_active_vmas = 1 | parent
|---------------------|
FORK
Process 1
|-------------|
| avc v
|-----| vma |-----| |-----------------------------|
| A |<------| avc | | |
|-----| |-----| |--------------------------| |
| anon_vma ^ | | |
| |-----------| | |
v rb_root | | |
|---------------------| | |
| refcount = 3 | (self-parent) | |
| num_children = 2 |<-------------------| | |
| num_active_vmas = 1 | | | |
|---------------------| | | |
| | |
Process 2 |--------------------x------------x-------x-------|
| | | | |
|-------------------------x--------| | | |
rb_root | | | | | |
|---------------------|<-------------------x--------x---x-------x---| |
| refcount = 1 |<-------------------x----| | | | | |
| num_children = 1 |--------------------| | | | | | |
| num_active_vmas = 1 | parent | | | | | |
|---------------------| | | | | | |
| | | | | |
Process 3 | | | | | |
|----------------------x---| | | | |
|-------------| | |--------------x-------| | | |
| avc v v v | | | |
|-----| vma |-----| |-----| |-----| | | | |
| C |<------| avc |.| avc |.| avc | | | | |
|-----| |-----| |-----| |-----| | | | |
| anon_vma ^ | | | |
| |------------------------------x-----------| | | |
v rb_root | | | | | |
|---------------------| | | | | |
| refcount = 1 | | | | | |
| num_children = 0 |-------------------------| | | | |
| num_active_vmas = 1 | parent | | | |
|---------------------| | | | |
| | | |
Process 4 | | | |
|----------------------------------| | | |
|-------------| | |------------------------------| | |
| avc v v v | |
|-----| vma |-----| |-----| |-----| | |
| D |<------| avc |.| avc |.| avc | | |
|-----| |-----| |-----| |-----| | |
| anon_vma ^ | |
| |--------------------------------------------------x---|
| |
|----------------------------------------------------------------|
We reuse the empty anon_vma from VMA B. Note that process 3 is now parented
to process 4's (and 2's) anon_vma.
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@...cle.com>
---
tools/testing/selftests/mm/merge.c | 361 +++++++++++++++++++++++++++++
1 file changed, 361 insertions(+)
diff --git a/tools/testing/selftests/mm/merge.c b/tools/testing/selftests/mm/merge.c
index 8d70c24b4303..25731482a5b4 100644
--- a/tools/testing/selftests/mm/merge.c
+++ b/tools/testing/selftests/mm/merge.c
@@ -2,6 +2,7 @@
#define _GNU_SOURCE
#include "../kselftest_harness.h"
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -10,11 +11,18 @@
#include "vm_util.h"
#include <linux/mman.h>
+enum poll_action {
+ POLL_TASK_RUN,
+ POLL_TASK_WAIT,
+ POLL_TASK_EXIT,
+};
+
FIXTURE(merge)
{
unsigned int page_size;
char *carveout;
struct procmap_fd procmap;
+ volatile enum poll_action *ipc;
};
FIXTURE_SETUP(merge)
@@ -26,12 +34,18 @@ FIXTURE_SETUP(merge)
ASSERT_NE(self->carveout, MAP_FAILED);
/* Setup PROCMAP_QUERY interface. */
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
+
+ /* Quick and dirty IPC. */
+ self->ipc = (volatile enum poll_action *)mmap(NULL, self->page_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+ ASSERT_NE(self->ipc, MAP_FAILED);
}
FIXTURE_TEARDOWN(merge)
{
ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
ASSERT_EQ(close_procmap(&self->procmap), 0);
+ ASSERT_EQ(munmap((void *)self->ipc, self->page_size), 0);
}
TEST_F(merge, mprotect_unfaulted_left)
@@ -1777,4 +1791,351 @@ TEST_F(merge, mremap_relocate_anon_mprotect_faulted_faulted)
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
}
+TEST_F(merge, mremap_relocate_anon_single_fork)
+{
+ unsigned int page_size = self->page_size;
+ char *carveout = self->carveout;
+ volatile enum poll_action *poll = self->ipc;
+ char *ptr, *ptr2;
+ pid_t pid2;
+ int err;
+
+ /*
+ * . . .
+ * . |-------| . . Map A, fault in and
+ * . | A |-.-----| . fork process 1 to
+ * . |-------| . | . process 2.
+ * . . v .
+ * . . |-------| .
+ * . . | B | .
+ * . . |-------| .
+ * . . .
+ * . Process 1 . Process 2 .
+ */
+ ptr = mmap(carveout, 3 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+ /* Fault it in. */
+ ptr[0] = 'x';
+
+ pid2 = fork();
+ ASSERT_NE(pid2, -1);
+ /* Parent process. */
+ if (pid2 != 0) {
+ /* mremap() fails due to forked children. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ poll[0] = POLL_TASK_EXIT;
+
+ wait(NULL);
+ return;
+ }
+
+ /* This is process 2. */
+
+ /* mremap() fails due to forked parents. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ /* Wait for parent to finish. */
+ while (poll[0] == POLL_TASK_RUN)
+ ;
+}
+
+TEST_F(merge, mremap_relocate_anon_fork_twice)
+{
+ unsigned int page_size = self->page_size;
+ char *carveout = self->carveout;
+ volatile enum poll_action *poll = self->ipc;
+ char *ptr, *ptr2;
+ pid_t pid2, pid3;
+ int err;
+
+ /*
+ * . . .
+ * . |-------| . . Map A, fault in and
+ * . | A |-.-----| . fork process 1 to
+ * . |-------| . | . process 2.
+ * . . v .
+ * . . |-------| .
+ * . . | B | .
+ * . . |-------| .
+ * . . .
+ * . Process 1 . Process 2 .
+ */
+ ptr = mmap(carveout, 3 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+ /* Fault it in. */
+ ptr[0] = 'x';
+ pid2 = fork();
+ ASSERT_NE(pid2, -1);
+ /* If parent process, simply wait. */
+ if (pid2 != 0) {
+ while (true) {
+ if (poll[0] == POLL_TASK_EXIT)
+ break;
+ if (poll[0] == POLL_TASK_WAIT)
+ continue;
+
+ /* mremap() fails due to forked children. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED |
+ MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ /* Strictly, should be atomic. */
+ if (poll[0] == POLL_TASK_RUN)
+ poll[0] = POLL_TASK_WAIT;
+ }
+
+ wait(NULL);
+ return;
+ }
+
+ /* This is process 2. */
+
+ /* Wait for parent to finish. */
+ while (poll[0] == POLL_TASK_RUN)
+ ;
+
+ /*
+ * . . . .
+ * . |-------| . . .
+ * . | A | . . .
+ * . |-------| . . .
+ * . . . .
+ * . . |-------| . . Fork process 2 to
+ * . . | B |-.-----| . process 3.
+ * . . |-------| . | .
+ * . . . v .
+ * . . . |-------| .
+ * . . . | C | .
+ * . . . |-------| .
+ * . . . .
+ * . Process 1 . Process 2 . Process 3 .
+ */
+ pid3 = fork();
+ ASSERT_NE(pid3, -1);
+ /* If parent process, simply wait. */
+ if (pid3 != 0) {
+ /* mremap() fails due to forked children. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED |
+ MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ /* We don't retrigger, so just indicate we're done. */
+ poll[1] = POLL_TASK_EXIT;
+
+ wait(NULL);
+ return;
+ }
+
+ /* This is process 3. */
+
+ /* Trigger root mremap(). */
+ poll[0] = POLL_TASK_RUN;
+ /* Wait for parents to finish. */
+
+ while (poll[0] == POLL_TASK_RUN)
+ ;
+ while (poll[1] == POLL_TASK_RUN)
+ ;
+
+ /* mremap() fails due to forked parents. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+ /* Kill waiting parent. */
+ poll[0] = POLL_TASK_EXIT;
+}
+
+TEST_F(merge, mremap_relocate_anon_3_times_reuse_anon_vma)
+{
+ unsigned int page_size = self->page_size;
+ char *carveout = self->carveout;
+ volatile enum poll_action *poll = self->ipc;
+ char *ptr, *ptr2;
+ pid_t pid2, pid3, pid4;
+ int err;
+
+ /*
+ * . . .
+ * . |-------| . . Map A, fault in and
+ * . | A |-.-----| . fork process 1 to
+ * . |-------| . | . process 2.
+ * . . v .
+ * . . |-------| .
+ * . . | B | .
+ * . . |-------| .
+ * . . .
+ * . Process 1 . Process 2 .
+ */
+ ptr = mmap(carveout, 3 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+ /* Fault it in. */
+ ptr[0] = 'x';
+ pid2 = fork();
+ ASSERT_NE(pid2, -1);
+ /* If parent process, simply wait. */
+ if (pid2 != 0) {
+ while (true) {
+ if (poll[0] == POLL_TASK_EXIT)
+ break;
+ if (poll[0] == POLL_TASK_WAIT)
+ continue;
+
+ /* mremap() fails due to forked children. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED |
+ MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ if (poll[0] == POLL_TASK_RUN)
+ poll[0] = POLL_TASK_WAIT;
+ }
+
+ wait(NULL);
+ return;
+ }
+
+ /* This is process 2. */
+
+ /* Wait for parent to finish. */
+ while (poll[0] == POLL_TASK_RUN)
+ ;
+
+ /*
+ * . . . .
+ * . |-------| . . .
+ * . | A | . . .
+ * . |-------| . . .
+ * . . . .
+ * . . |-------| . . Fork process 2 to
+ * . . | B |-.-----| . process 3.
+ * . . |-------| . | .
+ * . . . v .
+ * . . . |-------| .
+ * . . . | C | .
+ * . . . |-------| .
+ * . . . .
+ * . Process 1 . Process 2 . Process 3 .
+ */
+ pid3 = fork();
+ ASSERT_NE(pid3, -1);
+ /* If parent process, simply wait. */
+ if (pid3 != 0) {
+ /*
+ * We only try to mremap once, before unmapping so we can
+ * trigger reuse of B's anon_vma.
+ */
+ /* mremap() fails due to forked children. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED |
+ MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ /*
+ * . . . .
+ * . |-------| . . .
+ * . | A | . . .
+ * . |-------| . . .
+ * . . . .
+ * . . . . Unmap VMA B, but
+ * . . . . anon_vma is left
+ * . . . . around.
+ * . . . .
+ * . . . |-------| .
+ * . . . | C | .
+ * . . . |-------| .
+ * . . . .
+ * . Process 1 . Process 2 . Process 3 .
+ */
+ munmap(ptr, 3 * page_size);
+
+ /* We indicate we're done so child waits for */
+ poll[1] = POLL_TASK_EXIT;
+
+ wait(NULL);
+ return;
+ }
+
+ /* This is process 3. */
+
+ /* Trigger root mremap(). */
+ poll[0] = POLL_TASK_RUN;
+ /* Wait for parents to finish. */
+ while (poll[0] == POLL_TASK_RUN)
+ ;
+ while (poll[1] == POLL_TASK_RUN)
+ ;
+
+ pid4 = fork();
+ ASSERT_NE(pid4, -1);
+
+ if (pid4 != 0) {
+ /* mremap() fails due to forked children. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED |
+ MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+
+ /* We don't retrigger, so just indicate we're done. */
+ poll[2] = POLL_TASK_EXIT;
+
+ wait(NULL);
+ return;
+ }
+
+ /* This is process 4. */
+
+ /* Trigger root mremap(). */
+ poll[0] = POLL_TASK_RUN;
+ /* We unmapped VMA B, so nothing to trigger there. */
+ /* Wait for parents to finish. */
+ while (poll[0] == POLL_TASK_RUN)
+ ;
+ while (poll[2] == POLL_TASK_RUN)
+ ;
+
+ /* mremap() fails due to forked parents. */
+ ptr2 = sys_mremap(ptr, page_size, page_size,
+ MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_MUST_RELOCATE_ANON,
+ &carveout[3 * page_size]);
+ err = errno;
+ ASSERT_EQ(ptr2, MAP_FAILED);
+ ASSERT_EQ(err, EFAULT);
+ /* Kill waiting parent. */
+ poll[0] = POLL_TASK_EXIT;
+}
+
TEST_HARNESS_MAIN
--
2.49.0
Powered by blists - more mailing lists