blob: cc0a0cb9e1ca9f8e3e3c4eea36cf90325a044ec8 [file]
// 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 <lib/unittest/unittest.h>
#include <lib/unittest/user_memory.h>
#include <lib/user_copy/internal.h>
#include <lib/user_copy/user_ptr.h>
#include <zircon/syscalls/port.h>
#include <vm/fault.h>
#include <vm/vm_aspace.h>
#include <vm/vm_object_paged.h>
namespace {
using testing::UserMemory;
constexpr uint32_t kTestValue = 0xDEADBEEF;
bool test_copy_out(bool pre_map) {
BEGIN_TEST;
auto user = UserMemory::Create(PAGE_SIZE);
if (pre_map) {
ASSERT_EQ(user->CommitAndMap(PAGE_SIZE), ZX_OK, "");
}
ASSERT_EQ(user->user_out<uint32_t>().copy_to_user(kTestValue), ZX_OK, "");
uint32_t temp;
ASSERT_EQ(user->VmoRead(&temp, 0, sizeof(temp)), ZX_OK, "");
EXPECT_EQ(temp, kTestValue, "");
END_TEST;
}
bool test_copy_in(bool pre_map) {
BEGIN_TEST;
auto user = UserMemory::Create(PAGE_SIZE);
if (pre_map) {
ASSERT_EQ(user->CommitAndMap(PAGE_SIZE), ZX_OK, "");
}
ASSERT_EQ(user->VmoWrite(&kTestValue, 0, sizeof(kTestValue)), ZX_OK, "");
uint32_t temp;
ASSERT_EQ(user->user_in<uint32_t>().copy_from_user(&temp), ZX_OK, "");
EXPECT_EQ(temp, kTestValue, "");
END_TEST;
}
bool pre_map_copy_out() { return test_copy_out(true); }
bool fault_copy_out() { return test_copy_out(false); }
bool pre_map_copy_in() { return test_copy_in(true); }
bool fault_copy_in() { return test_copy_in(false); }
bool capture_faults_copy_out_success() {
BEGIN_TEST;
auto user = UserMemory::Create(PAGE_SIZE);
ASSERT_EQ(user->CommitAndMap(PAGE_SIZE), ZX_OK, "");
vaddr_t pf_va;
uint pf_flags;
ASSERT_EQ(user->user_out<uint32_t>().copy_to_user_capture_faults(kTestValue, &pf_va, &pf_flags),
ZX_OK, "");
uint32_t temp;
ASSERT_EQ(user->VmoRead(&temp, 0, sizeof(temp)), ZX_OK, "");
EXPECT_EQ(temp, kTestValue, "");
END_TEST;
}
bool capture_faults_copy_in_success() {
BEGIN_TEST;
auto user = UserMemory::Create(PAGE_SIZE);
ASSERT_EQ(user->CommitAndMap(PAGE_SIZE), ZX_OK, "");
ASSERT_EQ(user->VmoWrite(&kTestValue, 0, sizeof(kTestValue)), ZX_OK, "");
vaddr_t pf_va;
uint pf_flags;
uint32_t temp;
ASSERT_EQ(user->user_in<uint32_t>().copy_from_user_capture_faults(&temp, &pf_va, &pf_flags),
ZX_OK, "");
EXPECT_EQ(temp, kTestValue, "");
END_TEST;
}
bool capture_faults_test_capture() {
BEGIN_TEST;
auto user = UserMemory::Create(PAGE_SIZE);
vaddr_t pf_va;
uint pf_flags;
uint32_t temp;
ASSERT_NE(user->user_in<uint32_t>().copy_from_user_capture_faults(&temp, &pf_va, &pf_flags),
ZX_OK, "");
EXPECT_EQ(pf_va, user->base(), "");
EXPECT_EQ(pf_flags, VMM_PF_FLAG_NOT_PRESENT, "");
ASSERT_NE(user->user_out<uint32_t>().copy_to_user_capture_faults(kTestValue, &pf_va, &pf_flags),
ZX_OK, "");
EXPECT_EQ(pf_va, user->base(), "");
EXPECT_EQ(pf_flags, VMM_PF_FLAG_NOT_PRESENT | VMM_PF_FLAG_WRITE, "");
END_TEST;
}
// Verify is_copy_allowed<T>::value is true when T contains no implicit padding.
struct SomeTypeWithNoPadding {
uint64_t field1;
};
static_assert(internal::is_copy_allowed<SomeTypeWithNoPadding>::value);
static_assert(internal::is_copy_allowed<int>::value);
static_assert(internal::is_copy_allowed<zx_port_packet_t>::value);
// Verify is_copy_allowed<void>::value is false.
static_assert(!internal::is_copy_allowed<void>::value);
// Verify is_copy_allowed<T>::value is false when T contains implicit padding.
struct SomeTypeWithPadding {
uint64_t field1;
uint32_t field2;
};
static_assert(!internal::is_copy_allowed<SomeTypeWithPadding>::value);
// Verify is_copy_allowed<T>::value is false when T does not have a standard-layout.
struct SomeTypeWithNonStandardLayout : SomeTypeWithNoPadding {
uint32_t another_field;
};
static_assert(!internal::is_copy_allowed<SomeTypeWithNonStandardLayout>::value);
// Verify is_copy_allowed<T>::value is false when T is not trival.
struct SomeTypeNonTrivial {
SomeTypeNonTrivial(const SomeTypeNonTrivial& other) { another_field = other.another_field; }
uint32_t another_field;
};
static_assert(!internal::is_copy_allowed<SomeTypeNonTrivial>::value);
} // namespace
#define USER_COPY_UNITTEST(fname) UNITTEST(#fname, fname)
UNITTEST_START_TESTCASE(user_copy_tests)
USER_COPY_UNITTEST(pre_map_copy_out)
USER_COPY_UNITTEST(fault_copy_out)
USER_COPY_UNITTEST(pre_map_copy_in)
USER_COPY_UNITTEST(fault_copy_in)
USER_COPY_UNITTEST(capture_faults_copy_out_success)
USER_COPY_UNITTEST(capture_faults_copy_in_success)
USER_COPY_UNITTEST(capture_faults_test_capture)
UNITTEST_END_TESTCASE(user_copy_tests, "user_copy_tests", "User Copy test")