occlum/test/mmap/main.c
Tate, Hongliang Tian 70d3991ff5 Rewrite mremap system call
This rewrite serves three purposes:
1. Fix some subtle bugs in the old implementation;
2. Implement mremap using mmap and munmap so that mremap can automatically
enjoy new features (e.g., mprotect and memory permissions) once mmap and
munmap support the feature.
3. Write down the invariants hold by VMManager explictly so that the correctness
of the new implementation can be reason more easily.
2020-06-13 03:33:32 +00:00

774 lines
25 KiB
C

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include "test.h"
// ============================================================================
// Helper macros
// ============================================================================
#define KB (1024UL)
#define MB (1024 * 1024UL)
#define PAGE_SIZE (4 * KB)
#define ALIGN_DOWN(x, a) ((x) & ~(a-1)) // a must be a power of two
#define ALIGN_UP(x, a) ALIGN_DOWN((x+(a-1)), (a))
#define MAX_MMAP_USED_MEMORY (4 * MB)
// ============================================================================
// Helper functions
// ============================================================================
static int fill_file_with_repeated_bytes(int fd, size_t len, int byte_val) {
char buf[PAGE_SIZE];
memset(buf, byte_val, sizeof(buf));
size_t remain_bytes = len;
while (remain_bytes > 0) {
int to_write_bytes = MIN(sizeof(buf), remain_bytes);
int written_bytes = write(fd, buf, to_write_bytes);
if (written_bytes != to_write_bytes) {
THROW_ERROR("file write failed");
}
remain_bytes -= written_bytes;
}
return 0;
}
static int check_bytes_in_buf(char *buf, size_t len, int expected_byte_val) {
for (size_t bi = 0; bi < len; bi++) {
if (buf[bi] != (char)expected_byte_val) {
printf("check_bytes_in_buf: expect %02X, but found %02X, at offset %lu\n",
(unsigned char)expected_byte_val, (unsigned char)buf[bi], bi);
return -1;
}
}
return 0;
}
static void *get_a_stack_ptr() {
volatile int a = 0;
return (void *) &a;
}
// ============================================================================
// Test suite initialization
// ============================================================================
// Get a valid range of address hints for mmap
static int get_a_valid_range_of_hints(size_t *hint_begin, size_t *hint_end) {
size_t big_buf_len = MAX_MMAP_USED_MEMORY;
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
void *big_buf = mmap(NULL, big_buf_len, prot, flags, -1, 0);
if (big_buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
int ret = munmap(big_buf, big_buf_len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
*hint_begin = (size_t)big_buf;
*hint_end = *hint_begin + big_buf_len;
return 0;
}
static size_t HINT_BEGIN, HINT_END;
int test_suite_init() {
if (get_a_valid_range_of_hints(&HINT_BEGIN, &HINT_END) < 0) {
THROW_ERROR("get_a_valid_range_of_hints failed");
}
return 0;
}
// ============================================================================
// Test cases for anonymous mmap
// ============================================================================
int test_anonymous_mmap() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
for (size_t len = PAGE_SIZE; len <= MAX_MMAP_USED_MEMORY; len *= 2) {
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
if (check_bytes_in_buf(buf, len, 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros");
}
int ret = munmap(buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
}
return 0;
}
int test_anonymous_mmap_randomly() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
void *bufs[16] = {NULL};
size_t lens[16];
size_t num_bufs = 0;
size_t used_memory = 0;
for (int i = 0; i < 5; i++) {
// Phrase 1: do mmap with random sizes until no more buffers or memory
for (num_bufs = 0;
num_bufs < ARRAY_SIZE(bufs) && used_memory < MAX_MMAP_USED_MEMORY;
num_bufs++) {
// Choose the mmap size randomly
size_t len = rand() % (MAX_MMAP_USED_MEMORY - used_memory) + 1;
len = ALIGN_UP(len, PAGE_SIZE);
// Do mmap
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
bufs[num_bufs] = buf;
lens[num_bufs] = len;
// Update memory usage
used_memory += len;
}
// Phrase 2: do munmap to free all memory mapped memory
for (int bi = 0; bi < num_bufs; bi++) {
void *buf = bufs[bi];
size_t len = lens[bi];
int ret = munmap(buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
bufs[bi] = NULL;
lens[bi] = 0;
}
num_bufs = 0;
used_memory = 0;
}
return 0;
}
int test_anonymous_mmap_randomly_with_good_hints() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
for (int i = 0; i < 10; i++) {
size_t hint = HINT_BEGIN + rand() % (HINT_END - HINT_BEGIN);
hint = ALIGN_DOWN(hint, PAGE_SIZE);
size_t len = rand() % (HINT_END - (size_t)hint);
len = ALIGN_UP(len + 1, PAGE_SIZE);
void *addr = mmap((void *)hint, len, prot, flags, -1, 0);
if (addr != (void *)hint) {
THROW_ERROR("mmap with hint failed");
}
int ret = munmap(addr, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
}
return 0;
}
int test_anonymous_mmap_with_bad_hints() {
size_t bad_hints[] = {
PAGE_SIZE, // too low!
0xffff800000000000UL, // too high!
ALIGN_DOWN((size_t)get_a_stack_ptr(), PAGE_SIZE), // overlapped with stack!
HINT_BEGIN + 123, // within the valid range, not page aligned!
};
int len = PAGE_SIZE;
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
for (int hi = 0; hi < ARRAY_SIZE(bad_hints); hi++) {
void *bad_hint = (void *)bad_hints[hi];
void *addr = mmap(bad_hint, len, prot, flags, -1, 0);
if (addr == MAP_FAILED) {
THROW_ERROR("mmap should have tolerated a bad hint");
}
if (addr == bad_hint) {
THROW_ERROR("mmap should not have accepted a bad hint");
}
int ret = munmap(addr, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
}
return 0;
}
int test_anonymous_mmap_with_zero_len() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
int len = 0; // invalid!
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf != MAP_FAILED) {
THROW_ERROR("mmap with zero len should have been failed");
}
return 0;
}
int test_anonymous_mmap_with_non_page_aligned_len() {
int len = PAGE_SIZE + 17; // length need not to be page aligned!
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap with non-page aligned len should have worked");
}
// Even the length is not page aligned, the page mmaping is done in pages
if (check_bytes_in_buf(buf, ALIGN_UP(len, PAGE_SIZE), 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros");
}
int ret = munmap(buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
// ============================================================================
// Test cases for file-backed mmap
// ============================================================================
int test_file_mmap() {
const char *file_path = "/root/mmap_file.data";
int fd = open(file_path, O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (fd < 0) {
THROW_ERROR("file creation failed");
}
int file_len = 12 * KB + 128;
int byte_val = 0xab;
fill_file_with_repeated_bytes(fd, file_len, byte_val);
close(fd);
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE;
fd = open(file_path, O_RDONLY);
if (fd < 0) {
THROW_ERROR("file open failed");
}
off_t offset = 0;
for (size_t len = PAGE_SIZE; len <= file_len; len *= 2) {
char *buf = mmap(NULL, len, prot, flags, fd, offset);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
if (check_bytes_in_buf(buf, len, byte_val) < 0) {
THROW_ERROR("the buffer is not initialized according to the file");
}
int ret = munmap(buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
}
close(fd);
unlink(file_path);
return 0;
}
int test_file_mmap_with_offset() {
const char *file_path = "/root/mmap_file.data";
int fd = open(file_path, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (fd < 0) {
THROW_ERROR("file creation failed");
}
size_t first_len = 4 * KB + 47;
int first_val = 0xab;
fill_file_with_repeated_bytes(fd, first_len, first_val);
size_t second_len = 9 * KB - 47;
int second_val = 0xcd;
fill_file_with_repeated_bytes(fd, second_len, second_val);
size_t file_len = first_len + second_len;
off_t offset = 4 * KB;
int len = file_len - offset + 1 * KB;
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE;
assert(offset <= first_len);
char *buf = mmap(NULL, len, prot, flags, fd, offset);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
char *buf_cursor = buf;
if (check_bytes_in_buf(buf_cursor, first_len - offset, first_val) < 0) {
THROW_ERROR("the buffer is not initialized according to the file");
}
buf_cursor += first_len - offset;
if (check_bytes_in_buf(buf_cursor, second_len, second_val) < 0) {
THROW_ERROR("the buffer is not initialized according to the file");
}
buf_cursor += second_len;
if (check_bytes_in_buf(buf_cursor, ALIGN_UP(len, PAGE_SIZE) - (buf_cursor - buf),
0) < 0) {
THROW_ERROR("the remaining of the last page occupied by the buffer is not initialized to zeros");
}
int ret = munmap(buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
close(fd);
unlink(file_path);
return 0;
}
int test_file_mmap_with_invalid_fd() {
size_t len = PAGE_SIZE;
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE;
int fd = 1234; // invalid!
off_t offset = 0;
void *buf = mmap(NULL, len, prot, flags, fd, offset);
if (buf != MAP_FAILED) {
THROW_ERROR("file mmap with an invalid fd should have been failed");
}
return 0;
}
int test_file_mmap_with_non_page_aligned_offset() {
const char *file_path = "/root/mmap_file.data";
int fd = open(file_path, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (fd < 0) {
THROW_ERROR("file creation failed");
}
int file_len = 12 * KB + 128;
int byte_val = 0xab;
fill_file_with_repeated_bytes(fd, file_len, byte_val);
size_t len = PAGE_SIZE;
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
off_t offset = PAGE_SIZE + 127; // Invalid!
void *buf = mmap(NULL, len, prot, flags, fd, offset);
if (buf != MAP_FAILED) {
THROW_ERROR("mmap with non-page aligned len should have been failed");
}
close(fd);
unlink(file_path);
return 0;
}
// TODO: what if offset > file size or offset + len > file size?
// ============================================================================
// Test cases for fixed mmap
// ============================================================================
int test_fixed_mmap_that_does_not_override_any_mmaping() {
size_t hint = HINT_BEGIN + (HINT_END - HINT_BEGIN) / 3;
hint = ALIGN_DOWN(hint, PAGE_SIZE);
size_t len = (HINT_END - HINT_BEGIN) / 3;
len = ALIGN_UP(len, PAGE_SIZE);
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
void *addr = mmap((void *)hint, len, prot, flags, -1, 0);
if (addr != (void *)hint) {
THROW_ERROR("mmap with fixed address failed");
}
int ret = munmap(addr, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
int test_fixed_mmap_that_overrides_existing_mmaping() {
// We're about to allocate two buffers: parent_buf and child_buf.
// The child_buf will override a range of memory that has already
// been allocated to the parent_buf.
size_t parent_len = 10 * PAGE_SIZE;
size_t pre_child_len = 2 * PAGE_SIZE, post_child_len = 3 * PAGE_SIZE;
size_t child_len = parent_len - pre_child_len - post_child_len;
// Allocate parent_buf
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
void *parent_buf = mmap(NULL, parent_len, prot, flags, -1, 0);
if (parent_buf == MAP_FAILED) {
THROW_ERROR("mmap for parent failed");
}
int parent_val = 0xab;
memset(parent_buf, parent_val, parent_len);
// Allocate child_buf
void *child_buf = (char *)parent_buf + pre_child_len;
if (mmap(child_buf, child_len, prot, flags | MAP_FIXED, -1, 0) != child_buf) {
THROW_ERROR("mmap with fixed address failed");
}
// Check that child_buf, which overrides parent_buf, is initialized to zeros
if (check_bytes_in_buf(child_buf, child_len, 0) < 0) {
THROW_ERROR("the content of child mmap memory is not initialized");
}
// Check that the rest of parent_buf are kept intact
if (check_bytes_in_buf((char *)child_buf - pre_child_len,
pre_child_len, parent_val) < 0 ||
check_bytes_in_buf((char *)child_buf + child_len,
post_child_len, parent_val) < 0) {
THROW_ERROR("the content of parent mmap memory is broken");
}
// Deallocate parent_buf along with child_buf
int ret = munmap(parent_buf, parent_len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
int test_fixed_mmap_with_non_page_aligned_addr() {
size_t hint = HINT_BEGIN + 123; // Not aligned!
size_t len = 1 * PAGE_SIZE;
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
void *addr = mmap((void *)hint, len, prot, flags, -1, 0);
if (addr != MAP_FAILED) {
THROW_ERROR("fixed mmap with non-page aligned hint should have failed");
}
return 0;
}
// ============================================================================
// Test cases for munmap
// ============================================================================
static int check_buf_is_munmapped(void *target_addr, size_t len) {
// The trivial case of zero-len meory region is considered as unmapped
if (len == 0) { return 0; }
// If the target_addr is not already mmaped, it should succeed to use it as
// a hint for mmap.
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
void *real_addr = mmap(target_addr, len, prot, flags, -1, 0);
if (real_addr != target_addr) {
THROW_ERROR("address is already mmaped");
}
munmap(target_addr, len);
return 0;
}
static int mmap_then_munmap(size_t mmap_len, ssize_t munmap_offset, size_t munmap_len) {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
// Make sure that we are manipulating memory between [HINT_BEGIN, HINT_END)
void *mmap_addr = (void *)(munmap_offset >= 0 ? HINT_BEGIN : HINT_BEGIN - munmap_offset);
if (mmap(mmap_addr, mmap_len, prot, flags, -1, 0) != mmap_addr) {
THROW_ERROR("mmap failed");
}
void *munmap_addr = (char *)mmap_addr + munmap_offset;
if (munmap(munmap_addr, munmap_len) < 0) {
THROW_ERROR("munmap failed");
}
if (check_buf_is_munmapped(munmap_addr, munmap_len) < 0) {
THROW_ERROR("munmap does not really free the memory");
}
// Make sure that when this function returns, there are no memory mappings
// within [HINT_BEGIN, HINT_END)
if (munmap((void *)HINT_BEGIN, HINT_END - HINT_BEGIN) < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
int test_munmap_whose_range_is_a_subset_of_a_mmap_region() {
size_t mmap_len = 4 * PAGE_SIZE;
ssize_t munmap_offset = 1 * PAGE_SIZE;
size_t munmap_len = 2 * PAGE_SIZE;
if (mmap_then_munmap(mmap_len, munmap_offset, munmap_len) < 0) {
THROW_ERROR("first mmap and then munmap failed");
}
return 0;
}
int test_munmap_whose_range_is_a_superset_of_a_mmap_region() {
size_t mmap_len = 4 * PAGE_SIZE;
ssize_t munmap_offset = -2 * PAGE_SIZE;
size_t munmap_len = 7 * PAGE_SIZE;
if (mmap_then_munmap(mmap_len, munmap_offset, munmap_len) < 0) {
THROW_ERROR("first mmap and then munmap failed");
}
return 0;
}
int test_munmap_whose_range_intersects_with_a_mmap_region() {
size_t mmap_len = 200 * PAGE_SIZE;
size_t munmap_offset = 100 * PAGE_SIZE + 10 * PAGE_SIZE;
size_t munmap_len = 4 * PAGE_SIZE;
if (mmap_then_munmap(mmap_len, munmap_offset, munmap_len) < 0) {
THROW_ERROR("first mmap and then munmap failed");
}
return 0;
}
int test_munmap_whose_range_intersects_with_no_mmap_regions() {
size_t mmap_len = 1 * PAGE_SIZE;
size_t munmap_offset = 1 * PAGE_SIZE;
size_t munmap_len = 1 * PAGE_SIZE;
if (mmap_then_munmap(mmap_len, munmap_offset, munmap_len) < 0) {
THROW_ERROR("first mmap and then munmap failed");
}
return 0;
}
int test_munmap_whose_range_intersects_with_multiple_mmap_regions() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
size_t mmap_len1 = 100 * PAGE_SIZE;
void *mmap_addr1 = mmap(NULL, mmap_len1, prot, flags, -1, 0);
if (mmap_addr1 == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
size_t mmap_len2 = 12 * PAGE_SIZE;
void *mmap_addr2 = mmap(NULL, mmap_len2, prot, flags, -1, 0);
if (mmap_addr2 == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
size_t mmap_min = MIN((size_t)mmap_addr1, (size_t)mmap_addr2);
size_t mmap_max = MAX((size_t)mmap_addr1 + mmap_len1,
(size_t)mmap_addr2 + mmap_len2);
void *munmap_addr = (void *)mmap_min;
size_t munmap_len = mmap_max - mmap_min;
if (munmap(munmap_addr, munmap_len) < 0) {
THROW_ERROR("munmap failed");
}
if (check_buf_is_munmapped(munmap_addr, munmap_len) < 0) {
THROW_ERROR("munmap does not really free the memory");
}
return 0;
}
int test_munmap_with_null_addr() {
// Set the address for munmap to NULL!
//
// The man page of munmap states that "it is not an error if the indicated
// range does not contain any mapped pages". This is not considered as
// an error!
void *munmap_addr = NULL;
size_t munmap_len = PAGE_SIZE;
if (munmap(munmap_addr, munmap_len) < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
int test_munmap_with_zero_len() {
void *munmap_addr = (void *)HINT_BEGIN;
// Set the length for munmap to 0! This is invalid!
size_t munmap_len = 0;
if (munmap(munmap_addr, munmap_len) == 0) {
THROW_ERROR("munmap with zero length should have failed");
}
return 0;
}
int test_munmap_with_non_page_aligned_len() {
size_t mmap_len = 2 * PAGE_SIZE;
size_t munmap_offset = 0;
// Set the length for munmap to a non-page aligned value!
//
// The man page of munmap states that "the address addr must be a
// multiple of the page size (but length need not be). All pages
// containing a part of the indicated range are unmapped". So this is
// not considered as an error!
size_t munmap_len = 1 * PAGE_SIZE + 123;
if (mmap_then_munmap(mmap_len, munmap_offset, munmap_len) < 0) {
THROW_ERROR("first mmap and then munmap failed");
}
return 0;
}
// ============================================================================
// Test cases for mremap
// ============================================================================
int test_mremap() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
for (size_t len = PAGE_SIZE; len < MAX_MMAP_USED_MEMORY; len *= 2) {
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
if (check_bytes_in_buf(buf, len, 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros");
}
void *expand_buf = mremap(buf, len, 2 * len, MREMAP_MAYMOVE);
if (expand_buf == MAP_FAILED) {
THROW_ERROR("mremap with big size failed");
}
if (check_bytes_in_buf(expand_buf, len, 0) < 0) {
THROW_ERROR("the old part of expand buffer is not zero");
}
memset(expand_buf, 'a', len * 2);
void *shrink_buf = mremap(expand_buf, 2 * len, len, 0);
if (shrink_buf == MAP_FAILED) {
THROW_ERROR("mmap with small size failed");
}
if (check_bytes_in_buf(shrink_buf, len, 'a') < 0) {
THROW_ERROR("the shrink buffer is not correct");
}
int ret = munmap(shrink_buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
}
return 0;
}
int test_mremap_subrange() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
size_t len = PAGE_SIZE * 4;
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
if (check_bytes_in_buf(buf, len, 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros");
}
/* remap a subrange in the buffer */
void *new_part_buf = mremap(buf + len / 4, len / 4, len, MREMAP_MAYMOVE);
if (new_part_buf == MAP_FAILED) {
THROW_ERROR("mremap with subrange failed");
}
if (check_bytes_in_buf(new_part_buf, len / 4, 0) < 0) {
THROW_ERROR("the old part of buffer is not zero");
}
void *rear_buf = buf + len / 2;
/* now the length of rear buffer is (len / 2), remap the second part */
void *new_part_rear_buf = mremap(rear_buf + len / 4, len / 4, len, MREMAP_MAYMOVE);
if (new_part_rear_buf == MAP_FAILED) {
THROW_ERROR("mremap with rear subrange failed");
}
if (check_bytes_in_buf(new_part_rear_buf, len / 4, 0) < 0) {
THROW_ERROR("the old part of rear buffer is not zero");
}
int ret = munmap(buf, len / 4) || munmap(new_part_buf, len) ||
munmap(rear_buf, len / 4) || munmap(new_part_rear_buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
// FIXME: may cause segfault on Linux
int test_mremap_with_fixed_addr() {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
size_t len = PAGE_SIZE * 2;
void *buf = mmap(NULL, len, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
THROW_ERROR("mmap failed");
}
if (check_bytes_in_buf(buf, len, 0) < 0) {
THROW_ERROR("the buffer is not initialized to zeros");
}
void *new_addr = buf + len * 2;
void *new_buf = mremap(buf, len, len, MREMAP_FIXED, new_addr);
if (new_buf != MAP_FAILED || errno != EINVAL) {
THROW_ERROR("check mremap with invalid flags failed");
}
new_buf = mremap(buf, len, len, MREMAP_FIXED | MREMAP_MAYMOVE, buf);
if (new_buf != MAP_FAILED || errno != EINVAL) {
THROW_ERROR("check mremap with overlap addr failed");
}
new_buf = mremap(buf, len, len, MREMAP_FIXED | MREMAP_MAYMOVE, new_addr);
if (new_buf == MAP_FAILED) {
THROW_ERROR("mmap with a fixed address failed");
}
if (check_bytes_in_buf(new_buf, len, 0) < 0) {
THROW_ERROR("the new buffer is not zero");
}
int ret = munmap(new_buf, len);
if (ret < 0) {
THROW_ERROR("munmap failed");
}
return 0;
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_anonymous_mmap),
TEST_CASE(test_anonymous_mmap_randomly),
TEST_CASE(test_anonymous_mmap_randomly_with_good_hints),
TEST_CASE(test_anonymous_mmap_with_bad_hints),
TEST_CASE(test_anonymous_mmap_with_zero_len),
TEST_CASE(test_anonymous_mmap_with_non_page_aligned_len),
TEST_CASE(test_file_mmap),
TEST_CASE(test_file_mmap_with_offset),
TEST_CASE(test_file_mmap_with_invalid_fd),
TEST_CASE(test_file_mmap_with_non_page_aligned_offset),
TEST_CASE(test_fixed_mmap_that_does_not_override_any_mmaping),
TEST_CASE(test_fixed_mmap_that_overrides_existing_mmaping),
TEST_CASE(test_fixed_mmap_with_non_page_aligned_addr),
TEST_CASE(test_munmap_whose_range_is_a_subset_of_a_mmap_region),
TEST_CASE(test_munmap_whose_range_is_a_superset_of_a_mmap_region),
TEST_CASE(test_munmap_whose_range_intersects_with_a_mmap_region),
TEST_CASE(test_munmap_whose_range_intersects_with_no_mmap_regions),
TEST_CASE(test_munmap_whose_range_intersects_with_multiple_mmap_regions),
TEST_CASE(test_munmap_with_null_addr),
TEST_CASE(test_munmap_with_zero_len),
TEST_CASE(test_munmap_with_non_page_aligned_len),
TEST_CASE(test_mremap),
TEST_CASE(test_mremap_subrange),
TEST_CASE(test_mremap_with_fixed_addr),
};
int main() {
if (test_suite_init() < 0) {
THROW_ERROR("test_suite_init failed");
}
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}