blob: 93b62792c4805018c3671514f1f52e508e059f5e [file] [log] [blame]
/*
* Copyright (c) 2015, Google Inc. All rights reserved
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <assert.h>
#include <err.h>
#include <kernel/usercopy.h>
#include <lib/unittest/unittest.h>
#include <lk/init.h>
#include <stdio.h>
#include <string.h>
#include <trusty/string.h>
#ifdef ARCH_ARM64
#include <arch/safecopy.h>
#endif
#define PORT_NAME "com.android.kernel.usercopy-unittest"
#define TEST_BUF_SIZE (16)
#define TEST_BUF1_SIZE (TEST_BUF_SIZE / 2)
#define TEST_BUF2_SIZE (TEST_BUF_SIZE - TEST_BUF1_SIZE)
#define TEST_BUF_COPY_START (1)
#define TEST_BUF_COPY_SIZE (TEST_BUF_SIZE - TEST_BUF_COPY_START - 1)
#define TEST_BUF1_COPY_SIZE (TEST_BUF1_SIZE - TEST_BUF_COPY_START)
#define TEST_BUF2_COPY_SIZE (TEST_BUF_COPY_SIZE - TEST_BUF1_COPY_SIZE)
#define TEST_BUF_COPY_LAST (TEST_BUF_SIZE - 1 - 1)
#define TEST_BUF2_COPY_LAST (TEST_BUF_COPY_LAST - TEST_BUF1_SIZE)
#define SRC_DATA (0x22)
#define DEST_DATA (0x11)
#define FLAGS_NO_PAGE (ARCH_MMU_FLAG_INVALID)
#define FLAGS_NO_USER (0u)
#define FLAGS_RO_USER (ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_RO)
#define FLAGS_RW_USER (ARCH_MMU_FLAG_PERM_USER)
#define STACK_ADDR_IDX (0)
#define HEAP_ADDR_IDX (1)
#define GLOBAL_ADDR_IDX (2)
#define START_PAGE_ADDR ((void*)(PAGE_SIZE * 0x10))
#define TEST_BUF_ADDR \
((user_addr_t)((uintptr_t)(START_PAGE_ADDR + PAGE_SIZE - TEST_BUF1_SIZE)))
static inline user_addr_t get_addr_param(void) {
const void* const* param_arr = GetParam();
const user_addr_t* addr = param_arr[0];
return *addr;
}
static inline uint32_t get_start_flags_param(void) {
const void* const* param_arr = GetParam();
const uint32_t* start_flags = param_arr[1];
return *start_flags;
}
static inline uint32_t get_end_flags_param(void) {
const void* const* param_arr = GetParam();
const uint32_t* end_flags = param_arr[2];
return *end_flags;
}
static int checkbuf(const char* buf, char c, size_t size) {
int error_count = 0;
for (size_t i = 0; i < size; i++) {
if (buf[i] != c) {
error_count++;
}
}
return error_count;
}
static void usercopy_test_init_buf(char* kbuf1,
char* kbuf2,
uint8_t val,
int null_offset) {
if (kbuf1) {
memset(kbuf1, val, TEST_BUF1_SIZE);
if (null_offset >= 0 && null_offset < TEST_BUF1_SIZE) {
kbuf1[null_offset] = '\0';
}
}
if (kbuf2) {
memset(kbuf2, val, TEST_BUF2_SIZE);
if (null_offset >= TEST_BUF1_SIZE && null_offset < TEST_BUF_SIZE) {
kbuf2[null_offset - TEST_BUF1_SIZE] = '\0';
}
}
}
typedef struct {
struct vmm_aspace* aspace;
} usercopytest_t;
TEST_F_SETUP(usercopytest) {
int ret;
void* addr = START_PAGE_ADDR;
uint32_t start_flags = get_start_flags_param();
uint32_t end_flags = get_end_flags_param();
_state->aspace = NULL;
ret = vmm_create_aspace(&_state->aspace, "usercopy_test", 0);
ASSERT_EQ(NO_ERROR, ret);
if (start_flags != FLAGS_NO_PAGE) {
ret = vmm_alloc(_state->aspace, "start-page", PAGE_SIZE, &addr, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
start_flags | ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret);
ASSERT_EQ(START_PAGE_ADDR, addr);
}
addr += PAGE_SIZE;
if (end_flags != FLAGS_NO_PAGE) {
ret = vmm_alloc(_state->aspace, "end-page", PAGE_SIZE, &addr, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD,
end_flags | ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret);
ASSERT_EQ(START_PAGE_ADDR + PAGE_SIZE, addr);
}
vmm_set_active_aspace(_state->aspace);
test_abort:
return;
}
TEST_F_TEARDOWN(usercopytest) {
vmm_set_active_aspace(NULL);
if (_state->aspace) {
vmm_free_aspace(_state->aspace);
}
}
TEST_P(usercopytest, copy_to_user) {
user_addr_t addr = get_addr_param();
uint32_t arch_mmu_flags_start = get_start_flags_param();
uint32_t arch_mmu_flags_end = get_end_flags_param();
int ret;
char src_buf[TEST_BUF_SIZE];
char* dest_kbuf1;
char* dest_kbuf2;
char expect1;
char expect2;
dest_kbuf1 = paddr_to_kvaddr(vaddr_to_paddr((void*)(uintptr_t)addr));
dest_kbuf2 = paddr_to_kvaddr(
vaddr_to_paddr((void*)(uintptr_t)addr + TEST_BUF1_SIZE));
/* dest buffs should be NULL iff their flags are FLAGS_NO_PAGE */
EXPECT_EQ((dest_kbuf1 == NULL), (arch_mmu_flags_start == FLAGS_NO_PAGE));
EXPECT_EQ((dest_kbuf2 == NULL), (arch_mmu_flags_end == FLAGS_NO_PAGE));
usercopy_test_init_buf(dest_kbuf1, dest_kbuf2, DEST_DATA, -1);
memset(src_buf, SRC_DATA, sizeof(src_buf));
/* Zero-length copy should always succeed */
ret = copy_to_user(addr + TEST_BUF_COPY_START, NULL, 0);
EXPECT_EQ(0, ret);
/* Dest buffer should be untouched after zero-length copy */
if (dest_kbuf1) {
EXPECT_EQ(0, checkbuf(dest_kbuf1, DEST_DATA, TEST_BUF1_SIZE));
}
if (dest_kbuf2) {
EXPECT_EQ(0, checkbuf(dest_kbuf2, DEST_DATA, TEST_BUF2_SIZE));
}
/* Perform non-zero length copy */
ret = copy_to_user(addr + TEST_BUF_COPY_START,
src_buf + TEST_BUF_COPY_START, TEST_BUF_COPY_SIZE);
/*
* If both pages are writeable copy_to_user should succeed otherwise it
* should return ERR_FAULT.
*/
if (arch_mmu_flags_start == ARCH_MMU_FLAG_PERM_USER &&
arch_mmu_flags_end == ARCH_MMU_FLAG_PERM_USER) {
/*
* If both pages are writeable from user-space copy_to_user should
* return success and every byte should be copied to dest_buf.
*/
EXPECT_EQ(0, ret);
expect1 = SRC_DATA;
expect2 = SRC_DATA;
} else {
/*
* If one of the pages is not writeable from user-space copy_to_user
* should return ERR_FAULT. If only the first page is writeable everying
* should be copied in the first page or nothing should be copied in the
* first page. If the first page is not writeable, nothing should be
* copied to either page. If the second page is not writeable, no data
* should be copied to it, even if the first page was written to.
*/
EXPECT_EQ(ERR_FAULT, ret);
if (arch_mmu_flags_start == ARCH_MMU_FLAG_PERM_USER &&
dest_kbuf1[TEST_BUF_COPY_START] == SRC_DATA) {
expect1 = SRC_DATA;
} else {
expect1 = DEST_DATA;
}
expect2 = DEST_DATA;
}
/* copy_to_user should not modify src_buf at all */
EXPECT_EQ(0, checkbuf(src_buf, SRC_DATA, TEST_BUF_SIZE));
if (dest_kbuf1) {
/* Dest byte before copied region should be untouched */
EXPECT_EQ(DEST_DATA, dest_kbuf1[0]);
/* Check that copied region match expected value we selected above */
EXPECT_EQ(0, checkbuf(dest_kbuf1 + TEST_BUF_COPY_START, expect1,
TEST_BUF1_COPY_SIZE));
}
if (dest_kbuf2) {
/* Check that copied region match expected value we selected above */
EXPECT_EQ(0, checkbuf(dest_kbuf2, expect2, TEST_BUF2_COPY_SIZE));
/* Dest byte after copied region should be untouched */
EXPECT_EQ(DEST_DATA, dest_kbuf2[TEST_BUF2_SIZE - 1]);
}
}
TEST_P(usercopytest, copy_from_user) {
user_addr_t addr = get_addr_param();
uint32_t arch_mmu_flags_start = get_start_flags_param();
uint32_t arch_mmu_flags_end = get_end_flags_param();
int ret;
char dest_buf[TEST_BUF_SIZE];
char* src_kbuf1;
char* src_kbuf2;
char expect1;
char expect2;
memset(dest_buf, DEST_DATA, sizeof(dest_buf));
src_kbuf1 = paddr_to_kvaddr(vaddr_to_paddr((void*)(uintptr_t)addr));
src_kbuf2 = paddr_to_kvaddr(
vaddr_to_paddr((void*)(uintptr_t)addr + TEST_BUF1_SIZE));
/* src buffs should be NULL iff their flags are FLAGS_NO_PAGE */
EXPECT_EQ((src_kbuf1 == NULL), (arch_mmu_flags_start == FLAGS_NO_PAGE));
EXPECT_EQ((src_kbuf2 == NULL), (arch_mmu_flags_end == FLAGS_NO_PAGE));
usercopy_test_init_buf(src_kbuf1, src_kbuf2, SRC_DATA, -1);
/* Zero-length copy should always succeed */
ret = copy_from_user(NULL, addr + TEST_BUF_COPY_START, 0);
EXPECT_EQ(0, ret);
/* Dest buffer should be untouched after zero-length copy */
EXPECT_EQ(0, checkbuf(dest_buf, DEST_DATA, TEST_BUF_SIZE));
/* Perform non-zero length copy */
ret = copy_from_user(dest_buf + TEST_BUF_COPY_START,
addr + TEST_BUF_COPY_START, TEST_BUF_COPY_SIZE);
if (arch_mmu_flags_start & arch_mmu_flags_end & ARCH_MMU_FLAG_PERM_USER) {
/*
* If both pages are readable from user-space copy_from_user should
* return success and every byte should be copied to dest_buf.
*/
EXPECT_EQ(0, ret);
expect1 = SRC_DATA;
expect2 = SRC_DATA;
} else {
/*
* If one of the pages is not readable from user-space copy_from_user
* should return ERR_FAULT, and the parts of dest_buf that could not be
* copied into should be set to 0.
* The destination kernel buffer should always be written so
* potentially uninitialized kernel data does not leak.
*/
EXPECT_EQ(ERR_FAULT, ret);
if (!(arch_mmu_flags_start & ARCH_MMU_FLAG_PERM_USER) ||
!dest_buf[TEST_BUF_COPY_START]) {
expect1 = 0;
} else {
expect1 = SRC_DATA;
}
expect2 = 0;
}
EXPECT_EQ(0, checkbuf(dest_buf + TEST_BUF_COPY_START, expect1,
TEST_BUF1_COPY_SIZE));
EXPECT_EQ(0, checkbuf(dest_buf + TEST_BUF1_SIZE, expect2,
TEST_BUF2_COPY_SIZE));
/* Dest bytes before and after copied region should be untouched */
EXPECT_EQ(DEST_DATA, dest_buf[0]);
EXPECT_EQ(DEST_DATA, dest_buf[TEST_BUF_SIZE - 1]);
/* Src buffer should not be modified */
if (src_kbuf1) {
EXPECT_EQ(0, checkbuf(src_kbuf1, SRC_DATA, TEST_BUF1_SIZE));
}
if (src_kbuf2) {
EXPECT_EQ(0, checkbuf(src_kbuf2, SRC_DATA, TEST_BUF2_SIZE));
}
}
#if ARCH_ARM64
#define ENABLED_ON_ARM64_NAME(name) name
#else
#define ENABLED_ON_ARM64_NAME(name) DISABLED_##name
#define copy_from_anywhere(dst, src, len) -1
#endif
TEST_P(usercopytest, ENABLED_ON_ARM64_NAME(copy_from_anywhere)) {
user_addr_t addr = get_addr_param();
uint32_t arch_mmu_flags_start = get_start_flags_param();
uint32_t arch_mmu_flags_end = get_end_flags_param();
int ret;
char dest_buf[TEST_BUF_SIZE];
char* src_kbuf1;
char* src_kbuf2;
char expect1;
char expect2;
memset(dest_buf, DEST_DATA, sizeof(dest_buf));
src_kbuf1 = paddr_to_kvaddr(vaddr_to_paddr((void*)(uintptr_t)addr));
src_kbuf2 = paddr_to_kvaddr(
vaddr_to_paddr((void*)(uintptr_t)addr + TEST_BUF1_SIZE));
/* src buffs should be NULL iff their flags are FLAGS_NO_PAGE */
EXPECT_EQ((src_kbuf1 == NULL), (arch_mmu_flags_start == FLAGS_NO_PAGE));
EXPECT_EQ((src_kbuf2 == NULL), (arch_mmu_flags_end == FLAGS_NO_PAGE));
usercopy_test_init_buf(src_kbuf1, src_kbuf2, SRC_DATA, -1);
/* Zero-length copy should always succeed */
ret = copy_from_anywhere(NULL, addr + TEST_BUF_COPY_START, 0);
EXPECT_EQ(0, ret);
/* Dest buffer should be untouched after zero-length copy */
EXPECT_EQ(0, checkbuf(dest_buf, DEST_DATA, TEST_BUF_SIZE));
/* Perform non-zero length copy */
ret = copy_from_anywhere(dest_buf + TEST_BUF_COPY_START,
addr + TEST_BUF_COPY_START, TEST_BUF_COPY_SIZE);
if (arch_mmu_flags_start != FLAGS_NO_PAGE &&
arch_mmu_flags_end != FLAGS_NO_PAGE) {
/*
* If both pages are readable, copy_from_anywhere should return
* success and every byte should be copied to dest_buf.
*/
EXPECT_EQ(0, ret);
expect1 = SRC_DATA;
expect2 = SRC_DATA;
} else {
/*
* If one of the pages is not readable copy_from_anywhere should
* return ERR_FAULT, and the parts of dest_buf that could not be
* copied into should be set to 0.
* The destination kernel buffer should always be written so
* potentially uninitialized kernel data does not leak.
*/
EXPECT_EQ(ERR_FAULT, ret);
if (arch_mmu_flags_start == FLAGS_NO_PAGE) {
expect1 = 0;
} else {
expect1 = SRC_DATA;
}
expect2 = 0;
}
EXPECT_EQ(0, checkbuf(dest_buf + TEST_BUF_COPY_START, expect1,
TEST_BUF1_COPY_SIZE));
EXPECT_EQ(0, checkbuf(dest_buf + TEST_BUF1_SIZE, expect2,
TEST_BUF2_COPY_SIZE));
/* Dest bytes before and after copied region should be untouched */
EXPECT_EQ(DEST_DATA, dest_buf[0]);
EXPECT_EQ(DEST_DATA, dest_buf[TEST_BUF_SIZE - 1]);
/* Src buffer should not be modified */
if (src_kbuf1) {
EXPECT_EQ(0, checkbuf(src_kbuf1, SRC_DATA, TEST_BUF1_SIZE));
}
if (src_kbuf2) {
EXPECT_EQ(0, checkbuf(src_kbuf2, SRC_DATA, TEST_BUF2_SIZE));
}
}
static void usercopy_test_strlcpy_from_user_inner(user_addr_t addr,
uint arch_mmu_flags_start,
uint arch_mmu_flags_end,
int copy_size,
int null_off) {
int ret;
char dest_buf[TEST_BUF_SIZE];
char* src_kbuf1;
char* src_kbuf2;
size_t dest_len;
int copy_len = copy_size ? copy_size - 1 : 0;
memset(dest_buf, DEST_DATA, sizeof(dest_buf));
src_kbuf1 = paddr_to_kvaddr(vaddr_to_paddr((void*)(uintptr_t)addr));
src_kbuf2 = paddr_to_kvaddr(
vaddr_to_paddr((void*)(uintptr_t)addr + TEST_BUF1_SIZE));
/* src buffs should be NULL iff their flags are FLAGS_NO_PAGE */
EXPECT_EQ((src_kbuf1 == NULL), (arch_mmu_flags_start == FLAGS_NO_PAGE));
EXPECT_EQ((src_kbuf2 == NULL), (arch_mmu_flags_end == FLAGS_NO_PAGE));
usercopy_test_init_buf(src_kbuf1, src_kbuf2, SRC_DATA, null_off);
ret = strlcpy_from_user(dest_buf + TEST_BUF_COPY_START,
addr + TEST_BUF_COPY_START, copy_size);
dest_len = strnlen(dest_buf + TEST_BUF_COPY_START, TEST_BUF_COPY_SIZE);
if (copy_size) {
/*
* Kernel buffer should always be null terminated.
*/
EXPECT_NE(TEST_BUF_COPY_SIZE, dest_len, " null_off=%d, copy_size=%d\n",
null_off, copy_size);
} else {
/*
* If copy_size is 0, then kernel buffer will not be null terminated.
*/
EXPECT_EQ(TEST_BUF_COPY_SIZE, dest_len, " null_off=%d, copy_size=%d\n",
null_off, copy_size);
dest_len = 0;
}
/*
* If the string in dest_buf is not empty it should only contain data from
* the source string.
*/
EXPECT_EQ(0, checkbuf(dest_buf + TEST_BUF_COPY_START, SRC_DATA, dest_len),
" null_off=%d, copy_size=%d\n", null_off, copy_size);
if ((arch_mmu_flags_start & ARCH_MMU_FLAG_PERM_USER) &&
((arch_mmu_flags_end & ARCH_MMU_FLAG_PERM_USER) ||
null_off < TEST_BUF1_SIZE)) {
/*
* If the pages readable from user-space contain a 0 terminated string,
* strlcpy_from_user should return the length of that string and every
* byte up to the 0 terminator that fits in dest_buf should be copied
* there. dest_buf should always be 0 terminated.
*/
EXPECT_EQ(null_off - TEST_BUF_COPY_START, ret,
" wrong strlen returned, null_off=%d, copy_size=%d\n",
null_off, copy_size);
EXPECT_EQ(MIN(null_off - TEST_BUF_COPY_START, copy_len), dest_len,
" null_off=%d, copy_size=%d\n", null_off, copy_size);
} else {
/*
* If one of the pages is not readable from user-space strlcpy_from_user
* should return ERR_FAULT, and dest_buf should have a null terminator
* at the start of the faulting page or at the start of the string.
*/
EXPECT_EQ(ERR_FAULT, ret, " null_off=%d, copy_size=%d\n", null_off,
copy_size);
if (!(arch_mmu_flags_start & ARCH_MMU_FLAG_PERM_USER)) {
EXPECT_EQ(0, dest_len, " null_off=%d, copy_size=%d\n", null_off,
copy_size);
} else if (dest_len) {
EXPECT_EQ(MIN(TEST_BUF1_COPY_SIZE, copy_len), dest_len,
" null_off=%d, copy_size=%d\n", null_off, copy_size);
}
}
/* Src buffer should not be modified */
if (src_kbuf1) {
if (null_off < TEST_BUF1_SIZE) {
EXPECT_EQ(0, checkbuf(src_kbuf1, SRC_DATA, null_off));
EXPECT_EQ('\0', src_kbuf1[null_off]);
EXPECT_EQ(0, checkbuf(src_kbuf1 + null_off + 1, SRC_DATA,
TEST_BUF1_SIZE - null_off - 1));
} else {
EXPECT_EQ(0, checkbuf(src_kbuf1, SRC_DATA, TEST_BUF1_SIZE));
}
}
if (src_kbuf2) {
if (null_off >= TEST_BUF1_SIZE) {
size_t null_off2 = null_off - TEST_BUF1_SIZE;
EXPECT_EQ(0, checkbuf(src_kbuf2, SRC_DATA, null_off2));
EXPECT_EQ('\0', src_kbuf2[null_off2]);
EXPECT_EQ(0, checkbuf(src_kbuf2 + null_off2 + 1, SRC_DATA,
TEST_BUF2_SIZE - null_off2 - 1));
} else {
EXPECT_EQ(0, checkbuf(src_kbuf2, SRC_DATA, TEST_BUF2_SIZE));
}
}
/* Dest bytes before and after copied region should be untouched */
EXPECT_EQ(DEST_DATA, dest_buf[0]);
EXPECT_EQ(DEST_DATA, dest_buf[TEST_BUF_COPY_START + copy_size]);
EXPECT_EQ(DEST_DATA, dest_buf[TEST_BUF_SIZE - 1]);
}
TEST_P(usercopytest, strlcpy_from_user) {
user_addr_t addr = get_addr_param();
uint32_t arch_mmu_flags_start = get_start_flags_param();
uint32_t arch_mmu_flags_end = get_end_flags_param();
size_t copy_sizes[] = {0, TEST_BUF1_COPY_SIZE, TEST_BUF_COPY_SIZE};
size_t copy_sizes_index;
int null_off;
int copy_size;
for (copy_sizes_index = 0; copy_sizes_index < countof(copy_sizes);
copy_sizes_index++) {
copy_size = copy_sizes[copy_sizes_index];
for (null_off = TEST_BUF_COPY_START; null_off < TEST_BUF_SIZE;
null_off++) {
usercopy_test_strlcpy_from_user_inner(addr, arch_mmu_flags_start,
arch_mmu_flags_end, copy_size,
null_off);
}
}
}
static const char* flags_to_str(uint32_t flags) {
switch (flags) {
case FLAGS_NO_PAGE:
return "--";
case FLAGS_NO_USER:
return "ko";
case FLAGS_RO_USER:
return "ro";
case FLAGS_RW_USER:
return "rw";
default:
return "??";
}
}
static void user_param_to_string(const void* param,
char* buf,
size_t buf_size) {
uint32_t start_flags = get_start_flags_param();
uint32_t end_flags = get_end_flags_param();
size_t count = 0;
count = scnprintf(buf + count, buf_size - count, "%s",
flags_to_str(start_flags));
scnprintf(buf + count, buf_size - count, "%s", flags_to_str(end_flags));
}
INSTANTIATE_TEST_SUITE_P(UserCopyTestParams,
usercopytest,
testing_Combine(testing_Values(TEST_BUF_ADDR),
testing_Values(FLAGS_NO_PAGE,
FLAGS_NO_USER,
FLAGS_RO_USER,
FLAGS_RW_USER),
testing_Values(FLAGS_NO_PAGE,
FLAGS_NO_USER,
FLAGS_RO_USER,
FLAGS_RW_USER)),
user_param_to_string);
#if IS_64BIT && USER_32BIT
/*
* Tests with Kernel addresses are not applicable to arm64u32 since kernel
* addresses do not fit in a user_addr_t.
*/
static_assert(KERNEL_BASE > UINT32_MAX);
PORT_TEST(usercopy_tests, PORT_NAME)
#else
/* These are filled in before the tests are run */
static user_addr_t kernel_addrs[3];
static void kernel_param_to_string(const void* param,
char* buf,
size_t buf_size) {
const void* const* kernel_param = param;
size_t idx = ((user_addr_t*)kernel_param[0] - kernel_addrs);
const char* str;
switch (idx) {
case STACK_ADDR_IDX:
str = "kernel-stack";
break;
case HEAP_ADDR_IDX:
str = "kernel-heap";
break;
case GLOBAL_ADDR_IDX:
str = "kernel-global";
break;
default:
str = "unknown-address-type";
}
scnprintf(buf, buf_size, "%s", str);
}
INSTANTIATE_TEST_SUITE_P(KernelUserCopyTestParams,
usercopytest,
testing_Combine(testing_ValuesIn(kernel_addrs),
testing_Values(FLAGS_NO_USER),
testing_Values(FLAGS_NO_USER)),
kernel_param_to_string);
static bool run_usercopy_test(struct unittest* test) {
bool tests_passed;
static uint8_t global_buf[TEST_BUF_SIZE];
uint8_t stack_buf[TEST_BUF_SIZE];
uint8_t* heap_buf = malloc(TEST_BUF_SIZE);
ASSERT(heap_buf);
kernel_addrs[STACK_ADDR_IDX] = (user_addr_t)stack_buf;
kernel_addrs[HEAP_ADDR_IDX] = (user_addr_t)heap_buf;
kernel_addrs[GLOBAL_ADDR_IDX] = (user_addr_t)global_buf;
tests_passed = RUN_ALL_TESTS();
free(heap_buf);
return tests_passed;
}
static void usercopy_test_init(uint level) {
static struct unittest usercopy_unittest = {
.port_name = PORT_NAME,
.run_test = run_usercopy_test,
};
unittest_add(&usercopy_unittest);
}
LK_INIT_HOOK(usercopy_test, usercopy_test_init, LK_INIT_LEVEL_APPS);
#endif // !(IS_64BIT && USER_32BIT)