blob: 43d60959e304ac1ff8032c9e624cef0c627f2cfc [file] [edit]
/*
* Copyright (c) 2010 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <sys/mman.h>
#include <cstdlib>
#include <cstring>
#include "native_client/tests/syscalls/test.h"
namespace {
// Use 64Kb regions in the memory tests. This is usually a whole number of
// pages. The 32Kb size is used for certain other failure modes.
const size_t k64Kbytes = 64 * 1024;
const size_t k32Kbytes = 32 * 1024;
// |MAP_ANONYMOUS| expects a filedesc of -1. While this is not strictly true
// on all systems, portable code should work this way.
const int kAnonymousFiledesc = -1;
// When |MAP_ANONYMOUS| is not specified, any negative file desc should report
// EBADF.
const int kBadFiledesc = -3;
// Try to mmap a 0-length region. This is expected to fail.
int TestZeroLengthRegion() {
START_TEST("TestZeroLengthRegion");
void* mmap_ptr = mmap(NULL,
0,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED == mmap_ptr);
EXPECT(EINVAL == errno);
END_TEST();
}
// Hand in a bad file descriptor, this test is successful if mmap() fails.
// The |MAP_ANONYMOUS| flag is deliberately not used for this test, because
// we are trying to map an actual (but bad) filedesc.
int TestBadFiledesc() {
START_TEST("TestBadFiledesc");
void* mmap_ptr = mmap(NULL,
k64Kbytes,
PROT_READ,
MAP_PRIVATE,
kBadFiledesc,
0);
EXPECT(MAP_FAILED == mmap_ptr);
EXPECT(EBADF == errno);
END_TEST();
}
// Verify that mmap does not fail if a bad hint address is passed, but
// |MMAP_FIXED| is not specified.
int TestMmapBadHint() {
START_TEST("TestMmapBadHint");
void* bad_hint = (void *) 0x123;
void* mmap_ptr = mmap(bad_hint,
k64Kbytes,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED != mmap_ptr);
EXPECT(mmap_ptr != bad_hint);
EXPECT(munmap(mmap_ptr, k64Kbytes) == 0);
END_TEST();
}
// Verify that mmap does fail if a bad hint address is passed and
// |MMAP_FIXED| is specified.
int TestMmapBadHintFixed() {
START_TEST("TestMmapBadHintFixed");
void* bad_hint = (void *) 0x123;
void* mmap_ptr = mmap(bad_hint,
k64Kbytes,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED == mmap_ptr);
END_TEST();
}
// Test mmap() and munmap(), since these often to go together. Tries to mmap
// a 64 Kb region of memory and then tests to make sure that the pages have all
// been 0-filled.
int TestMmapMunmap() {
START_TEST("TestMmapMunmap");
void* mmap_ptr = mmap(NULL,
k64Kbytes,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED != mmap_ptr);
// Create a zero-filled region on the heap for comparison with the mmaped
// region.
void* zeroes = std::malloc(k64Kbytes);
std::memset(zeroes, 0, k64Kbytes);
EXPECT(std::memcmp(mmap_ptr, zeroes, k64Kbytes) == 0);
// Attempt to release the mapped memory.
EXPECT(munmap(mmap_ptr, k64Kbytes) == 0);
std::free(zeroes);
END_TEST();
}
// munmap() of text (executable) pages should fail.
int TestMunmapText() {
START_TEST("TestMunmapText");
// Text pages start at 64K for the syscall trampolines. If this first call
// to munmap() succeeds, we will get a segfault on the next trampoline usage.
// In this case, sel_ldr will return a recognizable error code.
int rv = munmap(reinterpret_cast<void*>(k64Kbytes), k64Kbytes);
// Note: if the munmap succeeds, this code is probably not running any more.
EXPECT(rv == -1);
EXPECT(EINVAL == errno);
// Untrusted code text starts at 128K. If this second call to munmap()
// succeeds, there will be a rapid segfault and sel_ldr will return a
// recognizable error code.
rv = munmap(reinterpret_cast<void*>(k64Kbytes*2), k64Kbytes);
// Note: if the munmap succeeds, this code is probably not running any more.
EXPECT(rv == -1);
EXPECT(EINVAL == errno);
END_TEST();
}
// Verify that mmap into the NULL pointer guard page will fail. This uses the
// |MAP_FIXED| flag to try an force mmap() to pin the region at NULL.
int TestMmapNULL() {
START_TEST("TestMmapNULL");
void *mmap_ptr = mmap(NULL,
k64Kbytes,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED == mmap_ptr);
EXPECT(EINVAL == errno);
END_TEST();
}
// Attempt to allocate 32Kb, starting at 32Kb. This should fail.
int TestMmap32k() {
START_TEST("TestMmap32k");
void *mmap_ptr = mmap(reinterpret_cast<void*>(k32Kbytes),
k32Kbytes,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED == mmap_ptr);
EXPECT(EINVAL == errno);
END_TEST();
}
// Verify that mmap() with the |MAP_FIXED| flag and a non-page-aligned address
// will fail.
int TestMmapNonPageAligned() {
START_TEST("TestMmapNonPageAligned");
// Reserve some legal (page-aligned) address space in which to perform the
// test.
char* local_heap = reinterpret_cast<char*>(mmap(NULL,
k64Kbytes,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
kAnonymousFiledesc,
0));
EXPECT(MAP_FAILED != local_heap);
void* unaligned_ptr = mmap(static_cast<void*>(local_heap + 0x100),
k64Kbytes,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
kAnonymousFiledesc,
0);
EXPECT(MAP_FAILED == unaligned_ptr);
EXPECT(EINVAL == errno);
END_TEST();
}
} // namespace
// Run through the complete sequence of memory tests. Sets the exit code to
// the number of failed tests. Exit code 0 means all passed.
int main() {
int fail_count = 0;
fail_count += TestZeroLengthRegion();
fail_count += TestBadFiledesc();
fail_count += TestMmapBadHint();
fail_count += TestMmapBadHintFixed();
fail_count += TestMmapMunmap();
fail_count += TestMunmapText();
fail_count += TestMmapNULL();
fail_count += TestMmap32k();
fail_count += TestMmapNonPageAligned();
return fail_count;
}