/* * Copyright (c) 2021, 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 #if KERNEL_SCS_ENABLED #define FEATURE_GATED_TEST_NAME(name) name /** * inspect_thread() - Inspect the shadow stack of a thread * * @t: thread to test * @ss_sz: expected shadow stack size */ static void inspect_thread(thread_t *t, size_t ss_sz) { /* all threads have a shadow stack when the feature is enabled */ ASSERT_NE(NULL, t->shadow_stack, "Shadow call stack missing"); ASSERT_EQ(true, is_kernel_address((vaddr_t)t->shadow_stack), "Shadow call stack does not point to kernel memory"); EXPECT_EQ(t->shadow_stack_size, round_up(t->shadow_stack_size, sizeof(vaddr_t)), "Shadow call stack size was not rounded to the pointer size"); EXPECT_NE(t->stack, t->shadow_stack, "Shadow call stack aliases the regular stack"); EXPECT_EQ(ss_sz, t->shadow_stack_size, "Shadow call stack did not have the expected size"); /* check the shadow stack size by inverting the last element */ void* last_elem = t->shadow_stack + t->shadow_stack_size - sizeof(vaddr_t); EXPECT_EQ(NO_ERROR, mmutest_arch_store_uint32((uint32_t*)last_elem, false), "Actual size of shadow call stack differs from recorded size"); /* restore last_elem by calling mmutest_arch_store_uint32 a second time */ EXPECT_EQ(NO_ERROR, mmutest_arch_store_uint32((uint32_t*)last_elem, false), "Restoring last element of shadow stack failed"); const size_t extra_space = round_up(t->shadow_stack_size, PAGE_SIZE) - t->shadow_stack_size; void *const guard_region = t->shadow_stack - extra_space; EXPECT_EQ(0, (vaddr_t)guard_region & (PAGE_SIZE - 1), "Shadow call stack guard region is not page aligned"); /* check for guard page before the shadow stack */ void* before_guard_end = guard_region - sizeof(uint32_t); ASSERT_EQ(ERR_GENERIC, mmutest_arch_store_uint32((uint32_t*)before_guard_end, false), "Expected guard page after shadow call stack"); /* check for guard page after the shadow stack */ void *after_guard_begin = t->shadow_stack + t->shadow_stack_size; ASSERT_EQ(ERR_GENERIC, mmutest_arch_store_uint32((uint32_t*)after_guard_begin, false), "Expected guard page after shadow call stack"); /* * this test will not run on idle threads which are the only * threads that do not have the free shadow stack flag set. */ ASSERT_EQ(0, t->flags & THREAD_FLAG_IDLE, "Thread is an idle thread"); EXPECT_NE(0, t->flags & THREAD_FLAG_FREE_SHADOW_STACK, "Shadow call stack did not have the free flag set"); /* * Shadow stacks grow up. Test that the shadow stack is set up such that * we'll hit the guard page once we use the number of bytes corresponding * to the shadow stack size even if more bytes were allocated. We do so * by checking that all bytes are zero (i.e., unused) in the interval * [guard_region...t->shadow_stack). */ vaddr_t *slot = (vaddr_t *)guard_region; while (slot < (vaddr_t*)t->shadow_stack) { ASSERT_EQ(0, *slot++, "Expected unused shadow call stack slot"); } /* shadow stack slots are either unused or point to kernel memory */ while (slot < (vaddr_t*)after_guard_begin) { vaddr_t ret_addr = *slot++; if (!ret_addr) continue; /* slot is unused */ ASSERT_EQ(true, is_kernel_address(ret_addr), "Expected pointer to kernel memory"); } test_abort:; } #else #define FEATURE_GATED_TEST_NAME(name) DISABLED_##name static void inspect_thread(thread_t *t, size_t ss_sz) { } #endif TEST(scstest, FEATURE_GATED_TEST_NAME(current_kernel_thread_has_scs)) { thread_t *curr_thread = get_current_thread(); inspect_thread(curr_thread, DEFAULT_SHADOW_STACK_SIZE); } static int new_thread_func(void* arg) { size_t expected_shadow_stack_size = *(size_t*)arg; thread_t *curr_thread = get_current_thread(); inspect_thread(curr_thread, expected_shadow_stack_size); return 0; } TEST(scstest, FEATURE_GATED_TEST_NAME(new_kernel_thread_has_scs)) { int test_thread_ret; size_t shadow_stack_size = DEFAULT_SHADOW_STACK_SIZE; thread_t* test_thread = thread_create("scstest_thread", new_thread_func, &shadow_stack_size, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE); ASSERT_NE(NULL, test_thread, "Failed to create test thread"); ASSERT_EQ(NO_ERROR, thread_resume(test_thread), "Failed to start thread"); ASSERT_EQ(NO_ERROR, thread_join(test_thread, &test_thread_ret, INFINITE_TIME), "Failed to wait on test thread"); /* test_thread is deallocated here, do inspection in new_thread_func */ test_abort:; } TEST(scstest, FEATURE_GATED_TEST_NAME(new_kernel_thread_has_custom_size)) { int test_thread_ret; size_t expected_shadow_stack_size = 128; thread_t* test_thread = thread_create_etc(NULL, "scstest_thread", new_thread_func, &expected_shadow_stack_size, DEFAULT_PRIORITY, NULL, DEFAULT_STACK_SIZE, expected_shadow_stack_size); ASSERT_NE(NULL, test_thread, "Failed to create test thread"); ASSERT_EQ(NO_ERROR, thread_resume(test_thread), "Failed to start thread"); ASSERT_EQ(NO_ERROR, thread_join(test_thread, &test_thread_ret, INFINITE_TIME), "Failed to wait on test thread"); test_abort:; } PORT_TEST(scstest, "com.android.kernel.scstest");