aboutsummaryrefslogtreecommitdiff
path: root/user/usr/bin/tests/memtest.c
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /user/usr/bin/tests/memtest.c
Created student weenix repository
Diffstat (limited to 'user/usr/bin/tests/memtest.c')
-rw-r--r--user/usr/bin/tests/memtest.c816
1 files changed, 816 insertions, 0 deletions
diff --git a/user/usr/bin/tests/memtest.c b/user/usr/bin/tests/memtest.c
new file mode 100644
index 0000000..eee16ed
--- /dev/null
+++ b/user/usr/bin/tests/memtest.c
@@ -0,0 +1,816 @@
+/*
+ * Test correct user space memory management, particularly segfaults
+ * Tests fun cases of mmap, munmap, and brk
+ * -- Alvin Kerber (alvin)
+ *
+ * Modified by twd in 7/2018 to work with improved address space layout.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <weenix/syscall.h>
+
+#include <test/test.h>
+
+/* Shared header trickery */
+#include "page.h"
+
+#include "linkermagic.h"
+
+#define syscall_success(expr) \
+ test_assert(0 <= (expr), "\nunexpected error: %s (%d)", \
+ test_errstr(errno), errno)
+
+/* Helpful defines */
+#define assert_fault(statement, msg) \
+ do \
+ { \
+ int __status; \
+ test_fork_begin() \
+ { \
+ statement; \
+ return 0; \
+ } \
+ test_fork_end(&__status); \
+ test_assert(EFAULT == __status, \
+ "Unexpected lack of segfault on " #statement " : " msg); \
+ } while (0);
+
+#define assert_nofault(statement, msg) \
+ do \
+ { \
+ int __status; \
+ test_fork_begin() \
+ { \
+ statement; \
+ return 0; \
+ } \
+ test_fork_end(&__status); \
+ test_assert(0 == __status, \
+ "Unexpected segfault on " #statement " : " msg); \
+ } while (0);
+
+static char root_dir[64];
+
+/* Overflow the stack */
+static void overflow(void)
+{
+ int junk[1000];
+ overflow();
+}
+
+static int test_overflow(void)
+{
+ printf("Testing stack overflow\n");
+ assert_fault(overflow(), "Stack overflow");
+ return 0;
+}
+
+static int test_mmap_bounds(void)
+{
+ int fd, status;
+ void *addr;
+
+ printf("Testing boundaries and permissions of mmap()\n");
+
+ test_assert(0 < (fd = open("/dev/zero", O_RDWR, 0)), NULL);
+ test_assert(
+ MAP_FAILED != (addr = mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0)),
+ NULL);
+ /* Make sure we can actually access these addresses */
+ test_assert('\0' == *(char *)addr, NULL);
+ test_assert('\0' == *((char *)addr + PAGE_SIZE), NULL);
+ test_assert('\0' == *((char *)addr + PAGE_SIZE * 2), NULL);
+ test_assert('\0' == *((char *)addr + PAGE_SIZE * 3 - 1), NULL);
+
+ /* Unmap the ends */
+ test_assert(0 == munmap(addr, PAGE_SIZE), NULL);
+ test_assert(0 == munmap((char *)addr + PAGE_SIZE * 2, PAGE_SIZE), NULL);
+
+ /* Adjust to center, now surrounded by unmapped regions */
+ addr = (char *)addr + PAGE_SIZE;
+
+ /* Make sure we didn't unmap the middle */
+ test_assert('\0' == *((char *)addr), NULL);
+ test_assert('\0' == *((char *)addr + PAGE_SIZE - 1), NULL);
+ assert_nofault(*(char *)addr = 'a', "");
+ assert_nofault(*((char *)addr + PAGE_SIZE - 1) = 'b', "");
+
+ /* Regions around it are unmapped */
+ assert_fault(char foo = *((char *)addr + PAGE_SIZE), "");
+ assert_fault(char foo = *((char *)addr - PAGE_SIZE), "");
+ assert_fault(char foo = *((char *)addr - 1), "");
+ assert_fault(*((char *)addr + PAGE_SIZE) = 'a', "");
+ assert_fault(*((char *)addr - 1) = 'a', "");
+ assert_fault(*((char *)addr + PAGE_SIZE * 2 - 1) = 'a', "");
+
+ /* Remap as read-only */
+ test_assert(
+ addr == mmap(addr, 1, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0), NULL);
+
+ assert_fault(*((char *)addr) = 'a', "");
+ assert_fault(*((char *)addr + PAGE_SIZE - 1) = 'a', "");
+
+ /* "Unmap" */
+ test_assert(0 == munmap((char *)addr - PAGE_SIZE, PAGE_SIZE), NULL);
+ test_assert(0 == munmap((char *)addr + PAGE_SIZE, PAGE_SIZE), NULL);
+
+ /* Make sure it's still there, also that it's overwritten */
+ test_assert('\0' == *((char *)addr), NULL);
+ test_assert('\0' == *((char *)addr + PAGE_SIZE - 1), NULL);
+
+ /* Unmap for real */
+ test_assert(0 == munmap(addr, 1), NULL);
+
+ assert_fault(char foo = *(char *)addr, "");
+ assert_fault(char foo = *((char *)addr + PAGE_SIZE - 1), "");
+
+ /* Test fun permissions */
+ test_assert(addr == mmap(addr, PAGE_SIZE, PROT_EXEC,
+ MAP_PRIVATE | MAP_FIXED, fd, 0),
+ NULL);
+ assert_fault(char foo = *(char *)addr, "");
+ assert_fault(char foo = *((char *)addr + PAGE_SIZE - 1), "");
+ assert_fault(*((char *)addr) = 'a', "");
+
+ test_assert(
+ addr == mmap(addr, PAGE_SIZE, 0, MAP_PRIVATE | MAP_FIXED, fd, 0), NULL);
+ assert_fault(char foo = *(char *)addr, "");
+ assert_fault(char foo = *((char *)addr + PAGE_SIZE - 1), "");
+ assert_fault(*((char *)addr) = 'a', "");
+
+ return 0;
+}
+
+static int test_brk_bounds(void)
+{
+ void *oldbrk, *newbrk;
+ int status;
+
+ printf("Testing boundaries and permissions of brk()\n");
+
+ /* "Stabilize" our old brk at a page boundary */
+ test_assert((void *)-1 != (oldbrk = sbrk(0)), NULL);
+ oldbrk = PAGE_ALIGN_UP(oldbrk);
+ test_assert(0 == brk(oldbrk), NULL);
+
+ /* Look at next page-aligned addr */
+ newbrk = (char *)oldbrk + PAGE_SIZE;
+
+ assert_fault(char foo = *(char *)newbrk, "");
+ assert_fault(*(char *)newbrk = 'a', "");
+
+ /* Move brk to next page-aligned addr */
+ test_assert(0 == brk(newbrk), NULL);
+
+ /* Access the new memory */
+ test_assert('\0' == *(char *)oldbrk, NULL);
+ test_assert('\0' == *((char *)newbrk - 1), NULL);
+ *((char *)newbrk - 1) = 'a';
+
+ assert_fault(char foo = *(char *)newbrk, "");
+ assert_fault(*(char *)newbrk = 'a', "");
+
+ /* Move brk up by 1 byte */
+ test_assert(0 == brk((char *)newbrk + 1), NULL);
+
+ /* Access the new memory */
+ test_assert('\0' == *(char *)newbrk, NULL);
+ test_assert('\0' == *((char *)newbrk + PAGE_SIZE - 1), NULL);
+ assert_nofault(*(char *)newbrk = 'b', "");
+
+ /* Old memory didn't change */
+ test_assert('a' == *((char *)newbrk - 1), NULL);
+
+ /* Move it back */
+ test_assert(0 == brk(newbrk), NULL);
+
+ assert_fault(char foo = *(char *)newbrk, "");
+ assert_fault(*(char *)newbrk = 'a', "");
+
+ /* Move it up, make sure region wiped. Note that the actual wipe test is
+ * 'evil' and is in eviltest. This just checks to make sure the brk region
+ * is private mapped (modified in subprocesses) */
+ test_assert(0 == brk((char *)newbrk + PAGE_SIZE), NULL);
+ test_assert('\0' == *(char *)newbrk, NULL);
+ test_assert('\0' == *((char *)newbrk + PAGE_SIZE - 1), NULL);
+
+ /* Move it down by 1 byte */
+ test_assert(0 == brk((char *)newbrk - 1), NULL);
+
+ /* Access still-accessible memory */
+ test_assert('a' == *((char *)newbrk - 1), NULL);
+ *((char *)newbrk - 2) = 'z';
+
+ /* Move brk to multiple addrs on same page, make sure page remains */
+ test_assert(0 == brk((char *)newbrk - 1000), NULL);
+ test_assert('z' == *((char *)newbrk - 2), NULL);
+ test_assert(0 == brk((char *)oldbrk + 1), NULL);
+ test_assert('z' == *((char *)newbrk - 2), NULL);
+ test_assert(0 == brk((char *)oldbrk + 1000), NULL);
+ test_assert('a' == *((char *)newbrk - 1), NULL);
+
+ return 0;
+}
+
+static int test_munmap(void)
+{
+ char *addr, *middle;
+
+ printf("Testing munmap()\n");
+
+ /* Map lots of areas. We're kind of lazy, so for now they're all anonymous
+ */
+ test_assert(
+ MAP_FAILED != (addr = mmap(NULL, PAGE_SIZE * 20, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0)),
+ NULL);
+
+ *(addr + PAGE_SIZE * 8) = '^';
+ *(addr + PAGE_SIZE * 12) = '$';
+
+ /* Make sure TLB / page tables are cleared on unmap */
+ assert_fault(*addr = 'a'; munmap(addr, PAGE_SIZE); char foo = *addr;, "");
+ assert_fault(*addr = 'a'; munmap(addr, PAGE_SIZE * 20); char foo = *addr;
+ , "");
+ assert_fault(*(addr + PAGE_SIZE * 10) = 'a';
+ munmap(addr + PAGE_SIZE * 10, PAGE_SIZE * 5);
+ char foo = *(addr + PAGE_SIZE * 10);, "");
+
+ /* Overwrite middle of area (implicit unmap) */
+ test_assert(
+ MAP_FAILED != (middle = mmap(addr + PAGE_SIZE * 10, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON | MAP_FIXED, -1, 0)),
+ NULL);
+
+ /* Make sure we overwrote the middle but not the whole thing */
+ test_assert('\0' == *middle, NULL);
+ assert_nofault(*middle = 'a', "");
+ test_assert('a' == *middle, NULL);
+
+ test_assert('\0' == *(addr + PAGE_SIZE * 9), NULL);
+ assert_nofault(*(addr + PAGE_SIZE * 9) = 'a', "");
+ test_assert('\0' == *(addr + PAGE_SIZE * 9), NULL);
+
+ test_assert('\0' == *(addr + PAGE_SIZE * 11), NULL);
+ assert_nofault(*(addr + PAGE_SIZE * 11) = 'a', "");
+ test_assert('\0' == *(addr + PAGE_SIZE * 11), NULL);
+
+ test_assert('\0' == *addr, NULL);
+ test_assert('\0' == *(addr + PAGE_SIZE * 20 - 1), NULL);
+
+ /* Make sure the offsets are appropriate */
+ test_assert('^' == *(addr + PAGE_SIZE * 8), NULL);
+ test_assert('$' == *(addr + PAGE_SIZE * 12), NULL);
+
+ /* Unmap a weird overlapping region */
+ test_assert(0 == munmap(addr + PAGE_SIZE * 9, PAGE_SIZE * 3), NULL);
+
+ /* Make sure everything's gone */
+ assert_fault(char foo = *(addr + PAGE_SIZE * 9), "");
+ assert_fault(char foo = *(addr + PAGE_SIZE * 10), "");
+ assert_fault(char foo = *(addr + PAGE_SIZE * 12 - 1), "");
+
+ /* Make sure offsets are still correct */
+ test_assert('^' == *(addr + PAGE_SIZE * 8), NULL);
+ test_assert('$' == *(addr + PAGE_SIZE * 12), NULL);
+
+ /* Unmap nothing at all */
+ test_assert(0 == munmap(addr + PAGE_SIZE * 10, PAGE_SIZE), NULL);
+ test_assert(0 == munmap(addr + PAGE_SIZE * 9, PAGE_SIZE * 3), NULL);
+
+ /* Unmap almost the whole (remaining) thing */
+ test_assert(0 == munmap(addr + PAGE_SIZE, PAGE_SIZE * 19), NULL);
+
+ /* Make sure the beginning's still there */
+ test_assert('\0' == *addr, NULL);
+
+ /* Finish up, make sure everything's gone */
+ test_assert(0 == munmap(addr, PAGE_SIZE * 15), NULL);
+ assert_fault(char foo = *(addr + PAGE_SIZE), "");
+ assert_fault(char foo = *(addr), "");
+ assert_fault(char foo = *(addr + PAGE_SIZE * 20 - 1), "");
+
+ return 0;
+}
+
+static int test_start_brk(void)
+{
+ printf("Testing using brk() near starting brk\n");
+ test_assert(bss_end == sbrk(0), "brk should not have moved yet");
+ test_assert(!PAGE_ALIGNED(bss_end) && !PAGE_ALIGNED((char *)bss_end + 1),
+ "starting brk is page aligned; test is too easy...");
+
+ /* Up to next page boundary should already be accessible (end of bss) */
+ char *oldbrk = PAGE_ALIGN_UP(bss_end);
+ test_assert('\0' == *(oldbrk - 1), NULL);
+ *(oldbrk - 1) = 'a';
+ assert_fault(char foo = *oldbrk, "");
+
+ /* Move brk up to next page boundary */
+ test_assert(0 == brk(oldbrk), NULL);
+ test_assert('a' == *(oldbrk - 1), NULL);
+ *(oldbrk - 1) = 'b';
+ assert_fault(char foo = *oldbrk, "");
+ assert_fault(char foo = *(oldbrk + PAGE_SIZE), "");
+
+ /* Try to move before starting brk */
+ test_assert(0 != brk((char *)bss_end - 1), NULL);
+ test_assert(0 != brk(PAGE_ALIGN_DOWN(bss_end)), NULL);
+
+ /* Move it up another page */
+ char *newbrk = oldbrk + PAGE_SIZE;
+ test_assert(0 == brk(newbrk), NULL);
+
+ /* Make sure everything accessible (read/write) */
+ test_assert('b' == *(oldbrk - 1), NULL);
+ test_assert('\0' == *oldbrk, NULL);
+ test_assert('\0' == *(newbrk - 1), NULL);
+ *oldbrk = 'z';
+ *(newbrk - 1) = 'y';
+ assert_fault(char foo = *newbrk, "");
+ assert_fault(char foo = *(newbrk + PAGE_SIZE), "");
+
+ /* Try to move before starting brk */
+ test_assert(0 != brk((char *)bss_end - 1), NULL);
+ test_assert(0 != brk(PAGE_ALIGN_DOWN(bss_end)), NULL);
+
+ /* Move back to starting brk */
+ test_assert(0 == brk((char *)bss_end + 1), NULL);
+ /* Make sure region is gone */
+ test_assert('b' == *(oldbrk - 1), NULL);
+ assert_fault(char foo = *oldbrk, "");
+ assert_fault(char foo = *newbrk, "");
+
+ /* Move it up, make sure we have new clean region */
+ test_assert(0 == brk(oldbrk + 1), NULL);
+ /* This behavior is undefined, this represents how it
+ * works on Linux but these need not pass
+ */
+ /*test_assert('\0' == *oldbrk, NULL);
+ test_assert('\0' == *(newbrk - 1), NULL);*/
+ assert_fault(char foo = *newbrk, "");
+
+ /* Move back and finish */
+ test_assert(0 == brk(bss_end), NULL);
+ test_assert('b' == *(oldbrk - 1), NULL);
+
+ return 0;
+}
+
+static int test_brk_mmap(void)
+{
+ printf("Testing interactions of brk() and mmap()\n");
+ test_assert(bss_end == sbrk(0), "brk should not have moved yet");
+ char *oldbrk = PAGE_ALIGN_UP(bss_end);
+
+ /* Put a mapping in the way */
+ test_assert(MAP_FAILED != mmap(oldbrk, PAGE_SIZE * 2, PROT_READ,
+ MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0),
+ NULL);
+ /* Mapping is there */
+ test_assert('\0' == *oldbrk, NULL);
+ test_assert('\0' == *(oldbrk - 1), NULL);
+
+ /* Moving brk without getting area is fine */
+ test_assert(0 == brk(oldbrk), NULL);
+ test_assert('\0' == *oldbrk, NULL);
+ test_assert('\0' == *(oldbrk - 1), NULL);
+ test_assert(0 == brk((char *)bss_end + 1), NULL);
+ test_assert('\0' == *oldbrk, NULL);
+ test_assert('\0' == *(oldbrk - 1), NULL);
+
+ /* But can't move it up at all */
+ test_assert(0 != brk(oldbrk + 1), NULL);
+ test_assert(0 != brk(oldbrk + PAGE_SIZE), NULL);
+ test_assert(0 != brk(oldbrk + PAGE_SIZE * 2), NULL);
+ test_assert(0 != brk(oldbrk + PAGE_SIZE * 3), NULL);
+
+ /* Make it smaller */
+ test_assert(0 == munmap(oldbrk, PAGE_SIZE), NULL);
+ /* Region inaccessible */
+ assert_fault(char foo = *oldbrk, "");
+ assert_fault(char foo = *(oldbrk + PAGE_SIZE - 1), "");
+
+ /* Expand brk accordingly */
+ test_assert(0 == brk(oldbrk + PAGE_SIZE), NULL);
+ test_assert('\0' == *oldbrk, NULL);
+ test_assert('\0' == *(oldbrk + PAGE_SIZE - 1), NULL);
+ *oldbrk = 'a';
+
+ /* Can't go too far */
+ test_assert(0 != brk(oldbrk + PAGE_SIZE + 1), NULL);
+ test_assert(0 != brk(oldbrk + PAGE_SIZE * 2), NULL);
+ test_assert(0 != brk(oldbrk + PAGE_SIZE * 3), NULL);
+
+ return 0;
+}
+
+static int test_mmap_fill(void)
+{
+ printf("Testing filling up virtual address space\n");
+ char *hi, *lo, *addr;
+ /* map something and remove it to find out how high we can go */
+ test_assert(
+ MAP_FAILED != (hi = mmap(NULL, 1, 0, MAP_ANON | MAP_PRIVATE, -1, 0)),
+ NULL);
+ test_assert(0 == munmap(hi, 1), NULL);
+ hi += PAGE_SIZE;
+
+ test_assert(bss_end == sbrk(0), NULL);
+ lo = PAGE_ALIGN_UP(bss_end);
+
+ /* Fill this up with 2 mappings */
+#define MID_ADDR ((char *)0x80000000)
+ if (MID_ADDR > lo)
+ {
+ test_assert(
+ MID_ADDR == mmap(NULL,
+ (size_t)((uintptr_t)hi - (uintptr_t)MID_ADDR), 0,
+ MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+ }
+ if (MID_ADDR < hi)
+ {
+ test_assert(
+ lo == mmap(NULL, (size_t)((uintptr_t)MID_ADDR - (uintptr_t)lo), 0,
+ MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+ }
+
+ /* mmap file below stack */
+ test_assert(MAP_FAILED != (addr = mmap(NULL, 1, PROT_READ,
+ MAP_ANON | MAP_PRIVATE, -1, 0)),
+ NULL);
+ test_assert((uintptr_t)addr < (uintptr_t)&addr, NULL);
+ test_assert('\0' == *addr, NULL);
+ /* mmap fixed on top of it */
+ test_assert(MAP_FAILED != mmap(addr, 1, PROT_READ,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+ test_assert('\0' == *addr, NULL);
+
+ /* Try something too big */
+ test_assert(MAP_FAILED ==
+ mmap(NULL, (size_t)addr, 0, MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+
+ /* Fill up the entire remaining space */
+#ifdef old
+ test_assert(
+ MAP_FAILED != mmap(NULL,
+ (size_t)((uintptr_t)addr - (uintptr_t)USER_MEM_LOW),
+ 0, MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+#else
+ // all space beyond break is already taken
+#endif
+
+#ifdef old
+ /* No space left */
+ test_assert(MAP_FAILED == mmap(NULL, 1, 0, MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+
+ /* Make some space, we should fill it */
+ test_assert(0 == munmap(addr, 1), NULL);
+ test_assert(addr == mmap(NULL, 1, PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+ test_assert('\0' == *addr, NULL);
+
+ test_assert(MAP_FAILED == mmap(NULL, 1, 0, MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+#endif
+
+ /* Clean out some more space */
+ test_assert(0 == munmap(MID_ADDR - PAGE_SIZE, PAGE_SIZE * 2), NULL);
+ test_assert(MID_ADDR - PAGE_SIZE == mmap(NULL, PAGE_SIZE * 2, PROT_READ,
+ MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+ test_assert('\0' == *MID_ADDR, NULL);
+ test_assert('\0' == *(MID_ADDR - PAGE_SIZE), NULL);
+ test_assert('\0' == *(MID_ADDR + PAGE_SIZE - 1), NULL);
+
+ /* Cut into pieces, access each of them */
+ char *p;
+ for (p = lo + PAGE_SIZE; p < lo + PAGE_SIZE * 20; p += PAGE_SIZE * 2)
+ {
+ test_assert(
+ MAP_FAILED != mmap(p, 1, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0),
+ NULL);
+ test_assert('\0' == *p, NULL);
+ *p = 'a';
+ assert_fault(char foo = *(p + PAGE_SIZE), "");
+ }
+
+ // there still should be a few pages available in low memory
+ test_assert(MAP_FAILED != mmap(NULL, 1, 0, MAP_ANON | MAP_PRIVATE, -1, 0),
+ NULL);
+
+ /* Try brk too */
+ test_assert(0 == brk(lo), NULL);
+ test_assert(0 != brk(lo + 1), NULL);
+
+ /* Clean it all up */
+ test_assert(0 == munmap(lo, (size_t)((uintptr_t)hi - (uintptr_t)lo)), NULL);
+ return 0;
+}
+
+static int test_mmap_repeat(void)
+{
+#define MMAP_REPEAT_FILE "mmaprepeattest"
+#define REPEAT_STR "FooFooFoo"
+
+ int fd, i;
+ char *addrs[10];
+ printf("Testing repeated mmap() of same file\n");
+
+ /* Set up test file */
+ test_assert(-1 != (fd = open(MMAP_REPEAT_FILE, O_RDWR | O_CREAT, 0)), NULL);
+ test_assert(10 == write(fd, REPEAT_STR, 10), NULL);
+ test_assert(0 == unlink(MMAP_REPEAT_FILE), NULL);
+
+ /* map it private many times */
+ for (i = 0; i < 10; i++)
+ {
+ test_assert(MAP_FAILED != (addrs[i] = mmap(NULL, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0)),
+ NULL);
+ test_assert(!strcmp(addrs[i], REPEAT_STR), NULL);
+ }
+ /* Make sure changes don't propagate */
+ *addrs[0] = 'Z';
+ *(addrs[0] + PAGE_SIZE - 1) = 'Q';
+ for (i = 1; i < 10; i++)
+ {
+ test_assert(!strcmp(addrs[i], REPEAT_STR), NULL);
+ test_assert('\0' == *(addrs[i] + PAGE_SIZE - 1), NULL);
+ }
+
+ /* map it shared many times */
+ for (i = 0; i < 10; i++)
+ {
+ test_assert(MAP_FAILED != (addrs[i] = mmap(NULL, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)),
+ NULL);
+ test_assert(!strcmp(addrs[i], REPEAT_STR), NULL);
+ }
+ /* Make sure changes propagate */
+ *addrs[3] = 'Z';
+ *(addrs[5] + PAGE_SIZE - 1) = 'Q';
+ for (i = 0; i < 10; i++)
+ {
+ test_assert('Z' == *addrs[i], NULL);
+ test_assert('Q' == *(addrs[i] + PAGE_SIZE - 1), NULL);
+ }
+
+ return 0;
+}
+
+static int test_mmap_beyond(void)
+{
+ /* <insert evil laughter here> */
+#define MMAP_BEYOND_FILE "mmapbeyondtest"
+#define BEYOND_STR "FOOBAR!"
+
+ int fd;
+ char *addr, *addr2;
+ int status;
+
+ printf("Testing mmap() beyond end of backing object\n");
+
+ /* Set up test file */
+ test_assert(-1 != (fd = open(MMAP_BEYOND_FILE, O_RDWR | O_CREAT, 0)), NULL);
+ test_assert(8 == write(fd, BEYOND_STR, 8), NULL);
+ test_assert(0 == unlink(MMAP_BEYOND_FILE), NULL);
+
+ /* Set up test mmap */
+ test_assert(
+ MAP_FAILED != (addr = mmap(NULL, PAGE_SIZE * 10, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)),
+ NULL);
+ /* make sure it's there */
+ test_assert(!strcmp(addr, BEYOND_STR), NULL);
+
+ /* Do it again, but with private mapping. */
+ test_assert(MAP_FAILED !=
+ (addr2 = mmap(NULL, PAGE_SIZE * 10, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0)),
+ NULL);
+ /* make sure it's there */
+ test_assert(!strcmp(addr2, BEYOND_STR), NULL);
+ *addr2 = 'a';
+
+ /* Can't go too far on either */
+ assert_fault(char foo = *(addr + PAGE_SIZE), "");
+ assert_fault(char foo = *(addr + PAGE_SIZE * 5), "");
+ assert_fault(*((char *)addr + PAGE_SIZE * 5) = 'a', "");
+
+ assert_fault(char foo = *(addr2 + PAGE_SIZE), "");
+ assert_fault(char foo = *(addr2 + PAGE_SIZE * 5), "");
+ assert_fault(*(addr2 + PAGE_SIZE * 5) = 'a', "");
+
+ /* Write more to it */
+ test_assert(PAGE_SIZE * 3 == lseek(fd, PAGE_SIZE * 3, SEEK_SET), NULL);
+ test_assert(8 == write(fd, BEYOND_STR, 8), NULL);
+
+ /* Can go up to new location */
+ test_assert(!strcmp(addr, BEYOND_STR), NULL);
+ test_assert('\0' == *(addr + PAGE_SIZE), NULL);
+ test_assert('\0' == *(addr + PAGE_SIZE * 2), NULL);
+ test_assert(!strcmp(addr + PAGE_SIZE * 3, BEYOND_STR), NULL);
+
+ test_assert('a' == *addr2, NULL);
+ test_assert('\0' == *(addr2 + PAGE_SIZE), NULL);
+ test_assert('\0' == *(addr2 + PAGE_SIZE * 2), NULL);
+
+ /* Can't go beyond it */
+ assert_fault(char foo = *(addr + PAGE_SIZE * 4), "");
+ assert_fault(char foo = *(addr + PAGE_SIZE * 8), "");
+ assert_fault(*(addr + PAGE_SIZE * 5) = 'a', "");
+
+ assert_fault(char foo = *(addr2 + PAGE_SIZE * 4), "");
+ assert_fault(char foo = *(addr2 + PAGE_SIZE * 8), "");
+ assert_fault(*(addr2 + PAGE_SIZE * 5) = 'a', "");
+
+ return 0;
+}
+
+/* TODO Figure out a way to not have these be repeated. */
+/* Copied from vfstest. Linking stuff prevents use of the same file. */
+static void make_rootdir(void)
+{
+ int err;
+
+ root_dir[0] = '\0';
+ do
+ {
+ snprintf(root_dir, sizeof(root_dir), "memtest-%d", rand());
+ err = mkdir(root_dir, 0777);
+ if (err && errno != EEXIST)
+ {
+ printf("Failed to make test root directory: %s\n", strerror(errno));
+ exit(errno);
+ }
+ } while (err != 0);
+ printf("Created test root directory: ./%s\n", root_dir);
+}
+
+/* Copied from vfstest. Linking stuff prevents use of the same file. */
+static int getdent(const char *dir, dirent_t *dirent)
+{
+ int ret, fd = -1;
+
+ if (0 > (fd = open(dir, O_RDONLY, 0777)))
+ {
+ return -1;
+ }
+
+ ret = 1;
+ while (ret != 0)
+ {
+ if (0 > (ret = getdents(fd, dirent, sizeof(*dirent))))
+ {
+ return -1;
+ }
+ if (0 != strcmp(".", dirent->d_name) &&
+ 0 != strcmp("..", dirent->d_name))
+ {
+ close(fd);
+ return 1;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+/* Copied from vfstest. Linking stuff prevents use of the same file. */
+static int removeall(const char *dir)
+{
+ int ret, fd = -1;
+ dirent_t dirent;
+ stat_t status;
+
+ if (0 > chdir(dir))
+ {
+ goto error;
+ }
+
+ ret = 1;
+ while (ret != 0)
+ {
+ if (0 > (ret = getdent(".", &dirent)))
+ {
+ goto error;
+ }
+ if (0 == ret)
+ {
+ break;
+ }
+
+ if (0 > stat(dirent.d_name, &status))
+ {
+ goto error;
+ }
+
+ if (S_ISDIR(status.st_mode))
+ {
+ if (0 > removeall(dirent.d_name))
+ {
+ goto error;
+ }
+ }
+ else
+ {
+ if (0 > unlink(dirent.d_name))
+ {
+ goto error;
+ }
+ }
+ }
+
+ if (0 > chdir(".."))
+ {
+ return errno;
+ }
+
+ if (0 > rmdir(dir))
+ {
+ return errno;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ if (0 <= fd)
+ {
+ close(fd);
+ }
+
+ return errno;
+}
+
+/* Copied from vfstest. Linking stuff prevents use of the same file. */
+static void destroy_rootdir(void)
+{
+ if (0 != removeall(root_dir))
+ {
+ fprintf(stderr, "ERROR: could not remove testing root %s: %s\n",
+ root_dir, strerror(errno));
+ exit(-1);
+ }
+ printf("Removed test root directory: ./%s\n", root_dir);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 1)
+ {
+ fprintf(stderr, "USAGE: memtest\n");
+ return 1;
+ }
+ int status;
+
+ /* Make sure we found out if anything segfaults that shouldn't */
+#define childtest(fun) \
+ do \
+ { \
+ test_fork_begin() { return fun(); } \
+ test_fork_end(&status); \
+ test_assert(EFAULT != status, "Test process shouldn't segfault!"); \
+ test_assert(0 == status, "Test process returned error"); \
+ } while (0)
+
+ /* printf("Linker magic: start 0x%p, text end 0x%p, data end 0x%p, bss end
+ 0x%p\n", text_start, text_end, data_end, bss_end); */
+ test_init();
+ make_rootdir();
+ syscall_success(chdir(root_dir));
+ childtest(test_overflow);
+ childtest(test_mmap_bounds);
+ childtest(test_brk_bounds);
+ childtest(test_munmap);
+ childtest(test_start_brk);
+ childtest(test_brk_mmap);
+ // childtest(test_mmap_fill); // [+] TODO UPDATE FOR 64 BIT
+ childtest(test_mmap_repeat);
+ childtest(test_mmap_beyond);
+ syscall_success(chdir(".."));
+ destroy_rootdir();
+ test_fini();
+
+ return 0;
+}