#define _GNU_SOURCE
#include "kselftest_harness.h"
#include <linux/prctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <linux/perf_event.h>
#include "vm_util.h"
#include <linux/mman.h>
FIXTURE(merge)
{
unsigned int page_size;
char *carveout;
struct procmap_fd procmap;
};
static char *map_carveout(unsigned int page_size)
{
return mmap(NULL, 30 * page_size, PROT_NONE,
MAP_ANON | MAP_PRIVATE, -1, 0);
}
static pid_t do_fork(struct procmap_fd *procmap)
{
pid_t pid = fork();
if (pid == -1)
return -1;
if (pid != 0) {
wait(NULL);
return pid;
}
if (close_procmap(procmap))
return -1;
if (open_self_procmap(procmap))
return -1;
return 0;
}
FIXTURE_SETUP(merge)
{
self->page_size = psize();
self->carveout = map_carveout(self->page_size);
ASSERT_NE(self->carveout, MAP_FAILED);
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
}
FIXTURE_TEARDOWN(merge)
{
ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
close_procmap(&self->procmap);
prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);
}
FIXTURE(merge_with_fork)
{
unsigned int page_size;
char *carveout;
struct procmap_fd procmap;
};
FIXTURE_VARIANT(merge_with_fork)
{
bool forked;
};
FIXTURE_VARIANT_ADD(merge_with_fork, forked)
{
.forked = true,
};
FIXTURE_VARIANT_ADD(merge_with_fork, unforked)
{
.forked = false,
};
FIXTURE_SETUP(merge_with_fork)
{
self->page_size = psize();
self->carveout = map_carveout(self->page_size);
ASSERT_NE(self->carveout, MAP_FAILED);
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
}
FIXTURE_TEARDOWN(merge_with_fork)
{
ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
ASSERT_EQ(close_procmap(&self->procmap), 0);
prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);
}
TEST_F(merge, mprotect_unfaulted_left)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr;
ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0);
ptr[5 * page_size] = 'x';
ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
}
TEST_F(merge, mprotect_unfaulted_right)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr;
ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0);
ptr[0] = 'x';
ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
}
TEST_F(merge, mprotect_unfaulted_both)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr;
ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
ptr[3 * page_size] = 'x';
ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
}
TEST_F(merge, mprotect_faulted_left_unfaulted_right)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr;
ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
ptr[0] = 'x';
ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
}
TEST_F(merge, mprotect_unfaulted_left_faulted_right)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr;
ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
ptr[3 * page_size] = 'x';
ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
}
TEST_F(merge, forked_target_vma)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2;
pid_t pid;
int i;
ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr[0] = 'x';
pid = do_fork(&self->procmap);
ASSERT_NE(pid, -1);
if (pid != 0)
return;
for (i = 0; i < 5 * page_size; i += page_size)
ptr[i] = 'x';
ptr2 = mmap(&ptr[5 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 5 * page_size);
}
TEST_F(merge, forked_source_vma)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2;
pid_t pid;
int i;
ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr[0] = 'x';
pid = do_fork(&self->procmap);
ASSERT_NE(pid, -1);
if (pid != 0)
return;
for (i = 0; i < 5 * page_size; i += page_size)
ptr[i] = 'x';
ptr2 = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC), 0);
ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
}
TEST_F(merge, handle_uprobe_upon_merged_vma)
{
const size_t attr_sz = sizeof(struct perf_event_attr);
unsigned int page_size = self->page_size;
const char *probe_file = "./foo";
char *carveout = self->carveout;
struct perf_event_attr attr;
unsigned long type;
void *ptr1, *ptr2;
int fd;
fd = open(probe_file, O_RDWR|O_CREAT, 0600);
ASSERT_GE(fd, 0);
ASSERT_EQ(ftruncate(fd, page_size), 0);
if (read_sysfs("/sys/bus/event_source/devices/uprobe/type", &type) != 0) {
SKIP(goto out, "Failed to read uprobe sysfs file, skipping");
}
memset(&attr, 0, attr_sz);
attr.size = attr_sz;
attr.type = type;
attr.config1 = (__u64)(long)probe_file;
attr.config2 = 0x0;
ASSERT_GE(syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0), 0);
ptr1 = mmap(&carveout[page_size], 10 * page_size, PROT_EXEC,
MAP_PRIVATE | MAP_FIXED, fd, 0);
ASSERT_NE(ptr1, MAP_FAILED);
ptr2 = mremap(ptr1, page_size, 2 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr1 + 5 * page_size);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_NE(mremap(ptr2, page_size, page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr1), MAP_FAILED);
out:
close(fd);
remove(probe_file);
}
TEST_F(merge, ksm_merge)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2;
int err;
ptr = mmap(&carveout[page_size], page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr2 = mmap(&carveout[2 * page_size], page_size,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size);
ASSERT_EQ(munmap(ptr2, page_size), 0);
err = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0);
if (err == -1) {
int errnum = errno;
ASSERT_EQ(errnum, EINVAL);
SKIP(return, "KSM memory merging not supported, skipping.");
}
ptr2 = mmap(&carveout[2 * page_size], page_size,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size);
ASSERT_EQ(munmap(ptr, 2 * page_size), 0);
ptr = mmap(&carveout[page_size], page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr2 = mmap(&carveout[2 * page_size], page_size,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size);
}
TEST_F(merge, mremap_unfaulted_to_faulted)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2;
ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000);
ASSERT_NE(ptr2, MAP_FAILED);
ptr[0] = 'x';
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
}
TEST_F(merge, mremap_unfaulted_behind_faulted)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2;
ptr = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr2 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000);
ASSERT_NE(ptr2, MAP_FAILED);
ptr[0] = 'x';
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 10 * page_size);
}
TEST_F(merge, mremap_unfaulted_between_faulted)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2, *ptr3;
ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ptr3 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr3, MAP_FAILED);
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000);
ASSERT_NE(ptr3, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000);
ASSERT_NE(ptr2, MAP_FAILED);
ptr[0] = 'x';
ptr3[0] = 'x';
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]);
ASSERT_NE(ptr3, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
ASSERT_TRUE(find_vma_procmap(procmap, ptr3));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr3);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr3 + 5 * page_size);
}
TEST_F(merge, mremap_unfaulted_between_faulted_unfaulted)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2, *ptr3;
ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ptr3 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr3, MAP_FAILED);
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000);
ASSERT_NE(ptr3, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000);
ASSERT_NE(ptr2, MAP_FAILED);
ptr[0] = 'x';
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]);
ASSERT_NE(ptr3, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
}
TEST_F(merge, mremap_unfaulted_between_correctly_placed_faulted)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2;
ptr = mmap(&carveout[page_size], 15 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr[0] = 'x';
ASSERT_EQ(munmap(&ptr[5 * page_size], 5 * page_size), 0);
ptr2 = mmap(&carveout[20 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr2, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
}
TEST_F(merge, mremap_correct_placed_faulted)
{
unsigned int page_size = self->page_size;
char *carveout = self->carveout;
struct procmap_fd *procmap = &self->procmap;
char *ptr, *ptr2, *ptr3;
ptr = mmap(&carveout[page_size], 15 * page_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
ASSERT_NE(ptr, MAP_FAILED);
ptr[0] = 'x';
ptr3 = &ptr[10 * page_size];
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000);
ASSERT_NE(ptr3, MAP_FAILED);
ptr2 = &ptr[5 * page_size];
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000);
ASSERT_NE(ptr2, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000);
ASSERT_NE(ptr, MAP_FAILED);
ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000);
ASSERT_NE(ptr, MAP_FAILED);
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr2[5 * page_size]);
ASSERT_NE(ptr3, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 10 * page_size);
ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]);
ASSERT_NE(ptr, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000);
ASSERT_NE(ptr2, MAP_FAILED);
ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 1000);
ASSERT_NE(ptr3, MAP_FAILED);
ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]);
ASSERT_NE(ptr3, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
}
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev)
{
struct procmap_fd *procmap = &self->procmap;
unsigned int page_size = self->page_size;
unsigned long offset;
char *ptr_a, *ptr_b;
ptr_a = mmap(&self->carveout[page_size + 3 * page_size],
3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_a, MAP_FAILED);
ptr_a[0] = 'x';
if (variant->forked) {
pid_t pid = do_fork(&self->procmap);
ASSERT_NE(pid, -1);
if (pid != 0)
return;
}
ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]);
ASSERT_NE(ptr_a, MAP_FAILED);
ptr_b = mmap(&self->carveout[page_size], 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_b, MAP_FAILED);
ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
&self->carveout[page_size + 3 * page_size]);
ASSERT_NE(ptr_a, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr_b));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b);
offset = variant->forked ? 3 * page_size : 6 * page_size;
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + offset);
}
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_next)
{
struct procmap_fd *procmap = &self->procmap;
unsigned int page_size = self->page_size;
unsigned long offset;
char *ptr_a, *ptr_b;
ptr_a = mmap(&self->carveout[page_size], 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_a, MAP_FAILED);
ptr_a[0] = 'x';
if (variant->forked) {
pid_t pid = do_fork(&self->procmap);
ASSERT_NE(pid, -1);
if (pid != 0)
return;
}
ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]);
ASSERT_NE(ptr_a, MAP_FAILED);
ptr_b = mmap(&self->carveout[page_size + 3 * page_size], 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_b, MAP_FAILED);
ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
&self->carveout[page_size]);
ASSERT_NE(ptr_a, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
offset = variant->forked ? 3 * page_size : 6 * page_size;
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + offset);
}
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev_unfaulted_next)
{
struct procmap_fd *procmap = &self->procmap;
unsigned int page_size = self->page_size;
unsigned long offset;
char *ptr_a, *ptr_b, *ptr_c;
ptr_b = mmap(&self->carveout[page_size + 3 * page_size], 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_b, MAP_FAILED);
ptr_b[0] = 'x';
if (variant->forked) {
pid_t pid = do_fork(&self->procmap);
ASSERT_NE(pid, -1);
if (pid != 0)
return;
}
ptr_b = mremap(ptr_b, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]);
ASSERT_NE(ptr_b, MAP_FAILED);
ptr_a = mmap(&self->carveout[page_size], 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_a, MAP_FAILED);
ptr_c = mmap(&self->carveout[page_size + 3 * page_size + 3 * page_size],
3 * page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_c, MAP_FAILED);
ptr_b = mremap(ptr_b, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
&self->carveout[page_size + 3 * page_size]);
ASSERT_NE(ptr_b, MAP_FAILED);
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
offset = variant->forked ? 3 * page_size : 9 * page_size;
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + offset);
if (variant->forked) {
ASSERT_TRUE(find_vma_procmap(procmap, ptr_b));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 3 * page_size);
}
}
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev_faulted_next)
{
struct procmap_fd *procmap = &self->procmap;
unsigned int page_size = self->page_size;
char *ptr_a, *ptr_b, *ptr_bc;
ptr_bc = mmap(&self->carveout[page_size + 3 * page_size],
3 * page_size + 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_bc, MAP_FAILED);
ptr_bc[0] = 'x';
if (variant->forked) {
pid_t pid = do_fork(&self->procmap);
ASSERT_NE(pid, -1);
if (pid != 0)
return;
}
ptr_b = mremap(ptr_bc, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]);
ASSERT_NE(ptr_b, MAP_FAILED);
ptr_a = mmap(&self->carveout[page_size], 3 * page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
ASSERT_NE(ptr_a, MAP_FAILED);
ptr_b = mremap(ptr_b, 3 * page_size, 3 * page_size,
MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
&self->carveout[page_size + 3 * page_size]);
ASSERT_NE(ptr_b, MAP_FAILED);
if (variant->forked) {
ASSERT_TRUE(find_vma_procmap(procmap, ptr_b));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 6 * page_size);
} else {
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size);
}
}
TEST_HARNESS_MAIN