diff options
author | 2020-08-10 21:03:40 +0000 | |
---|---|---|
committer | 2020-08-10 21:03:40 +0000 | |
commit | 3cab2bb3f667058bece8e38b12449a63a9d73c4b (patch) | |
tree | 732ebd68e507f798225d97f4f2dd7ff8468d3e17 /gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp | |
parent | Reduce log spam. (diff) | |
download | wireguard-openbsd-3cab2bb3f667058bece8e38b12449a63a9d73c4b.tar.xz wireguard-openbsd-3cab2bb3f667058bece8e38b12449a63a9d73c4b.zip |
Import compiler-rt 10.0.1 release.
ok kettenis@
Diffstat (limited to 'gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp')
-rw-r--r-- | gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp b/gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp new file mode 100644 index 00000000000..1d8e7e8af26 --- /dev/null +++ b/gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp @@ -0,0 +1,295 @@ +//===-- sanitizer_linux_test.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_linux.h +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_common/sanitizer_linux.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_file.h" +#include "gtest/gtest.h" + +#include <pthread.h> +#include <sched.h> +#include <stdlib.h> + +#include <algorithm> +#include <vector> + +namespace __sanitizer { + +struct TidReporterArgument { + TidReporterArgument() { + pthread_mutex_init(&terminate_thread_mutex, NULL); + pthread_mutex_init(&tid_reported_mutex, NULL); + pthread_cond_init(&terminate_thread_cond, NULL); + pthread_cond_init(&tid_reported_cond, NULL); + terminate_thread = false; + } + + ~TidReporterArgument() { + pthread_mutex_destroy(&terminate_thread_mutex); + pthread_mutex_destroy(&tid_reported_mutex); + pthread_cond_destroy(&terminate_thread_cond); + pthread_cond_destroy(&tid_reported_cond); + } + + tid_t reported_tid; + // For signaling to spawned threads that they should terminate. + pthread_cond_t terminate_thread_cond; + pthread_mutex_t terminate_thread_mutex; + bool terminate_thread; + // For signaling to main thread that a child thread has reported its tid. + pthread_cond_t tid_reported_cond; + pthread_mutex_t tid_reported_mutex; + + private: + // Disallow evil constructors + TidReporterArgument(const TidReporterArgument &); + void operator=(const TidReporterArgument &); +}; + +class ThreadListerTest : public ::testing::Test { + protected: + virtual void SetUp() { + pthread_t pthread_id; + tid_t tid; + for (uptr i = 0; i < kThreadCount; i++) { + SpawnTidReporter(&pthread_id, &tid); + pthread_ids_.push_back(pthread_id); + tids_.push_back(tid); + } + } + + virtual void TearDown() { + pthread_mutex_lock(&thread_arg.terminate_thread_mutex); + thread_arg.terminate_thread = true; + pthread_cond_broadcast(&thread_arg.terminate_thread_cond); + pthread_mutex_unlock(&thread_arg.terminate_thread_mutex); + for (uptr i = 0; i < pthread_ids_.size(); i++) + pthread_join(pthread_ids_[i], NULL); + } + + void SpawnTidReporter(pthread_t *pthread_id, tid_t *tid); + + static const uptr kThreadCount = 20; + + std::vector<pthread_t> pthread_ids_; + std::vector<tid_t> tids_; + + TidReporterArgument thread_arg; +}; + +// Writes its TID once to reported_tid and waits until signaled to terminate. +void *TidReporterThread(void *argument) { + TidReporterArgument *arg = reinterpret_cast<TidReporterArgument *>(argument); + pthread_mutex_lock(&arg->tid_reported_mutex); + arg->reported_tid = GetTid(); + pthread_cond_broadcast(&arg->tid_reported_cond); + pthread_mutex_unlock(&arg->tid_reported_mutex); + + pthread_mutex_lock(&arg->terminate_thread_mutex); + while (!arg->terminate_thread) + pthread_cond_wait(&arg->terminate_thread_cond, + &arg->terminate_thread_mutex); + pthread_mutex_unlock(&arg->terminate_thread_mutex); + return NULL; +} + +void ThreadListerTest::SpawnTidReporter(pthread_t *pthread_id, tid_t *tid) { + pthread_mutex_lock(&thread_arg.tid_reported_mutex); + thread_arg.reported_tid = -1; + ASSERT_EQ(0, pthread_create(pthread_id, NULL, + TidReporterThread, + &thread_arg)); + while (thread_arg.reported_tid == (tid_t)(-1)) + pthread_cond_wait(&thread_arg.tid_reported_cond, + &thread_arg.tid_reported_mutex); + pthread_mutex_unlock(&thread_arg.tid_reported_mutex); + *tid = thread_arg.reported_tid; +} + +static std::vector<tid_t> ReadTidsToVector(ThreadLister *thread_lister) { + std::vector<tid_t> listed_tids; + InternalMmapVector<tid_t> threads(128); + EXPECT_TRUE(thread_lister->ListThreads(&threads)); + return std::vector<tid_t>(threads.begin(), threads.end()); +} + +static bool Includes(std::vector<tid_t> first, std::vector<tid_t> second) { + std::sort(first.begin(), first.end()); + std::sort(second.begin(), second.end()); + return std::includes(first.begin(), first.end(), + second.begin(), second.end()); +} + +static bool HasElement(const std::vector<tid_t> &vector, tid_t element) { + return std::find(vector.begin(), vector.end(), element) != vector.end(); +} + +// ThreadLister's output should include the current thread's TID and the TID of +// every thread we spawned. +TEST_F(ThreadListerTest, ThreadListerSeesAllSpawnedThreads) { + tid_t self_tid = GetTid(); + ThreadLister thread_lister(getpid()); + std::vector<tid_t> listed_tids = ReadTidsToVector(&thread_lister); + ASSERT_TRUE(HasElement(listed_tids, self_tid)); + ASSERT_TRUE(Includes(listed_tids, tids_)); +} + +TEST_F(ThreadListerTest, DoNotForgetThreads) { + ThreadLister thread_lister(getpid()); + + // Run the loop body twice, because ThreadLister might behave differently if + // called on a freshly created object. + for (uptr i = 0; i < 2; i++) { + std::vector<tid_t> listed_tids = ReadTidsToVector(&thread_lister); + ASSERT_TRUE(Includes(listed_tids, tids_)); + } +} + +// If new threads have spawned during ThreadLister object's lifetime, calling +// relisting should cause ThreadLister to recognize their existence. +TEST_F(ThreadListerTest, NewThreads) { + ThreadLister thread_lister(getpid()); + std::vector<tid_t> threads_before_extra = ReadTidsToVector(&thread_lister); + + pthread_t extra_pthread_id; + tid_t extra_tid; + SpawnTidReporter(&extra_pthread_id, &extra_tid); + // Register the new thread so it gets terminated in TearDown(). + pthread_ids_.push_back(extra_pthread_id); + + // It would be very bizarre if the new TID had been listed before we even + // spawned that thread, but it would also cause a false success in this test, + // so better check for that. + ASSERT_FALSE(HasElement(threads_before_extra, extra_tid)); + + std::vector<tid_t> threads_after_extra = ReadTidsToVector(&thread_lister); + ASSERT_TRUE(HasElement(threads_after_extra, extra_tid)); +} + +TEST(SanitizerCommon, SetEnvTest) { + const char kEnvName[] = "ENV_FOO"; + SetEnv(kEnvName, "value"); + EXPECT_STREQ("value", getenv(kEnvName)); + unsetenv(kEnvName); + EXPECT_EQ(0, getenv(kEnvName)); +} + +#if (defined(__x86_64__) || defined(__i386__)) && !SANITIZER_ANDROID +void *thread_self_offset_test_func(void *arg) { + bool result = + *(uptr *)((char *)ThreadSelf() + ThreadSelfOffset()) == ThreadSelf(); + return (void *)result; +} + +TEST(SanitizerLinux, ThreadSelfOffset) { + EXPECT_TRUE((bool)thread_self_offset_test_func(0)); + pthread_t tid; + void *result; + ASSERT_EQ(0, pthread_create(&tid, 0, thread_self_offset_test_func, 0)); + ASSERT_EQ(0, pthread_join(tid, &result)); + EXPECT_TRUE((bool)result); +} + +// libpthread puts the thread descriptor at the end of stack space. +void *thread_descriptor_size_test_func(void *arg) { + uptr descr_addr = ThreadSelf(); + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + void *stackaddr; + size_t stacksize; + pthread_attr_getstack(&attr, &stackaddr, &stacksize); + return (void *)((uptr)stackaddr + stacksize - descr_addr); +} + +TEST(SanitizerLinux, ThreadDescriptorSize) { + pthread_t tid; + void *result; + ASSERT_EQ(0, pthread_create(&tid, 0, thread_descriptor_size_test_func, 0)); + ASSERT_EQ(0, pthread_join(tid, &result)); + EXPECT_EQ((uptr)result, ThreadDescriptorSize()); +} +#endif + +TEST(SanitizerCommon, LibraryNameIs) { + EXPECT_FALSE(LibraryNameIs("", "")); + + char full_name[256]; + const char *paths[] = { "", "/", "/path/to/" }; + const char *suffixes[] = { "", "-linux", ".1.2", "-linux.1.2" }; + const char *base_names[] = { "lib", "lib.0", "lib-i386" }; + const char *wrong_names[] = { "", "lib.9", "lib-x86_64" }; + for (uptr i = 0; i < ARRAY_SIZE(paths); i++) + for (uptr j = 0; j < ARRAY_SIZE(suffixes); j++) { + for (uptr k = 0; k < ARRAY_SIZE(base_names); k++) { + internal_snprintf(full_name, ARRAY_SIZE(full_name), "%s%s%s.so", + paths[i], base_names[k], suffixes[j]); + EXPECT_TRUE(LibraryNameIs(full_name, base_names[k])) + << "Full name " << full_name + << " doesn't match base name " << base_names[k]; + for (uptr m = 0; m < ARRAY_SIZE(wrong_names); m++) + EXPECT_FALSE(LibraryNameIs(full_name, wrong_names[m])) + << "Full name " << full_name + << " matches base name " << wrong_names[m]; + } + } +} + +#if defined(__mips64) +// Effectively, this is a test for ThreadDescriptorSize() which is used to +// compute ThreadSelf(). +TEST(SanitizerLinux, ThreadSelfTest) { + ASSERT_EQ(pthread_self(), ThreadSelf()); +} +#endif + +TEST(SanitizerCommon, StartSubprocessTest) { + int pipe_fds[2]; + ASSERT_EQ(0, pipe(pipe_fds)); +#if SANITIZER_ANDROID + const char *shell = "/system/bin/sh"; +#else + const char *shell = "/bin/sh"; +#endif + const char *argv[] = {shell, "-c", "echo -n 'hello'", (char *)NULL}; + int pid = StartSubprocess(shell, argv, + /* stdin */ kInvalidFd, /* stdout */ pipe_fds[1]); + ASSERT_GT(pid, 0); + + // wait for process to finish. + while (IsProcessRunning(pid)) { + } + ASSERT_FALSE(IsProcessRunning(pid)); + + char buffer[256]; + { + char *ptr = buffer; + uptr bytes_read; + while (ReadFromFile(pipe_fds[0], ptr, 256, &bytes_read)) { + if (!bytes_read) { + break; + } + ptr += bytes_read; + } + ASSERT_EQ(5, ptr - buffer); + *ptr = 0; + } + ASSERT_EQ(0, strcmp(buffer, "hello")) << "Buffer: " << buffer; + internal_close(pipe_fds[0]); +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX |