forked from OSchip/llvm-project
242 lines
7.4 KiB
C++
242 lines
7.4 KiB
C++
//===-- 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
|