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