1//===-- sanitizer_linux_test.cpp ------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Tests for sanitizer_linux.h
10//
11//===----------------------------------------------------------------------===//
12
13#include "sanitizer_common/sanitizer_platform.h"
14#if SANITIZER_LINUX
15
16# include <pthread.h>
17# include <sched.h>
18# include <stdlib.h>
19
20# include <algorithm>
21# include <vector>
22
23# include "gtest/gtest.h"
24# include "sanitizer_common/sanitizer_common.h"
25# include "sanitizer_common/sanitizer_file.h"
26# include "sanitizer_common/sanitizer_linux.h"
27
28namespace __sanitizer {
29
30struct TidReporterArgument {
31 TidReporterArgument() {
32 pthread_mutex_init(mutex: &terminate_thread_mutex, NULL);
33 pthread_mutex_init(mutex: &tid_reported_mutex, NULL);
34 pthread_cond_init(cond: &terminate_thread_cond, NULL);
35 pthread_cond_init(cond: &tid_reported_cond, NULL);
36 terminate_thread = false;
37 }
38
39 ~TidReporterArgument() {
40 pthread_mutex_destroy(mutex: &terminate_thread_mutex);
41 pthread_mutex_destroy(mutex: &tid_reported_mutex);
42 pthread_cond_destroy(cond: &terminate_thread_cond);
43 pthread_cond_destroy(cond: &tid_reported_cond);
44 }
45
46 tid_t reported_tid;
47 // For signaling to spawned threads that they should terminate.
48 pthread_cond_t terminate_thread_cond;
49 pthread_mutex_t terminate_thread_mutex;
50 bool terminate_thread;
51 // For signaling to main thread that a child thread has reported its tid.
52 pthread_cond_t tid_reported_cond;
53 pthread_mutex_t tid_reported_mutex;
54
55 private:
56 // Disallow evil constructors
57 TidReporterArgument(const TidReporterArgument &);
58 void operator=(const TidReporterArgument &);
59};
60
61class ThreadListerTest : public ::testing::Test {
62 protected:
63 virtual void SetUp() {
64 pthread_t pthread_id;
65 tid_t tid;
66 for (uptr i = 0; i < kThreadCount; i++) {
67 SpawnTidReporter(pthread_id: &pthread_id, tid: &tid);
68 pthread_ids_.push_back(pthread_id);
69 tids_.push_back(tid);
70 }
71 }
72
73 virtual void TearDown() {
74 pthread_mutex_lock(mutex: &thread_arg.terminate_thread_mutex);
75 thread_arg.terminate_thread = true;
76 pthread_cond_broadcast(cond: &thread_arg.terminate_thread_cond);
77 pthread_mutex_unlock(mutex: &thread_arg.terminate_thread_mutex);
78 for (uptr i = 0; i < pthread_ids_.size(); i++)
79 pthread_join(pthread_ids_[i], NULL);
80 }
81
82 void SpawnTidReporter(pthread_t *pthread_id, tid_t *tid);
83
84 static const uptr kThreadCount = 20;
85
86 std::vector<pthread_t> pthread_ids_;
87 std::vector<tid_t> tids_;
88
89 TidReporterArgument thread_arg;
90};
91
92// Writes its TID once to reported_tid and waits until signaled to terminate.
93void *TidReporterThread(void *argument) {
94 TidReporterArgument *arg = reinterpret_cast<TidReporterArgument *>(argument);
95 pthread_mutex_lock(mutex: &arg->tid_reported_mutex);
96 arg->reported_tid = GetTid();
97 pthread_cond_broadcast(cond: &arg->tid_reported_cond);
98 pthread_mutex_unlock(mutex: &arg->tid_reported_mutex);
99
100 pthread_mutex_lock(mutex: &arg->terminate_thread_mutex);
101 while (!arg->terminate_thread)
102 pthread_cond_wait(cond: &arg->terminate_thread_cond,
103 mutex: &arg->terminate_thread_mutex);
104 pthread_mutex_unlock(mutex: &arg->terminate_thread_mutex);
105 return NULL;
106}
107
108void ThreadListerTest::SpawnTidReporter(pthread_t *pthread_id, tid_t *tid) {
109 pthread_mutex_lock(mutex: &thread_arg.tid_reported_mutex);
110 thread_arg.reported_tid = -1;
111 ASSERT_EQ(0,
112 pthread_create(newthread: pthread_id, NULL, start_routine: TidReporterThread, arg: &thread_arg));
113 while (thread_arg.reported_tid == (tid_t)(-1))
114 pthread_cond_wait(cond: &thread_arg.tid_reported_cond,
115 mutex: &thread_arg.tid_reported_mutex);
116 pthread_mutex_unlock(mutex: &thread_arg.tid_reported_mutex);
117 *tid = thread_arg.reported_tid;
118}
119
120static std::vector<tid_t> ReadTidsToVector(ThreadLister *thread_lister) {
121 std::vector<tid_t> listed_tids;
122 InternalMmapVector<tid_t> threads(128);
123 EXPECT_TRUE(thread_lister->ListThreads(&threads));
124 return std::vector<tid_t>(threads.begin(), threads.end());
125}
126
127static bool Includes(std::vector<tid_t> first, std::vector<tid_t> second) {
128 std::sort(first.begin(), first.end());
129 std::sort(second.begin(), second.end());
130 return std::includes(first.begin(), first.end(), second.begin(),
131 second.end());
132}
133
134static bool HasElement(const std::vector<tid_t> &vector, tid_t element) {
135 return std::find(vector.begin(), vector.end(), element) != vector.end();
136}
137
138// ThreadLister's output should include the current thread's TID and the TID of
139// every thread we spawned.
140TEST_F(ThreadListerTest, ThreadListerSeesAllSpawnedThreads) {
141 tid_t self_tid = GetTid();
142 ThreadLister thread_lister(getpid());
143 std::vector<tid_t> listed_tids = ReadTidsToVector(&thread_lister);
144 ASSERT_TRUE(HasElement(listed_tids, self_tid));
145 ASSERT_TRUE(Includes(listed_tids, tids_));
146
147 ASSERT_NE(nullptr, thread_lister.LoadStatus(tid: self_tid));
148 for (auto tid : tids_) ASSERT_NE(nullptr, thread_lister.LoadStatus(tid));
149}
150
151TEST_F(ThreadListerTest, DoNotForgetThreads) {
152 ThreadLister thread_lister(getpid());
153
154 // Run the loop body twice, because ThreadLister might behave differently if
155 // called on a freshly created object.
156 for (uptr i = 0; i < 2; i++) {
157 std::vector<tid_t> listed_tids = ReadTidsToVector(&thread_lister);
158 ASSERT_TRUE(Includes(listed_tids, tids_));
159 }
160}
161
162// If new threads have spawned during ThreadLister object's lifetime, calling
163// relisting should cause ThreadLister to recognize their existence.
164TEST_F(ThreadListerTest, NewThreads) {
165 ThreadLister thread_lister(getpid());
166 std::vector<tid_t> threads_before_extra = ReadTidsToVector(&thread_lister);
167
168 pthread_t extra_pthread_id;
169 tid_t extra_tid;
170 SpawnTidReporter(&extra_pthread_id, &extra_tid);
171 // Register the new thread so it gets terminated in TearDown().
172 pthread_ids_.push_back(extra_pthread_id);
173
174 // It would be very bizarre if the new TID had been listed before we even
175 // spawned that thread, but it would also cause a false success in this test,
176 // so better check for that.
177 ASSERT_FALSE(HasElement(threads_before_extra, extra_tid));
178
179 std::vector<tid_t> threads_after_extra = ReadTidsToVector(&thread_lister);
180 ASSERT_TRUE(HasElement(threads_after_extra, extra_tid));
181}
182
183TEST(SanitizerCommon, SetEnvTest) {
184 const char kEnvName[] = "ENV_FOO";
185 SetEnv(name: kEnvName, value: "value");
186 EXPECT_STREQ("value", getenv(name: kEnvName));
187 unsetenv(name: kEnvName);
188 EXPECT_EQ(0, getenv(name: kEnvName));
189}
190
191# if (defined(__x86_64__) || defined(__i386__)) && !SANITIZER_ANDROID
192// libpthread puts the thread descriptor at the end of stack space.
193void *thread_descriptor_size_test_func(void *arg) {
194 uptr descr_addr = (uptr)pthread_self();
195 pthread_attr_t attr;
196 pthread_getattr_np(th: pthread_self(), attr: &attr);
197 void *stackaddr;
198 size_t stacksize;
199 pthread_attr_getstack(attr: &attr, stackaddr: &stackaddr, stacksize: &stacksize);
200 return (void *)((uptr)stackaddr + stacksize - descr_addr);
201}
202
203TEST(SanitizerLinux, ThreadDescriptorSize) {
204 pthread_t tid;
205 void *result;
206 ASSERT_EQ(0, pthread_create(&tid, 0, thread_descriptor_size_test_func, 0));
207 ASSERT_EQ(0, pthread_join(tid, &result));
208 InitTlsSize();
209 EXPECT_EQ((uptr)result, ThreadDescriptorSize());
210}
211# endif
212
213TEST(SanitizerCommon, LibraryNameIs) {
214 EXPECT_FALSE(LibraryNameIs("", ""));
215
216 char full_name[256];
217 const char *paths[] = {"", "/", "/path/to/"};
218 const char *suffixes[] = {"", "-linux", ".1.2", "-linux.1.2"};
219 const char *base_names[] = {"lib", "lib.0", "lib-i386"};
220 const char *wrong_names[] = {"", "lib.9", "lib-x86_64"};
221 for (uptr i = 0; i < ARRAY_SIZE(paths); i++)
222 for (uptr j = 0; j < ARRAY_SIZE(suffixes); j++) {
223 for (uptr k = 0; k < ARRAY_SIZE(base_names); k++) {
224 internal_snprintf(full_name, ARRAY_SIZE(full_name), "%s%s%s.so",
225 paths[i], base_names[k], suffixes[j]);
226 EXPECT_TRUE(LibraryNameIs(full_name, base_names[k]))
227 << "Full name " << full_name << " doesn't match base name "
228 << base_names[k];
229 for (uptr m = 0; m < ARRAY_SIZE(wrong_names); m++)
230 EXPECT_FALSE(LibraryNameIs(full_name, wrong_names[m]))
231 << "Full name " << full_name << " matches base name "
232 << wrong_names[m];
233 }
234 }
235}
236
237# if defined(__mips64)
238// Effectively, this is a test for ThreadDescriptorSize() which is used to
239// compute ThreadSelf().
240TEST(SanitizerLinux, ThreadSelfTest) {
241 ASSERT_EQ(pthread_self(), ThreadSelf());
242}
243# endif
244
245TEST(SanitizerCommon, StartSubprocessTest) {
246 int pipe_fds[2];
247 ASSERT_EQ(0, pipe(pipe_fds));
248# if SANITIZER_ANDROID
249 const char *shell = "/system/bin/sh";
250# else
251 const char *shell = "/bin/sh";
252# endif
253 const char *argv[] = {shell, "-c", "echo -n 'hello'", (char *)NULL};
254 int pid = StartSubprocess(shell, argv, GetEnviron(),
255 /* stdin */ kInvalidFd, /* stdout */ pipe_fds[1]);
256 ASSERT_GT(pid, 0);
257
258 // wait for process to finish.
259 while (IsProcessRunning(pid)) {
260 }
261 ASSERT_FALSE(IsProcessRunning(pid));
262
263 char buffer[256];
264 {
265 char *ptr = buffer;
266 uptr bytes_read;
267 while (ReadFromFile(pipe_fds[0], ptr, 256, &bytes_read)) {
268 if (!bytes_read) {
269 break;
270 }
271 ptr += bytes_read;
272 }
273 ASSERT_EQ(5, ptr - buffer);
274 *ptr = 0;
275 }
276 ASSERT_EQ(0, strcmp(buffer, "hello")) << "Buffer: " << buffer;
277 internal_close(pipe_fds[0]);
278}
279
280} // namespace __sanitizer
281
282#endif // SANITIZER_LINUX
283

source code of compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cpp