//===-- sanitizer_libc_test.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Tests for sanitizer_libc.h. //===----------------------------------------------------------------------===// #include <algorithm> #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_platform.h" #include "gtest/gtest.h" #if SANITIZER_WINDOWS #define NOMINMAX #include <windows.h> #undef NOMINMAX #endif #if SANITIZER_POSIX # include <sys/stat.h> # include "sanitizer_common/sanitizer_posix.h" #endif // A regression test for internal_memmove() implementation. TEST(SanitizerCommon, InternalMemmoveRegression) { char src[] = "Hello World"; char *dest = src + 6; __sanitizer::internal_memmove(dest, src, 5); EXPECT_EQ(dest[0], src[0]); EXPECT_EQ(dest[4], src[4]); } TEST(SanitizerCommon, mem_is_zero) { size_t size = 128; char *x = new char[size]; memset(x, 0, size); for (size_t pos = 0; pos < size; pos++) { x[pos] = 1; for (size_t beg = 0; beg < size; beg++) { for (size_t end = beg; end < size; end++) { // fprintf(stderr, "pos %zd beg %zd end %zd \n", pos, beg, end); if (beg <= pos && pos < end) EXPECT_FALSE(__sanitizer::mem_is_zero(x + beg, end - beg)); else EXPECT_TRUE(__sanitizer::mem_is_zero(x + beg, end - beg)); } } x[pos] = 0; } delete [] x; } struct stat_and_more { struct stat st; unsigned char z; }; static void temp_file_name(char *buf, size_t bufsize, const char *prefix) { #if SANITIZER_WINDOWS buf[0] = '\0'; char tmp_dir[MAX_PATH]; if (!::GetTempPathA(MAX_PATH, tmp_dir)) return; // GetTempFileNameA needs a MAX_PATH buffer. char tmp_path[MAX_PATH]; if (!::GetTempFileNameA(tmp_dir, prefix, 0, tmp_path)) return; internal_strncpy(buf, tmp_path, bufsize); #else const char *tmpdir = "/tmp"; #if SANITIZER_ANDROID // I don't know a way to query temp directory location on Android without // going through Java interfaces. The code below is not ideal, but should // work. May require "adb root", but it is needed for almost any use of ASan // on Android already. tmpdir = GetEnv("EXTERNAL_STORAGE"); #endif u32 uid = GetUid(); internal_snprintf(buf, bufsize, "%s/%s%d", tmpdir, prefix, uid); #endif } TEST(SanitizerCommon, FileOps) { const char *str1 = "qwerty"; uptr len1 = internal_strlen(str1); const char *str2 = "zxcv"; uptr len2 = internal_strlen(str2); char tmpfile[128]; temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp."); fd_t fd = OpenFile(tmpfile, WrOnly); ASSERT_NE(fd, kInvalidFd); uptr bytes_written = 0; EXPECT_TRUE(WriteToFile(fd, str1, len1, &bytes_written)); EXPECT_EQ(len1, bytes_written); EXPECT_TRUE(WriteToFile(fd, str2, len2, &bytes_written)); EXPECT_EQ(len2, bytes_written); CloseFile(fd); EXPECT_TRUE(FileExists(tmpfile)); fd = OpenFile(tmpfile, RdOnly); ASSERT_NE(fd, kInvalidFd); #if SANITIZER_POSIX // The stat wrappers are posix-only. uptr fsize = internal_filesize(fd); EXPECT_EQ(len1 + len2, fsize); struct stat st1, st2, st3; EXPECT_EQ(0u, internal_stat(tmpfile, &st1)); EXPECT_EQ(0u, internal_lstat(tmpfile, &st2)); EXPECT_EQ(0u, internal_fstat(fd, &st3)); EXPECT_EQ(fsize, (uptr)st3.st_size); // Verify that internal_fstat does not write beyond the end of the supplied // buffer. struct stat_and_more sam; memset(&sam, 0xAB, sizeof(sam)); EXPECT_EQ(0u, internal_fstat(fd, &sam.st)); EXPECT_EQ(0xAB, sam.z); EXPECT_NE(0xAB, sam.st.st_size); EXPECT_NE(0, sam.st.st_size); #endif char buf[64] = {}; uptr bytes_read = 0; EXPECT_TRUE(ReadFromFile(fd, buf, len1, &bytes_read)); EXPECT_EQ(len1, bytes_read); EXPECT_EQ(0, internal_memcmp(buf, str1, len1)); EXPECT_EQ((char)0, buf[len1 + 1]); internal_memset(buf, 0, len1); EXPECT_TRUE(ReadFromFile(fd, buf, len2, &bytes_read)); EXPECT_EQ(len2, bytes_read); EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); CloseFile(fd); #if SANITIZER_WINDOWS // No sanitizer needs to delete a file on Windows yet. If we ever do, we can // add a portable wrapper and test it from here. ::DeleteFileA(&tmpfile[0]); #else internal_unlink(tmpfile); #endif } static const size_t kStrlcpyBufSize = 8; void test_internal_strlcpy(char *dbuf, const char *sbuf) { uptr retval = 0; retval = internal_strlcpy(dbuf, sbuf, kStrlcpyBufSize); EXPECT_EQ(internal_strncmp(dbuf, sbuf, kStrlcpyBufSize - 1), 0); EXPECT_EQ(internal_strlen(dbuf), std::min(internal_strlen(sbuf), (uptr)(kStrlcpyBufSize - 1))); EXPECT_EQ(retval, internal_strlen(sbuf)); // Test with shorter maxlen. uptr maxlen = 2; if (internal_strlen(sbuf) > maxlen) { retval = internal_strlcpy(dbuf, sbuf, maxlen); EXPECT_EQ(internal_strncmp(dbuf, sbuf, maxlen - 1), 0); EXPECT_EQ(internal_strlen(dbuf), maxlen - 1); } } TEST(SanitizerCommon, InternalStrFunctions) { const char *haystack = "haystack"; EXPECT_EQ(haystack + 2, internal_strchr(haystack, 'y')); EXPECT_EQ(haystack + 2, internal_strchrnul(haystack, 'y')); EXPECT_EQ(0, internal_strchr(haystack, 'z')); EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z')); char dbuf[kStrlcpyBufSize] = {}; const char *samesizestr = "1234567"; const char *shortstr = "123"; const char *longerstr = "123456789"; // Test internal_strlcpy. internal_strlcpy(dbuf, shortstr, 0); EXPECT_EQ(dbuf[0], 0); EXPECT_EQ(dbuf[0], 0); test_internal_strlcpy(dbuf, samesizestr); test_internal_strlcpy(dbuf, shortstr); test_internal_strlcpy(dbuf, longerstr); // Test internal_strlcat. char dcatbuf[kStrlcpyBufSize] = {}; uptr retval = 0; retval = internal_strlcat(dcatbuf, "aaa", 0); EXPECT_EQ(internal_strlen(dcatbuf), (uptr)0); EXPECT_EQ(retval, (uptr)3); retval = internal_strlcat(dcatbuf, "123", kStrlcpyBufSize); EXPECT_EQ(internal_strcmp(dcatbuf, "123"), 0); EXPECT_EQ(internal_strlen(dcatbuf), (uptr)3); EXPECT_EQ(retval, (uptr)3); retval = internal_strlcat(dcatbuf, "123", kStrlcpyBufSize); EXPECT_EQ(internal_strcmp(dcatbuf, "123123"), 0); EXPECT_EQ(internal_strlen(dcatbuf), (uptr)6); EXPECT_EQ(retval, (uptr)6); retval = internal_strlcat(dcatbuf, "123", kStrlcpyBufSize); EXPECT_EQ(internal_strcmp(dcatbuf, "1231231"), 0); EXPECT_EQ(internal_strlen(dcatbuf), (uptr)7); EXPECT_EQ(retval, (uptr)9); } // FIXME: File manipulations are not yet supported on Windows #if SANITIZER_POSIX && !SANITIZER_MAC TEST(SanitizerCommon, InternalMmapWithOffset) { char tmpfile[128]; temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.internalmmapwithoffset.tmp."); fd_t fd = OpenFile(tmpfile, RdWr); ASSERT_NE(fd, kInvalidFd); uptr page_size = GetPageSizeCached(); uptr res = internal_ftruncate(fd, page_size * 2); ASSERT_FALSE(internal_iserror(res)); res = internal_lseek(fd, page_size, SEEK_SET); ASSERT_FALSE(internal_iserror(res)); res = internal_write(fd, "AB", 2); ASSERT_FALSE(internal_iserror(res)); char *p = (char *)MapWritableFileToMemory(nullptr, page_size, fd, page_size); ASSERT_NE(nullptr, p); ASSERT_EQ('A', p[0]); ASSERT_EQ('B', p[1]); CloseFile(fd); UnmapOrDie(p, page_size); internal_unlink(tmpfile); } #endif