/* * 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 #include #include #include #include #include #include #include #ifdef ARCH_ARM64 #include #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)