/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #if defined(TRUSTY_USERSPACE) #include #include #include #include #else #include #include #include #endif #include #define CHECK_ERRNO(e) \ do { \ ASSERT_EQ(e, errno); \ errno = 0; \ } while (0) #define CLEAR_ERRNO() \ do { \ errno = 0; \ } while (0) typedef struct libc { } libc_t; TEST_F_SETUP(libc) { /* Isolate the tests. */ CLEAR_ERRNO(); } TEST_F_TEARDOWN(libc) { /* errno should have been checked and cleared if the test sets errno. */ CHECK_ERRNO(0); test_abort:; } #define BUFFER_SIZE 100 #define EXPECT_STREQ_COND(lhs, rhs_true, rhs_false, condition) \ EXPECT_STREQ((lhs), (condition) ? (rhs_true) : (rhs_false)) /* * Smoke test to make sure the endian functions are defined. * Musl may or may not expose them, depending on the feature test macros. */ TEST_F(libc, endian) { const uint32_t test_data = 0x12345678; /* TODO test le32, etc, once they are provided. */ ASSERT_EQ(test_data, be32toh(htobe32(test_data))); test_abort:; } /* * Test explicit_bzero() is present and functional. * explicit_bzero() exists is to defeat optimisations that may remove 'dead' * writes when trying to remove secrets from memory. The success of this is * hard to detect at runtime and best achieved by inspecting assembly listings * where needed. */ TEST_F(libc, explicit_bzero_test) { uint32_t test_data = 0x12345678; explicit_bzero(&test_data, sizeof(uint32_t)); EXPECT_EQ(test_data, 0); } TEST_F(libc, memset_test) { unsigned char buf[130]; buf[0] = 0; buf[129] = 0; for (int val = 1; val < 256; val <<= 1) { memset(&buf[1], val, 128); ASSERT_EQ(0, buf[0], "iteration %d", val); for (unsigned int i = 1; i < 128; i++) { ASSERT_EQ(val, buf[i], "iteration %d", val); } ASSERT_EQ(0, buf[129], "iteration %d", val); } test_abort:; } TEST_F(libc, memcmp_test) { unsigned char buf1[128]; unsigned char buf2[128]; /* Identical buffers. */ memset(buf1, 7, sizeof(buf1)); memset(buf2, 7, sizeof(buf2)); ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); /* buf1 slightly greater. */ buf1[127] = 9; buf2[127] = 8; ASSERT_LT(0, memcmp(buf1, buf2, sizeof(buf1))); /* buf1 much greater. */ buf1[127] = 127; buf2[127] = 0; ASSERT_LT(0, memcmp(buf1, buf2, sizeof(buf1))); /* buf2 slightly greater. */ buf1[127] = 8; buf2[127] = 9; ASSERT_GT(0, memcmp(buf1, buf2, sizeof(buf1))); /* buf2 much greater. */ buf1[127] = 0; buf2[127] = 127; ASSERT_GT(0, memcmp(buf1, buf2, sizeof(buf1))); /* Buffers are identical again. */ memcpy(buf2, buf1, sizeof(buf1)); ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); test_abort:; } TEST_F(libc, strcmp_test) { ASSERT_EQ(0, strcmp("", "")); ASSERT_GT(0, strcmp("", "bar")); ASSERT_LT(0, strcmp("bar", "")); ASSERT_EQ(0, strcmp("bar", "bar")); ASSERT_GT(0, strcmp("bar", "baz")); ASSERT_LT(0, strcmp("baz", "bar")); ASSERT_GT(0, strcmp("bar", "barbar")); ASSERT_LT(0, strcmp("barbar", "bar")); char negative[2] = {-127, 0}; char positive[2] = {0, 0}; // strcmp must treat characters as unsigned ASSERT_LT(0, strcmp(negative, positive)); ASSERT_LT(0, strncmp(negative, positive, 1)); test_abort:; } #if defined(TRUSTY_USERSPACE) #define MSEC 1000000ULL /* * Smoke test the time-related functions. * As long as gettime and nanosleep behave semi-reasonablly, we're happy. */ TEST_F(libc, time) { int64_t begin = 0; int64_t end = 0; int64_t delta = 0; trusty_gettime(0, &begin); trusty_nanosleep(0, 0, 10 * MSEC); trusty_gettime(0, &end); delta = end - begin; ASSERT_LT(1 * MSEC, delta); /* We've observed 200 ms sleeps in the emulator, so be generous. */ ASSERT_LT(delta, 1000 * MSEC); test_abort:; } /* Smoke test because we mocked out timezone functions. */ TEST_F(libc, localtime) { time_t time = 0; struct tm* result = localtime(&time); ASSERT_NE(NULL, result); /* Epoch. */ EXPECT_EQ(70, result->tm_year); EXPECT_EQ(0, result->tm_mon); EXPECT_EQ(1, result->tm_mday); EXPECT_EQ(0, result->tm_hour); EXPECT_EQ(0, result->tm_min); EXPECT_EQ(0, result->tm_sec); time += 24 * 60 * 60; result = localtime(&time); ASSERT_NE(NULL, result); EXPECT_EQ(70, result->tm_year); EXPECT_EQ(0, result->tm_mon); EXPECT_EQ(2, result->tm_mday); EXPECT_EQ(0, result->tm_hour); EXPECT_EQ(0, result->tm_min); EXPECT_EQ(0, result->tm_sec); test_abort:; } #endif TEST_F(libc, snprintf_test) { char buffer[16]; ASSERT_EQ(17, snprintf(buffer, sizeof(buffer), "%d %x %s...", 12345, 254, "hello")); ASSERT_EQ(0, strcmp(buffer, "12345 fe hello.")); test_abort:; } TEST_F(libc, atoi_test) { ASSERT_EQ(12345, atoi("12345")); ASSERT_EQ(-67890, atoi("-67890")); /* Note: Out-of-bound values are undefined behavior. */ test_abort:; } TEST_F(libc, print_test) { /* * Test printing compiles and doesn't crash. Yes, this is a weak test. * A stronger test would be better, but also more complicated. Stay simple, * for now. */ printf("Hello, stdout.\n"); fprintf(stderr, "Hello, stderr.\n"); CHECK_ERRNO(0); test_abort:; } #if defined(TRUSTY_USERSPACE) TEST_F(libc, print_float_test) { /* * %f should be valid and not cause an error, even if floating point * support is disabled. */ printf("num: %f\n", 1.23); CHECK_ERRNO(0); test_abort:; } #endif TEST_F(libc, print_errno_test) { /* * %m is not supported, but should not be an error, either. */ printf("err: %m\n"); CHECK_ERRNO(0); test_abort:; } TEST_F(libc, print_bad_test) { printf("[%k]\n"); /* TODO: EINVAL */ CLEAR_ERRNO(); test_abort:; } /* * Grab the frame pointer in a simple, non-inlined function. * Note this isn't a static function. We're trying to game the optimizer and * ensure it doesn't change the calling convention. */ __attribute__((__noinline__)) uintptr_t frame_ptr(void) { return (uintptr_t)__builtin_frame_address(0); } #if defined(TRUSTY_USERSPACE) TEST_F(libc, stack_alignment) { /* * On all the platforms we support, the frame pointer should be aligned to 2 * times pointer size. This includes x86_64 because the stack pointer is * implicitly re-aligned after function entry before it becomes the frame * pointer. * Note that this test passing does not guarantee correctness, but it can * catch badness. */ const uintptr_t alignment_mask = sizeof(void*) * 2 - 1; ASSERT_EQ(0, frame_ptr() & alignment_mask); test_abort:; } TEST_F(libc, stack_cookies) { uint64_t* p = (uint64_t*)getauxval(AT_RANDOM); ASSERT_NE(0, p); ASSERT_EQ(true, 0 != *p || 0 != *(p + 1)); test_abort:; } #if __has_feature(shadow_call_stack) void** guard_region_ptr(void); TEST_F(libc, shadow_call_stack) { /* * Leaf functions keep return address in the link register but the call * to guard_region_ptr will not get inlined which makes this a non-leaf * function -> return address of this function goes on the shadow stack */ void** guard_region_top = guard_region_ptr(); ASSERT_NE(0, guard_region_top); /* Get return address from stack */ void* ret_addr = __builtin_return_address(0); /* * Guard region top points to next free word so the * shadow copy of the return address is right below */ void* shadow_ret_addr = *(guard_region_top - 1); ASSERT_EQ(ret_addr, shadow_ret_addr); test_abort:; } #endif /* __has_feature(shadow_call_stack) */ #endif #define SCNPRINTF_TEST_BUF_LEN 8 TEST_F(libc, scnprintf) { char buf[SCNPRINTF_TEST_BUF_LEN]; const size_t buf_size = SCNPRINTF_TEST_BUF_LEN - 2; buf[0] = 'z'; /* We should always return 0 in the case of a zero size */ EXPECT_EQ(0, scnprintf(buf, 0, "foo")); /* We should have written nothing to the buffer */ EXPECT_EQ('z', buf[0]); buf[buf_size] = 'q'; /* If we would overflow, we should return chars printed */ EXPECT_EQ(buf_size - 1, scnprintf(buf, buf_size, "aaaaaaa")); /* If we would overflow, we should also not have written past end */ EXPECT_EQ('q', buf[buf_size]); /* The buffer should still be null terminated */ EXPECT_EQ(0, buf[buf_size - 1]); /* If we would fit, we should return the same as snprintf */ EXPECT_EQ(3, scnprintf(buf, buf_size, "%d\n", 10)); /* If it would fit, there should be a null terminator */ EXPECT_EQ(buf[3], 0); test_abort:; } TEST_F(libc, str_to_uuid) { const char* valid_str = "b100aae1-c0b3-4b8b-9e25-e69523968f7e"; const char* invalid_str; struct uuid res_uuid; struct uuid expected_uuid = { 0xb100aae1, 0xc0b3, 0x4b8b, {0x9e, 0x25, 0xe6, 0x95, 0x23, 0x96, 0x8f, 0x7e}}; invalid_str = "b100aae1-c0b3-4b8b-9e25-e69523968f7"; /* The string must be exactly 36 characters */ EXPECT_EQ(-1, str_to_uuid(invalid_str, &res_uuid)); invalid_str = "b100aae1c0b3-4b8b-9e25-e69523968f7e"; /* There must be exactly 5 groups */ EXPECT_EQ(-1, str_to_uuid(invalid_str, &res_uuid)); invalid_str = "b100aa-e1c0b3-4b8b-9e25-e69523968f7e"; /* Hyphens must be at specific locations */ EXPECT_EQ(-1, str_to_uuid(invalid_str, &res_uuid)); invalid_str = "g100aae1-c0b3-4b8b-9e25-e69523968f7e"; /* The string must contain only hyphens and hex characters */ EXPECT_EQ(-1, str_to_uuid(invalid_str, &res_uuid)); invalid_str = "B100aae1-c0b3-4b8b-9e25-e69523968f7e"; /* Hex characters must be lower case */ EXPECT_EQ(-1, str_to_uuid(invalid_str, &res_uuid)); EXPECT_EQ(0, str_to_uuid(valid_str, &res_uuid)); EXPECT_EQ(0, memcmp(&expected_uuid, &res_uuid, sizeof(struct uuid))); } TEST_F(libc, uuid_to_str) { const char* expected_str = "b100aae1-c0b3-4b8b-9e25-e69523968f7e"; const char* zero_str = "00000000-0000-0000-0000-000000000000"; struct uuid zero_uuid = {0}; char result_str[UUID_STR_SIZE]; struct uuid uuid = {0xb100aae1, 0xc0b3, 0x4b8b, {0x9e, 0x25, 0xe6, 0x95, 0x23, 0x96, 0x8f, 0x7e}}; /* Check for correct padding */ uuid_to_str(&zero_uuid, result_str); EXPECT_EQ(0, strncmp(zero_str, result_str, UUID_STR_SIZE)); uuid_to_str(&uuid, result_str); EXPECT_EQ(0, strncmp(expected_str, result_str, UUID_STR_SIZE)); } #if defined(TRUSTY_USERSPACE) /* * We're linking a prebuilt libgcc / compiler_rt provided by the toolchain. * It wasn't designed for Trusty, so does it actually work? If we set things up * wrong there may be ABI issues. One way to smoke these issues out is call * functions that take floating point arguments. However - libgcc does not * provide a full set of functions for every arch, only the ones it expects to * use. This means we need to do some arch-specific testing. */ #ifdef __arm__ extern double __extendsfdf2(float a); extern float __truncdfsf2(double a); TEST_F(libc, float_builtins) { EXPECT_EQ(123, (int)__truncdfsf2(__extendsfdf2(123.0f))); } #endif #ifdef __aarch64__ extern long double __extendsftf2(float a); extern float __trunctfsf2(long double a); TEST_F(libc, float_builtins) { EXPECT_EQ(123, (int)__trunctfsf2(__extendsftf2(123.0f))); } #endif /* * We provide a mock implementation of stdin because libcxx refers to it. * Make sure the mock behaves in a reasonable manner. */ TEST_F(libc, getc) { EXPECT_EQ(EOF, getc(stdin)); test_abort:; } #endif #if __ARM_NEON__ || __ARM_NEON #include /* * NOTE this is a fairly weak test that checks if a neon instruction can be * executed. This will help detect cases where the build flags do not match the * actual system the code is running on. */ TEST_F(libc, basic_neon) { int8x16_t block1 = vdupq_n_u8(0x55); int8x16_t block2 = vdupq_n_u8(0x33); int8x16_t expected; int8x16_t result; /* memset just to be sure. */ memset(&expected, 0x66, sizeof(int8x16_t)); result = veorq_s8(block1, block2); ASSERT_EQ(0, memcmp(&expected, &result, sizeof(int8x16_t))); test_abort:; } #endif #if defined(TRUSTY_USERSPACE) TEST_F(libc, sbrk) { /* Allocating and releasing a small range should succeed */ const ssize_t brk_test_size = 64; void* orig_brk = sbrk(brk_test_size); ASSERT_NE(orig_brk, (void*)-1); void* test_brk = sbrk(0); ASSERT_EQ(sbrk(-brk_test_size), test_brk); ASSERT_EQ(orig_brk, sbrk(0)); /* Allocating an oversized range should fail */ ASSERT_EQ(sbrk(10 * 4096), (void*)-1); ASSERT_EQ(errno, ENOMEM); test_abort: CLEAR_ERRNO(); } #endif TEST_F(libc, SnprintfLargePointerTest) { char buffer[BUFFER_SIZE]; snprintf(buffer, BUFFER_SIZE, "pointer: %p", (void*)0x5000); EXPECT_STREQ(buffer, "pointer: 0x5000"); } TEST_F(libc, SmallIntegerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "%d", 100); EXPECT_STREQ(buffer, "100"); } TEST_F(libc, NullPointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %p", (void*)0); #if defined(TRUSTY_USERSPACE) EXPECT_STREQ(buffer, "pointer: 0"); #else EXPECT_STREQ(buffer, "pointer: 0x0"); #endif } TEST_F(libc, SmallPointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %p", (void*)0x1000); EXPECT_STREQ(buffer, "pointer: 0x1000"); } TEST_F(libc, SmallPseudoNegativePointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %p", (void*)-4096); if (sizeof(void*) == 4) { EXPECT_STREQ(buffer, "pointer: 0xfffff000"); } else { EXPECT_STREQ(buffer, "pointer: 0xfffffffffffff000"); } } TEST_F(libc, BiggerPseudoNegativePointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %p", (void*)-4097); if (sizeof(void*) == 4) { EXPECT_STREQ_COND(buffer, "pointer: 0x***", "pointer: 0xffffefff", RELEASE_BUILD); } else { EXPECT_STREQ_COND(buffer, "pointer: 0x***", "pointer: 0xffffffffffffefff", RELEASE_BUILD); } } TEST_F(libc, SmallestPseudoNegativePointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %p", (void*)-1); if (sizeof(void*) == 4) { EXPECT_STREQ(buffer, "pointer: 0xffffffff"); } else { EXPECT_STREQ(buffer, "pointer: 0xffffffffffffffff"); } } TEST_F(libc, PointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %p", (void*)0x5000); EXPECT_STREQ_COND(buffer, "pointer: 0x***", "pointer: 0x5000", RELEASE_BUILD); } TEST_F(libc, PointerSprintfTest) { char buffer[BUFFER_SIZE]; sprintf(buffer, "pointer: %p", (void*)0x5000); EXPECT_STREQ(buffer, "pointer: 0x5000"); } TEST_F(libc, PointerSnprintfTest) { char buffer[BUFFER_SIZE]; snprintf(buffer, BUFFER_SIZE, "pointer: %p", (void*)0x5000); EXPECT_STREQ(buffer, "pointer: 0x5000"); } TEST_F(libc, LargerIntTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "integer: %d", 4097); EXPECT_STREQ_COND(buffer, "integer: ***", "integer: 4097", RELEASE_BUILD); } TEST_F(libc, LargerNegIntTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "integer: %d", -4097); EXPECT_STREQ_COND(buffer, "integer: ***", "integer: -4097", RELEASE_BUILD); } TEST_F(libc, PointerAndUnsignedOneLineOneBigOneSmall) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %u", 0x5000, 100); EXPECT_STREQ_COND(buffer, "pointer1: 0x*** number: 100", "pointer1: 0x5000 number: 100", RELEASE_BUILD); } TEST_F(libc, PointerAndUnsignedOneLineOneBigOneSmallInverse) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %u", 0x500, 10000); EXPECT_STREQ_COND(buffer, "pointer1: 0x500 number: ***", "pointer1: 0x500 number: 10000", RELEASE_BUILD); } TEST_F(libc, OnePointersTwoIntsOneLineOneSmallTwoBig) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %u hex: %x", 0x5, 10000, 0X70); EXPECT_STREQ_COND(buffer, "pointer1: 0x5 number: *** hex: 70", "pointer1: 0x5 number: 10000 hex: 70", RELEASE_BUILD); } TEST_F(libc, OnePointersTwoIntsOneLineOneSmallTwoBigInverse) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %u hex: %x", 0x5000, 10, 0X7000); EXPECT_STREQ_COND(buffer, "pointer1: 0x*** number: 10 hex: ***", "pointer1: 0x5000 number: 10 hex: 7000", RELEASE_BUILD); } TEST_F(libc, SmallIntTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "integer: %d", 4096); EXPECT_STREQ(buffer, "integer: 4096"); } TEST_F(libc, SmallNegIntTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "integer: %d", -4096); EXPECT_STREQ(buffer, "integer: -4096"); } TEST_F(libc, LargerUintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "unsigned integer: %u", 4097); EXPECT_STREQ_COND(buffer, "unsigned integer: ***", "unsigned integer: 4097", RELEASE_BUILD); } TEST_F(libc, LargerHexTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "unsigned integer: 0x%x", 0x1001); EXPECT_STREQ_COND(buffer, "unsigned integer: 0x***", "unsigned integer: 0x1001", RELEASE_BUILD); } TEST_F(libc, PrintfBufferLargeEnough) { char buffer[BUFFER_SIZE]; buffer[5] = '@'; snprintf_filtered(buffer, 5, "%x", 0x3000); EXPECT_STREQ_COND(buffer, "***", "3000", RELEASE_BUILD); EXPECT_EQ(buffer[5], '@'); } TEST_F(libc, PrintfBufferLargeEnoughForRelease) { char buffer[BUFFER_SIZE]; buffer[4] = '@'; snprintf_filtered(buffer, 4, "%x", 0x3000); EXPECT_STREQ_COND(buffer, "***", "300", RELEASE_BUILD); EXPECT_EQ(buffer[4], '@'); } TEST_F(libc, PrintfBufferTooSmallForRelease) { char buffer[BUFFER_SIZE]; buffer[3] = '@'; snprintf_filtered(buffer, 3, "%x", 0x3000); EXPECT_STREQ_COND(buffer, "**", "30", RELEASE_BUILD); EXPECT_EQ(buffer[3], '@'); } TEST_F(libc, SmallHexTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "unsigned integer: 0x%x", 0x1000); EXPECT_STREQ(buffer, "unsigned integer: 0x1000"); } TEST_F(libc, PointerAndUnsignedOneLine) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %u", 0x5000, 10000); EXPECT_STREQ_COND(buffer, "pointer1: 0x*** number: ***", "pointer1: 0x5000 number: 10000", RELEASE_BUILD); } TEST_F(libc, PointerUnsignedOneLineFilterOne) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %px number: %u", 0x5000, 10000); EXPECT_STREQ_COND(buffer, "pointer1: 0x5000 number: ***", "pointer1: 0x5000 number: 10000", RELEASE_BUILD); } TEST_F(libc, PointerUnsignedOneLineFilterOneInverse) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %ux", 0x5000, 10000); EXPECT_STREQ_COND(buffer, "pointer1: 0x*** number: 10000", "pointer1: 0x5000 number: 10000", RELEASE_BUILD); } TEST_F(libc, PointerUnsignedHexOneLineFilterOne) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %px number: %u hex: %xx", 0x5000, 10000, 0X7000); EXPECT_STREQ_COND(buffer, "pointer1: 0x5000 number: *** hex: 7000", "pointer1: 0x5000 number: 10000 hex: 7000", RELEASE_BUILD); } TEST_F(libc, PointerUnsignedHexOneLineFilterOneInverse) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer1: %p number: %ux hex: %x", 0x5000, 10000, 0X7000); EXPECT_STREQ_COND(buffer, "pointer1: 0x*** number: 10000 hex: ***", "pointer1: 0x5000 number: 10000 hex: 7000", RELEASE_BUILD); } TEST_F(libc, ReleaseUnfilteredPointerPrintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %px", (void*)0x5000); EXPECT_STREQ(buffer, "pointer: 0x5000"); } TEST_F(libc, ReleaseUnfilteredLargNegIntTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "integer: %dx", -4097); EXPECT_STREQ(buffer, "integer: -4097"); } TEST_F(libc, ReleaseUnfilteredLargerHexTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "unsigned integer: 0x%xx", 0x1001); EXPECT_STREQ(buffer, "unsigned integer: 0x1001"); } TEST_F(libc, ReleaseUnfilteredLargerUintTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "unsigned integer: %ux", 4097); EXPECT_STREQ(buffer, "unsigned integer: 4097"); } TEST_F(libc, ReleaseUnfilteredLargerUintXAtEndTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "unsigned integer: %uxx", 34127); EXPECT_STREQ(buffer, "unsigned integer: 34127x"); } TEST_F(libc, ReleaseUnfilteredPrintfBufferLargeEnoughTest) { char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); buffer[5] = '@'; snprintf_filtered(buffer, 5, "%xx", 0x3000); EXPECT_STREQ(buffer, "3000"); EXPECT_EQ(buffer[5], '@'); } TEST_F(libc, ReleaseUnfilteredPrintfBufferLargeEnoughForReleaseTest) { char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); buffer[4] = '@'; snprintf_filtered(buffer, 4, "%xx", 0x3000); EXPECT_STREQ(buffer, "300"); EXPECT_EQ(buffer[4], '@'); } TEST_F(libc, ReleaseUnfilteredPrintfBufferTooSmallForReleaseTest) { char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); buffer[3] = '@'; snprintf_filtered(buffer, 3, "%xx", 0x3000); EXPECT_STREQ(buffer, "30"); EXPECT_EQ(buffer[3], '@'); } TEST_F(libc, ReleaseUnfilteredPrintfStringXPrintsTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "%sx", "hello"); EXPECT_STREQ(buffer, "hellox"); } TEST_F(libc, ThreeModifierTogetherOneNotFilteredTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "%d%xx%u", 98765, 0x43210, 123456); EXPECT_STREQ_COND(buffer, "***43210***", "9876543210123456", RELEASE_BUILD); } TEST_F(libc, ThreeModifierTogetherOneNotFilteredInverseTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "%dx%x%ux", 98765, 0x43210, 123456); EXPECT_STREQ_COND(buffer, "98765***123456", "9876543210123456", RELEASE_BUILD); } TEST_F(libc, ReleaseUnfilteredThreeModifiersTest) { char buffer[BUFFER_SIZE]; snprintf_filtered(buffer, BUFFER_SIZE, "pointer: %px unsigned: %ux signed: %dx", (void*)0x5000, 7000, 80000); EXPECT_STREQ(buffer, "pointer: 0x5000 unsigned: 7000 signed: 80000"); } TEST_F(libc, SnprintfModifierNotUsedTest) { char buffer[BUFFER_SIZE]; snprintf(buffer, BUFFER_SIZE, "hex: %xx pointer: %px unsigned: %ux signed: %dx", 2, (void*)3, 4, 5); EXPECT_STREQ(buffer, "hex: 2x pointer: 0x3x unsigned: 4x signed: 5x"); } TEST_F(libc, UnsignedOverflowMacros) { for (int i = 0; i < 0x100; i++) { // When the macros defined in ctype.h are used for these functions, they // trigger UBSAN for a subset of the inputs. Otherwise the function // calls are likely optimized out since the results aren't used. (void)isalpha(i); (void)isdigit(i); (void)islower(i); (void)isupper(i); (void)isprint(i); (void)isgraph(i); (void)isspace(i); } } #if defined(TRUSTY_USERSPACE) #define TEST_BUF_SIZE 64 TEST_F(libc, PrepareDmaFailsOnMultipleCalls) { uint8_t buf[TEST_BUF_SIZE] = {0}; struct dma_pmem dma; int rc = prepare_dma(buf, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE, &dma); EXPECT_GE(rc, 1); /* Second prepare should fail */ rc = prepare_dma(buf, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE, &dma); EXPECT_EQ(ERR_INVALID_ARGS, rc); rc = finish_dma(buf, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE); EXPECT_EQ(NO_ERROR, rc); } TEST_F(libc, PrepareInputOutputDmaDifferentBufs) { uint8_t buf_in[TEST_BUF_SIZE] = {0}; uint8_t buf_out[TEST_BUF_SIZE] = {0}; struct dma_pmem dma_in; struct dma_pmem dma_out; int rc = prepare_input_output_dma(buf_in, TEST_BUF_SIZE, buf_out, TEST_BUF_SIZE, &dma_in, &dma_out); EXPECT_EQ(NO_ERROR, rc); /* Two areas should have been tracked */ rc = finish_dma(buf_in, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE); EXPECT_EQ(NO_ERROR, rc); rc = finish_dma(buf_out, TEST_BUF_SIZE, DMA_FLAG_FROM_DEVICE); EXPECT_EQ(NO_ERROR, rc); } TEST_F(libc, PrepareWithDifferentBufSizes) { uint8_t buf[TEST_BUF_SIZE] = {0}; struct dma_pmem dma_in; struct dma_pmem dma_out; int rc = prepare_input_output_dma(buf, TEST_BUF_SIZE, buf, TEST_BUF_SIZE / 2, &dma_in, &dma_out); EXPECT_EQ(ERR_INVALID_ARGS, rc); } TEST_F(libc, PrepareInputOutputDmaSameBufs) { uint8_t buf_in[TEST_BUF_SIZE] = {0}; uint8_t* buf_out = buf_in; struct dma_pmem dma_in; struct dma_pmem dma_out; int rc = prepare_input_output_dma(buf_in, TEST_BUF_SIZE, buf_out, TEST_BUF_SIZE, &dma_in, &dma_out); EXPECT_EQ(NO_ERROR, rc); /* One area should have been tracked */ rc = finish_dma(buf_in, TEST_BUF_SIZE, DMA_FLAG_BIDIRECTION); EXPECT_EQ(NO_ERROR, rc); } TEST_F(libc, FinishInputOutputDmaDifferentBufs) { uint8_t buf_in[TEST_BUF_SIZE] = {0}; uint8_t buf_out[TEST_BUF_SIZE] = {0}; struct dma_pmem dma_in; struct dma_pmem dma_out; int rc = prepare_dma(buf_in, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE, &dma_in); EXPECT_GE(rc, 1); rc = prepare_dma(buf_out, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE, &dma_out); EXPECT_GE(rc, 1); rc = finish_input_output_dma(buf_in, TEST_BUF_SIZE, buf_out, TEST_BUF_SIZE); EXPECT_EQ(NO_ERROR, rc); /* DMAs should already be finished, finish should return ERR_NOT_FOUND */ rc = finish_dma(buf_in, TEST_BUF_SIZE, DMA_FLAG_TO_DEVICE); EXPECT_EQ(ERR_NOT_FOUND, rc); rc = finish_dma(buf_out, TEST_BUF_SIZE, DMA_FLAG_FROM_DEVICE); EXPECT_EQ(ERR_NOT_FOUND, rc); } TEST_F(libc, FinishInputOutputSameDifferentBufs) { uint8_t buf_in[TEST_BUF_SIZE] = {0}; uint8_t* buf_out = buf_in; struct dma_pmem dma_in; int rc = prepare_dma(buf_in, TEST_BUF_SIZE, DMA_FLAG_BIDIRECTION, &dma_in); EXPECT_GE(rc, 1); rc = finish_input_output_dma(buf_in, TEST_BUF_SIZE, buf_out, TEST_BUF_SIZE); EXPECT_EQ(NO_ERROR, rc); /* DMAs should already be finished, finish should return ERR_NOT_FOUND */ rc = finish_dma(buf_in, TEST_BUF_SIZE, DMA_FLAG_BIDIRECTION); EXPECT_EQ(ERR_NOT_FOUND, rc); } #endif #if defined(TRUSTY_USERSPACE) PORT_TEST(libc, "com.android.libctest"); #else PORT_TEST(libc, "com.android.kernel.libctest"); #endif