blob: 9379b24573df8d7029c2db127081ac4c5bceee25 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <limits.h>
#include <string.h>
#include <fbl/algorithm.h>
#include <lib/fzl/memory-probe.h>
#include <unittest/unittest.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
namespace {
bool vmo_clone_size_align_test() {
BEGIN_TEST;
zx_handle_t vmo;
zx_status_t status = zx_vmo_create(0, 0, &vmo);
EXPECT_EQ(ZX_OK, status, "vm_object_create");
// create clones with different sizes, make sure the created size is a multiple of a page size
for (uint64_t s = 0; s < PAGE_SIZE * 4; s++) {
zx_handle_t clone_vmo;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE,
0, s, &clone_vmo), "vm_clone");
// should be the size rounded up to the nearest page boundary
uint64_t size = 0x99999999;
zx_status_t status = zx_vmo_get_size(clone_vmo, &size);
EXPECT_EQ(ZX_OK, status, "vm_object_get_size");
EXPECT_EQ(fbl::round_up(s, static_cast<size_t>(PAGE_SIZE)), size, "vm_object_get_size");
// close the handle
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo), "handle_close");
}
// close the handle
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
END_TEST;
}
// test set 1: create a few clones, close them
bool vmo_clone_test_1() {
BEGIN_TEST;
zx_handle_t vmo;
zx_handle_t clone_vmo[3];
// create a vmo
const size_t size = PAGE_SIZE * 4;
EXPECT_EQ(ZX_OK, zx_vmo_create(size, 0, &vmo), "vm_object_create");
EXPECT_EQ(ZX_OK, zx_object_set_property(vmo, ZX_PROP_NAME, "test1", 5), "zx_object_set_property");
// clone it
clone_vmo[0] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo[0]), "vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo[0], "vm_clone_handle");
char name[ZX_MAX_NAME_LEN];
EXPECT_EQ(ZX_OK, zx_object_get_property(clone_vmo[0], ZX_PROP_NAME, name, ZX_MAX_NAME_LEN), "zx_object_get_property");
EXPECT_TRUE(!strcmp(name, "test1"), "get_name");
// clone it a second time
clone_vmo[1] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo[1]), "vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo[1], "vm_clone_handle");
// clone the clone
clone_vmo[2] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(clone_vmo[1], ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo[2]), "vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo[2], "vm_clone_handle");
// close the original handle
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
// close the clone handles
for (auto h: clone_vmo)
EXPECT_EQ(ZX_OK, zx_handle_close(h), "handle_close");
END_TEST;
}
// test set 2: create a clone, verify that it COWs via the read/write interface
bool vmo_clone_test_2() {
BEGIN_TEST;
zx_handle_t vmo;
zx_handle_t clone_vmo[1];
// create a vmo
const size_t size = PAGE_SIZE * 4;
EXPECT_EQ(ZX_OK, zx_vmo_create(size, 0, &vmo), "vm_object_create");
// fill the original with stuff
for (size_t off = 0; off < size; off += sizeof(off)) {
zx_vmo_write(vmo, &off, off, sizeof(off));
}
// clone it
clone_vmo[0] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo[0]), "vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo[0], "vm_clone_handle");
// verify that the clone reads back as the same
for (size_t off = 0; off < size; off += sizeof(off)) {
size_t val;
zx_vmo_read(clone_vmo[0], &val, off, sizeof(val));
if (val != off) {
EXPECT_EQ(val, off, "vm_clone read back");
break;
}
}
// write to part of the clone
size_t val = 99;
zx_vmo_write(clone_vmo[0], &val, 0, sizeof(val));
// verify the clone was written to
EXPECT_EQ(ZX_OK, zx_vmo_read(clone_vmo[0], &val, 0, sizeof(val)), "writing to clone");
// verify it was written to
EXPECT_EQ(99, val, "reading back from clone");
// verify that the rest of the page it was written two was cloned
for (size_t off = sizeof(val); off < PAGE_SIZE; off += sizeof(off)) {
zx_vmo_read(clone_vmo[0], &val, off, sizeof(val));
if (val != off) {
EXPECT_EQ(val, off, "vm_clone read back");
break;
}
}
// verify that it didn't trash the original
for (size_t off = 0; off < size; off += sizeof(off)) {
zx_vmo_read(vmo, &val, off, sizeof(val));
if (val != off) {
EXPECT_EQ(val, off, "vm_clone read back of original");
break;
}
}
// write to the original in the part that is still visible to the clone
val = 99;
uint64_t offset = PAGE_SIZE * 2;
EXPECT_EQ(ZX_OK, zx_vmo_write(vmo, &val, offset, sizeof(val)), "writing to original");
EXPECT_EQ(ZX_OK, zx_vmo_read(clone_vmo[0], &val, offset, sizeof(val)), "reading back original from clone");
EXPECT_EQ(99, val, "checking value");
// close the clone handles
for (auto h: clone_vmo)
EXPECT_EQ(ZX_OK, zx_handle_close(h), "handle_close");
// close the original handle
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
END_TEST;
}
// test set 3: test COW via a mapping
bool vmo_clone_test_3() {
BEGIN_TEST;
zx_handle_t vmo;
zx_handle_t clone_vmo[1];
uintptr_t ptr;
uintptr_t clone_ptr;
volatile uint32_t *p;
volatile uint32_t *cp;
// create a vmo
const size_t size = PAGE_SIZE * 4;
EXPECT_EQ(ZX_OK, zx_vmo_create(size, 0, &vmo), "vm_object_create");
// map it
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr),
"map");
EXPECT_NE(ptr, 0, "map address");
p = (volatile uint32_t *)ptr;
// clone it
clone_vmo[0] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE | ZX_VMO_CHILD_RESIZABLE,
0, size, &clone_vmo[0]),"vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo[0], "vm_clone_handle");
// Attempt a non-resizable map fails.
EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
zx_vmar_map(zx_vmar_root_self(),
ZX_VM_PERM_READ|ZX_VM_PERM_WRITE|ZX_VM_REQUIRE_NON_RESIZABLE,
0, clone_vmo[0], 0, size, &clone_ptr), "map");
// Regular resizable mapping works.
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, clone_vmo[0], 0, size, &clone_ptr),
"map");
EXPECT_NE(clone_ptr, 0, "map address");
cp = (volatile uint32_t *)clone_ptr;
// read zeros from both
for (size_t off = 0; off < size / sizeof(off); off++) {
size_t val = p[off];
if (val != 0) {
EXPECT_EQ(0, val, "reading zeros from original");
break;
}
}
for (size_t off = 0; off < size / sizeof(off); off++) {
size_t val = cp[off];
if (val != 0) {
EXPECT_EQ(0, val, "reading zeros from original");
break;
}
}
// write to both sides and make sure it does a COW
p[0] = 99;
EXPECT_EQ(99, p[0], "wrote to original");
EXPECT_EQ(99, cp[0], "read back from clone");
cp[0] = 100;
EXPECT_EQ(100, cp[0], "read back from clone");
EXPECT_EQ(99, p[0], "read back from original");
// close the original handle
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
// close the clone handle
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo[0]), "handle_close");
// unmap
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), ptr, size), "unmap");
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), clone_ptr, size), "unmap");
END_TEST;
}
// verify that the parent is visible through decommitted pages
bool vmo_clone_decommit_test() {
BEGIN_TEST;
zx_handle_t vmo;
zx_handle_t clone_vmo;
uintptr_t ptr;
uintptr_t clone_ptr;
volatile uint32_t *p;
volatile uint32_t *cp;
// create a vmo
const size_t size = PAGE_SIZE * 4;
EXPECT_EQ(ZX_OK, zx_vmo_create(size, 0, &vmo), "vm_object_create");
// map it
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr),
"map");
EXPECT_NE(ptr, 0, "map address");
p = (volatile uint32_t *)ptr;
// clone it and map that
clone_vmo = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo), "vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo, "vm_clone_handle");
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, clone_vmo, 0, size, &clone_ptr),
"map");
EXPECT_NE(clone_ptr, 0, "map address");
cp = (volatile uint32_t *)clone_ptr;
// write to parent and make sure clone sees it
p[0] = 99;
EXPECT_EQ(99, p[0], "wrote to original");
EXPECT_EQ(99, cp[0], "read back from clone");
// write to clone to get a different state
cp[0] = 100;
EXPECT_EQ(100, cp[0], "read back from clone");
EXPECT_EQ(99, p[0], "read back from original");
EXPECT_EQ(ZX_OK, zx_vmo_op_range(clone_vmo, ZX_VMO_OP_DECOMMIT, 0, PAGE_SIZE, NULL, 0));
// make sure that clone reverted to original, and that parent is unaffected
// by the decommit
EXPECT_EQ(99, cp[0], "read back from clone");
EXPECT_EQ(99, p[0], "read back from original");
// make sure the decommitted page still has COW semantics
cp[0] = 100;
EXPECT_EQ(100, cp[0], "read back from clone");
EXPECT_EQ(99, p[0], "read back from original");
// close the original handle
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
// close the clone handle
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo), "handle_close");
// unmap
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), ptr, size), "unmap");
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), clone_ptr, size), "unmap");
END_TEST;
}
// verify the affect of commit on a clone
bool vmo_clone_commit_test() {
BEGIN_TEST;
zx_handle_t vmo;
zx_handle_t clone_vmo;
uintptr_t ptr;
uintptr_t clone_ptr;
volatile uint32_t *p;
volatile uint32_t *cp;
// create a vmo
const size_t size = PAGE_SIZE * 4;
EXPECT_EQ(ZX_OK, zx_vmo_create(size, 0, &vmo), "vm_object_create");
// map it
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr),
"map");
EXPECT_NE(ptr, 0, "map address");
p = (volatile uint32_t *)ptr;
// clone it and map that
clone_vmo = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo), "vm_clone");
EXPECT_NE(ZX_HANDLE_INVALID, clone_vmo, "vm_clone_handle");
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, clone_vmo, 0, size, &clone_ptr),
"map");
EXPECT_NE(clone_ptr, 0, "map address");
cp = (volatile uint32_t *)clone_ptr;
// write to parent and make sure clone sees it
memset((void*)p, 0x99, PAGE_SIZE);
EXPECT_EQ(0x99999999, p[0], "wrote to original");
EXPECT_EQ(0x99999999, cp[0], "read back from clone");
EXPECT_EQ(ZX_OK, zx_vmo_op_range(clone_vmo, ZX_VMO_OP_COMMIT, 0, PAGE_SIZE, NULL, 0));
// make sure that clone has the same contents as the parent
for (size_t i = 0; i < PAGE_SIZE / sizeof(*p); ++i) {
EXPECT_EQ(0x99999999, cp[i], "read new page");
}
EXPECT_EQ(0x99999999, p[0], "read back from original");
// write to clone and make sure parent doesn't see it
cp[0] = 0;
EXPECT_EQ(0, cp[0], "wrote to clone");
EXPECT_EQ(0x99999999, p[0], "read back from original");
EXPECT_EQ(ZX_OK, zx_vmo_op_range(clone_vmo, ZX_VMO_OP_DECOMMIT, 0, PAGE_SIZE, NULL, 0));
EXPECT_EQ(0x99999999, cp[0], "clone should match orig again");
EXPECT_EQ(0x99999999, p[0], "read back from original");
// close the original handle
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
// close the clone handle
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo), "handle_close");
// unmap
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), ptr, size), "unmap");
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), clone_ptr, size), "unmap");
END_TEST;
}
// test set 4: deal with clones with nonzero offsets and offsets that extend beyond the original
bool vmo_clone_test_4() {
BEGIN_TEST;
zx_handle_t vmo;
zx_handle_t clone_vmo[1];
uintptr_t ptr;
uintptr_t clone_ptr;
volatile size_t *p;
volatile size_t *cp;
// create a vmo
const size_t size = PAGE_SIZE * 4;
EXPECT_EQ(ZX_OK, zx_vmo_create(size, ZX_VMO_RESIZABLE, &vmo), "vm_object_create");
// map it
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr),
"map");
EXPECT_NE(ptr, 0, "map address");
p = (volatile size_t *)ptr;
// fill it with stuff
for (size_t off = 0; off < size / sizeof(off); off++)
p[off] = off;
// make sure that non page aligned clones do not work
clone_vmo[0] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_ERR_INVALID_ARGS, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 1, size, &clone_vmo[0]), "vm_clone");
// create a clone that extends beyond the parent by one page
clone_vmo[0] = ZX_HANDLE_INVALID;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(vmo, ZX_VMO_CHILD_COPY_ON_WRITE | ZX_VMO_CHILD_RESIZABLE,
PAGE_SIZE, size, &clone_vmo[0]), "vm_clone");
// map the clone
EXPECT_EQ(ZX_OK,
zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ|ZX_VM_PERM_WRITE, 0, clone_vmo[0], 0, size, &clone_ptr),
"map");
EXPECT_NE(clone_ptr, 0, "map address");
cp = (volatile size_t *)clone_ptr;
// verify that it seems to be mapping the original at an offset
for (size_t off = 0; off < (size - PAGE_SIZE) / sizeof(off); off++) {
if (cp[off] != off + PAGE_SIZE / sizeof(off)) {
EXPECT_EQ(cp[off], off + PAGE_SIZE / sizeof(off), "reading from clone");
break;
}
}
// verify that the last page we have mapped is beyond the original and should return zeros
for (size_t off = (size - PAGE_SIZE) / sizeof(off); off < size / sizeof(off); off++) {
if (cp[off] != 0) {
EXPECT_EQ(cp[off], 0, "reading from clone");
break;
}
}
// resize the original
EXPECT_EQ(ZX_OK, zx_vmo_set_size(vmo, size + PAGE_SIZE), "extend the vmo");
// verify that the last page we have mapped still returns zeros
for (size_t off = (size - PAGE_SIZE) / sizeof(off); off < size / sizeof(off); off++) {
if (cp[off] != 0) {
EXPECT_EQ(cp[off], 0, "reading from clone");
break;
}
}
// write to the new part of the original
size_t val = 99;
EXPECT_EQ(ZX_OK, zx_vmo_write(vmo, &val, size, sizeof(val)),
"writing to original after extending");
// verify that it is not reflected in the clone
EXPECT_EQ(0, cp[(size - PAGE_SIZE) / sizeof(*cp)],
"didn't modified newly exposed part of cow clone");
// write to a page in the original vmo
EXPECT_EQ(ZX_OK, zx_vmo_write(vmo, &val, size - PAGE_SIZE, sizeof(val)),
"writing to original after extending");
// verify that it is reflected in the clone
EXPECT_EQ(99, cp[(size - 2 * PAGE_SIZE) / sizeof(*cp)],
"modified newly exposed part of cow clone");
// shrink and enlarge the clone
EXPECT_EQ(ZX_OK, zx_vmo_set_size(clone_vmo[0], size - 2 * PAGE_SIZE), "shrunk the clone");
EXPECT_EQ(ZX_OK, zx_vmo_set_size(clone_vmo[0], size), "extend the clone");
// verify that new pages are zero-pages instead of uncovering previously visible parent pages
EXPECT_EQ(0, cp[(size - 2 * PAGE_SIZE) / sizeof(*cp)],
"didn't modified newly exposed part of cow clone");
// resize the original again, completely extending it beyond he clone
EXPECT_EQ(ZX_OK, zx_vmo_set_size(vmo, size + PAGE_SIZE * 2), "extend the vmo");
// resize the original to zero
EXPECT_EQ(ZX_OK, zx_vmo_set_size(vmo, 0), "truncate the vmo");
// verify that the clone now reads completely zeros, since it never COWed
for (size_t off = 0; off < size / sizeof(off); off++) {
if (cp[off] != 0) {
EXPECT_EQ(cp[off], 0, "reading zeros from clone");
break;
}
}
// close and unmap
EXPECT_EQ(ZX_OK, zx_handle_close(vmo), "handle_close");
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), ptr, size), "unmap");
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo[0]), "handle_close");
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), clone_ptr, size), "unmap");
END_TEST;
}
// Returns zero on failure.
static zx_rights_t get_handle_rights(zx_handle_t h) {
zx_info_handle_basic_t info;
zx_status_t s = zx_object_get_info(h, ZX_INFO_HANDLE_BASIC, &info,
sizeof(info), nullptr, nullptr);
if (s != ZX_OK) {
EXPECT_EQ(s, ZX_OK); // Poison the test
return 0;
}
return info.rights;
}
bool vmo_clone_rights_test() {
BEGIN_TEST;
static const char kOldVmoName[] = "original";
static const char kNewVmoName[] = "clone";
static const zx_rights_t kOldVmoRights =
ZX_RIGHT_READ | ZX_RIGHT_DUPLICATE;
static const zx_rights_t kNewVmoRights =
kOldVmoRights | ZX_RIGHT_WRITE |
ZX_RIGHT_GET_PROPERTY | ZX_RIGHT_SET_PROPERTY;
zx_handle_t vmo;
ASSERT_EQ(zx_vmo_create(PAGE_SIZE, 0, &vmo),
ZX_OK);
ASSERT_EQ(zx_object_set_property(vmo, ZX_PROP_NAME,
kOldVmoName, sizeof(kOldVmoName)),
ZX_OK);
ASSERT_EQ(get_handle_rights(vmo) & kOldVmoRights, kOldVmoRights);
zx_handle_t reduced_rights_vmo;
ASSERT_EQ(zx_handle_duplicate(vmo, kOldVmoRights, &reduced_rights_vmo),
ZX_OK);
EXPECT_EQ(get_handle_rights(reduced_rights_vmo), kOldVmoRights);
zx_handle_t clone;
ASSERT_EQ(zx_vmo_create_child(reduced_rights_vmo, ZX_VMO_CHILD_COPY_ON_WRITE,
0, PAGE_SIZE, &clone),
ZX_OK);
EXPECT_EQ(zx_handle_close(reduced_rights_vmo), ZX_OK);
ASSERT_EQ(zx_object_set_property(clone, ZX_PROP_NAME,
kNewVmoName, sizeof(kNewVmoName)),
ZX_OK);
char oldname[ZX_MAX_NAME_LEN] = "bad";
EXPECT_EQ(zx_object_get_property(vmo, ZX_PROP_NAME,
oldname, sizeof(oldname)),
ZX_OK);
EXPECT_STR_EQ(oldname, kOldVmoName, "original VMO name");
char newname[ZX_MAX_NAME_LEN] = "bad";
EXPECT_EQ(zx_object_get_property(clone, ZX_PROP_NAME,
newname, sizeof(newname)),
ZX_OK);
EXPECT_STR_EQ(newname, kNewVmoName, "clone VMO name");
EXPECT_EQ(zx_handle_close(vmo), ZX_OK);
EXPECT_EQ(get_handle_rights(clone), kNewVmoRights);
EXPECT_EQ(zx_handle_close(clone), ZX_OK);
END_TEST;
}
// Resizing a cloned VMO causes a fault.
bool vmo_clone_resize_clone_hazard() {
BEGIN_TEST;
const size_t size = PAGE_SIZE * 2;
zx_handle_t vmo;
ASSERT_EQ(zx_vmo_create(size, 0, &vmo), ZX_OK);
zx_handle_t clone_vmo;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(
vmo, ZX_VMO_CHILD_COPY_ON_WRITE | ZX_VMO_CHILD_RESIZABLE, 0, size, &clone_vmo), "vm_clone");
uintptr_t ptr_rw;
EXPECT_EQ(ZX_OK, zx_vmar_map(
zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
clone_vmo, 0, size, &ptr_rw), "map");
auto int_arr = reinterpret_cast<int*>(ptr_rw);
EXPECT_EQ(int_arr[1], 0);
EXPECT_EQ(ZX_OK, zx_vmo_set_size(clone_vmo, 0u));
EXPECT_EQ(false, probe_for_read(&int_arr[1]), "read probe");
EXPECT_EQ(false, probe_for_write(&int_arr[1]), "write probe");
EXPECT_EQ(ZX_OK, zx_handle_close(vmo));
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo));
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), ptr_rw, size), "unmap");
END_TEST;
}
// Resizing the parent VMO and accessing via a mapped VMO is ok.
bool vmo_clone_resize_parent_ok() {
BEGIN_TEST;
const size_t size = PAGE_SIZE * 2;
zx_handle_t vmo;
ASSERT_EQ(zx_vmo_create(size, ZX_VMO_RESIZABLE, &vmo), ZX_OK);
zx_handle_t clone_vmo;
EXPECT_EQ(ZX_OK, zx_vmo_create_child(
vmo, ZX_VMO_CHILD_COPY_ON_WRITE, 0, size, &clone_vmo), "vm_clone");
uintptr_t ptr_rw;
EXPECT_EQ(ZX_OK, zx_vmar_map(
zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
clone_vmo, 0, size, &ptr_rw), "map");
auto int_arr = reinterpret_cast<int*>(ptr_rw);
EXPECT_EQ(int_arr[1], 0);
EXPECT_EQ(ZX_OK, zx_vmo_set_size(vmo, 0u));
EXPECT_EQ(true, probe_for_read(&int_arr[1]), "read probe");
EXPECT_EQ(true, probe_for_write(&int_arr[1]), "write probe");
EXPECT_EQ(ZX_OK, zx_handle_close(vmo));
EXPECT_EQ(ZX_OK, zx_handle_close(clone_vmo));
EXPECT_EQ(ZX_OK, zx_vmar_unmap(zx_vmar_root_self(), ptr_rw, size), "unmap");
END_TEST;
}
// Check that non-resizable VMOs cannot get resized.
bool vmo_clone_no_resize_test() {
BEGIN_TEST;
const size_t len = PAGE_SIZE * 4;
zx_handle_t parent = ZX_HANDLE_INVALID;
zx_handle_t vmo = ZX_HANDLE_INVALID;
zx_vmo_create(len, 0, &parent);
zx_vmo_create_child(parent,
ZX_VMO_CHILD_COPY_ON_WRITE | ZX_VMO_CHILD_NON_RESIZEABLE,
0, len, &vmo);
EXPECT_NE(vmo, ZX_HANDLE_INVALID);
zx_status_t status;
status = zx_vmo_set_size(vmo, len + PAGE_SIZE);
EXPECT_EQ(ZX_ERR_UNAVAILABLE, status, "vm_object_set_size");
status = zx_vmo_set_size(vmo, len - PAGE_SIZE);
EXPECT_EQ(ZX_ERR_UNAVAILABLE, status, "vm_object_set_size");
size_t size;
status = zx_vmo_get_size(vmo, &size);
EXPECT_EQ(ZX_OK, status, "vm_object_get_size");
EXPECT_EQ(len, size, "vm_object_get_size");
uintptr_t ptr;
status = zx_vmar_map(
zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_REQUIRE_NON_RESIZABLE,
0, vmo, 0, len,
&ptr);
ASSERT_EQ(ZX_OK, status, "vm_map");
ASSERT_NE(ptr, 0, "vm_map");
status = zx_vmar_unmap(zx_vmar_root_self(), ptr, len);
EXPECT_EQ(ZX_OK, status, "unmap");
status = zx_handle_close(vmo);
EXPECT_EQ(ZX_OK, status, "handle_close");
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(vmo_clone_tests)
RUN_TEST(vmo_clone_size_align_test);
RUN_TEST(vmo_clone_test_1);
RUN_TEST(vmo_clone_test_2);
RUN_TEST(vmo_clone_test_3);
RUN_TEST(vmo_clone_test_4);
RUN_TEST(vmo_clone_decommit_test);
RUN_TEST(vmo_clone_commit_test);
RUN_TEST(vmo_clone_rights_test);
RUN_TEST(vmo_clone_resize_clone_hazard);
RUN_TEST(vmo_clone_resize_parent_ok);
RUN_TEST(vmo_clone_no_resize_test);
END_TEST_CASE(vmo_clone_tests)