1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <bits/alltypes.h>
17 #include <thread>
18 #include <unistd.h>
19 #include <string>
20 #include <sys/wait.h>
21 #include <csignal>
22 #include <securec.h>
23 #include <pthread.h>
24 #include <cstdlib>
25 #include <cstdio>
26 #include <semaphore.h>
27 #include <dlfcn.h>
28 #include <cstddef>
29 #include <sched.h>
30 #include <cstdarg>
31 #include <dirent.h>
32 #include <sanitizer/tsan_interface.h>
33 #include "napi/native_api.h"
34
35 #ifdef __APPLE__
36 #include <mach/mach_time.h>
37 #endif
38
39 #if TSAN_VECTORIZE
40 #include <emmintrin.h>
41 #include <smmintrin.h>
42 #endif
43
44 // TSan-invisible barrier.
45 // Tests use it to establish necessary execution order in a way that does not
46 // interfere with tsan (does not establish synchronization between threads).
47 typedef unsigned invisible_barrier_t;
48
49 #ifdef __cplusplus
50 extern "C" {
51 #endif
52 void __tsan_testonly_barrier_init(invisible_barrier_t *barrier,
53 unsigned count);
54 void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier);
55 unsigned long __tsan_testonly_shadow_stack_current_size();
56 #ifdef __cplusplus
57 }
58 #endif
59
BarrierInit(invisible_barrier_t * barrier,unsigned count)60 static inline void BarrierInit(invisible_barrier_t *barrier, unsigned count)
61 {
62 __tsan_testonly_barrier_init(barrier, count);
63 }
64
BarrierWait(invisible_barrier_t * barrier)65 static inline void BarrierWait(invisible_barrier_t *barrier)
66 {
67 __tsan_testonly_barrier_wait(barrier);
68 }
69
70 // Default instance of the barrier, but a test can declare more manually.
71 invisible_barrier_t g_barrier;
72
73 const unsigned COUNTNUM = 2;
74 constexpr int MAX_BUFFER_SIZE = 128;
75
FindDirAndCheck(DIR * dir,char * fileResult,int pid)76 static int FindDirAndCheck(DIR *dir, char *fileResult, int pid)
77 {
78 struct dirent *ptr;
79 char file[MAX_BUFFER_SIZE];
80 int filenameres = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s.%s.%d", "tsanXtsLog", "appspawn", pid);
81 if (filenameres < 0) {
82 return -1;
83 }
84 while((ptr = readdir(dir)) != NULL) {
85 if (strstr(ptr->d_name, file) != NULL) {
86 int findRes = snprintf_s(fileResult, MAX_BUFFER_SIZE, MAX_BUFFER_SIZE - 1, "%s/%s",
87 "/data/storage/el2/log/", ptr->d_name);
88 if (findRes < 0) {
89 return -1;
90 }
91 return 1;
92 }
93 }
94 return -1;
95 }
96
GetBuffer(int pid)97 static std::string GetBuffer(int pid)
98 {
99 std::string buffer;
100 char file[MAX_BUFFER_SIZE];
101 DIR *logdir = opendir("/data/storage/el2/log/");
102 int findRes = FindDirAndCheck(logdir, file, pid);
103 if (findRes < 0) {
104 return buffer;
105 }
106 FILE *fp = fopen(file, "r+");
107 if (!fp) {
108 return buffer;
109 }
110 if (fseek(fp, 0, SEEK_END) == -1) {
111 return buffer;
112 }
113 int size = ftell(fp);
114 if (size <= 0) {
115 ftruncate(fileno(fp), 0);
116 rewind(fp);
117 fclose(fp);
118 return buffer;
119 }
120 buffer.resize(size);
121 if (fseek(fp, 0, SEEK_SET) == -1) {
122 ftruncate(fileno(fp), 0);
123 rewind(fp);
124 fclose(fp);
125 return buffer;
126 }
127 int rsize = fread(&buffer[0], 1, size, fp);
128 if (rsize == 0) {
129 ftruncate(fileno(fp), 0);
130 rewind(fp);
131 fclose(fp);
132 return buffer;
133 }
134 ftruncate(fileno(fp), 0);
135 rewind(fp);
136 fclose(fp);
137 return buffer;
138 }
139
CheckTsanLog(const std::string & errType,const std::string & buffer)140 static bool CheckTsanLog(const std::string& errType, const std::string& buffer)
141 {
142 if (buffer.empty()) {
143 return false;
144 }
145 bool checkEventTypeFail = buffer.find(errType.c_str()) == std::string::npos;
146 if (checkEventTypeFail) {
147 return false;
148 }
149 return true;
150 }
151
152 namespace DataRace {
153 const int NUMFORTYFOUR = 44;
154 const int NUMFORTYTWO = 42;
155 const int NUMNINETYNINE = 99;
156 const int NUMSIXTEEN = 16;
Thread1(void * p)157 void *Thread1(void *p)
158 {
159 *(int*)p = NUMFORTYTWO;
160 return nullptr;
161 }
162
Thread2(void * p)163 void *Thread2(void *p)
164 {
165 *(int*)p = NUMFORTYFOUR;
166 return nullptr;
167 }
168
Alloc()169 __attribute__((noinline)) void *Alloc()
170 {
171 return malloc(NUMNINETYNINE);
172 }
173
AllocThread(void * arg)174 void *AllocThread(void* arg)
175 {
176 return Alloc();
177 }
178
DataRace(napi_env env,napi_callback_info info)179 __attribute__((optnone)) static napi_value DataRace(napi_env env, napi_callback_info info)
180 {
181 void *p = nullptr;
182 pthread_t t[2];
183 pthread_create(&t[0], nullptr, AllocThread, nullptr);
184 pthread_join(t[0], &p);
185 pthread_create(&t[0], nullptr, Thread1, (char*)p + NUMSIXTEEN);
186 pthread_create(&t[1], nullptr, Thread2, (char*)p + NUMSIXTEEN);
187 pthread_join(t[0], nullptr);
188 pthread_join(t[1], nullptr);
189 std::string bufferLog = GetBuffer(getpid());
190 bool findTsanLog = CheckTsanLog("ThreadSanitizer: data race", bufferLog) &&
191 CheckTsanLog("Write of size 4", bufferLog) &&
192 CheckTsanLog("created by main thread", bufferLog);
193 int checkRes = findTsanLog ? 1 : 0;
194 napi_value result = nullptr;
195 napi_create_int32(env, checkRes, &result);
196 return result;
197 }
198 }
199
200 namespace DataRaceOnVptr {
201 struct A {
ADataRaceOnVptr::A202 A()
203 {
204 sem_init(&sem, 0, 0);
205 }
FDataRaceOnVptr::A206 virtual void F() {}
DoneDataRaceOnVptr::A207 void Done()
208 {
209 sem_post(&sem);
210 }
~ADataRaceOnVptr::A211 virtual ~A()
212 {
213 sem_wait(&sem);
214 sem_destroy(&sem);
215 }
216 sem_t sem;
217 };
218
219 struct B : A {
FDataRaceOnVptr::B220 void F() override {}
221 };
222
223 static A *g_obj = new B;
224
Thread1(void * x)225 void *Thread1(void *x)
226 {
227 g_obj->F();
228 BarrierWait(&g_barrier);
229 g_obj->Done();
230 return nullptr;
231 }
232
Thread2(void * x)233 void *Thread2(void *x)
234 {
235 BarrierWait(&g_barrier);
236 delete g_obj;
237 g_obj = nullptr;
238 return nullptr;
239 }
240
DataRaceOnVptr(napi_env env,napi_callback_info info)241 __attribute__((optnone)) static napi_value DataRaceOnVptr(napi_env env, napi_callback_info info)
242 {
243 BarrierInit(&g_barrier, COUNTNUM);
244 pthread_t t[2];
245 pthread_create(&t[0], nullptr, Thread1, nullptr);
246 pthread_create(&t[1], nullptr, Thread2, nullptr);
247 pthread_join(t[0], nullptr);
248 pthread_join(t[1], nullptr);
249 std::string bufferLog = GetBuffer(getpid());
250 bool findTsanLog = CheckTsanLog("ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call)", bufferLog) &&
251 CheckTsanLog("Write of size 8", bufferLog) &&
252 CheckTsanLog("allocated by main thread", bufferLog);
253 int checkRes = findTsanLog ? 1 : 0;
254 napi_value result = nullptr;
255 napi_create_int32(env, checkRes, &result);
256 return result;
257 }
258 }
259
260 namespace HeapUseAfterFree {
261 const int NUMFORTYTWO = 42;
262 const int NUMHUNDRED = 100;
263 int *g_mem;
264 pthread_mutex_t g_mtx;
265
Thread1(void * x)266 void *Thread1(void *x)
267 {
268 pthread_mutex_lock(&g_mtx);
269 free(g_mem);
270 g_mem = nullptr;
271 pthread_mutex_unlock(&g_mtx);
272 BarrierWait(&g_barrier);
273 return nullptr;
274 }
275
Thread2(void * x)276 __attribute__((noinline)) void *Thread2(void *x)
277 {
278 BarrierWait(&g_barrier);
279 pthread_mutex_lock(&g_mtx);
280 g_mem[0] = NUMFORTYTWO;
281 pthread_mutex_unlock(&g_mtx);
282 return nullptr;
283 }
284
HeapUseAfterFree(napi_env env,napi_callback_info info)285 __attribute__((optnone)) static napi_value HeapUseAfterFree(napi_env env, napi_callback_info info)
286 {
287 BarrierInit(&g_barrier, COUNTNUM);
288 g_mem = (int*)malloc(NUMHUNDRED);
289 pthread_mutex_init(&g_mtx, nullptr);
290 pthread_t t;
291 pthread_create(&t, nullptr, Thread1, nullptr);
292 Thread2(nullptr);
293 pthread_join(t, nullptr);
294 pthread_mutex_destroy(&g_mtx);
295 std::string bufferLog = GetBuffer(getpid());
296 bool findTsanLog = CheckTsanLog("ThreadSanitizer: heap-use-after-free", bufferLog) &&
297 CheckTsanLog("Write of size 4", bufferLog) &&
298 CheckTsanLog("created by main thread", bufferLog);
299 int checkRes = findTsanLog ? 1 : 0;
300 napi_value result = nullptr;
301 napi_create_int32(env, checkRes, &result);
302 return result;
303 }
304 }
305
306 namespace VirtualCallVSFree {
307 struct A {
FVirtualCallVSFree::A308 virtual void F() {}
~AVirtualCallVSFree::A309 virtual ~A() {}
310 };
311
312 struct B : A {
FVirtualCallVSFree::B313 void F() override {}
314 };
315
Thread(void * x)316 void *Thread(void *x)
317 {
318 BarrierWait(&g_barrier);
319 ((A*)x)->F();
320 return nullptr;
321 }
322
VirtualCallVSFree(napi_env env,napi_callback_info info)323 __attribute__((optnone)) static napi_value VirtualCallVSFree(napi_env env, napi_callback_info info)
324 {
325 BarrierInit(&g_barrier, COUNTNUM);
326 A *obj = new B;
327 pthread_t t;
328 pthread_create(&t, nullptr, Thread, obj);
329 delete obj;
330 BarrierWait(&g_barrier);
331 pthread_join(t, nullptr);
332 std::string bufferLog = GetBuffer(getpid());
333 bool findTsanLog = CheckTsanLog("ThreadSanitizer: heap-use-after-free (virtual call vs free)", bufferLog) &&
334 CheckTsanLog("Read of size 8", bufferLog) &&
335 CheckTsanLog("created by main thread", bufferLog);
336 int checkRes = findTsanLog ? 1 : 0;
337 napi_value result = nullptr;
338 napi_create_int32(env, checkRes, &result);
339 return result;
340 }
341 }
342
343 namespace SignalHandlerError {
344 pthread_t g_mainth;
345 volatile int g_done;
346
MyHandler(int,siginfo_t * s,void * c)347 static void MyHandler(int, siginfo_t *s, void *c)
348 {
349 errno = 1;
350 g_done = 1;
351 }
352
Sendsignal(void * p)353 static void* Sendsignal(void *p)
354 {
355 BarrierWait(&g_barrier);
356 pthread_kill(g_mainth, SIGPROF);
357 return nullptr;
358 }
359
Loop()360 static __attribute__((noinline)) void Loop()
361 {
362 BarrierWait(&g_barrier);
363 while (g_done == 0) {
364 volatile char *p = (char*)malloc(1);
365 if (p != nullptr) {
366 p[0] = 0;
367 }
368 free(static_cast<void *>(const_cast<char *>(p)));
369 sched_yield();
370 }
371 }
372
SignalHandlerError(napi_env env,napi_callback_info info)373 __attribute__((optnone)) static napi_value SignalHandlerError(napi_env env, napi_callback_info info)
374 {
375 BarrierInit(&g_barrier, COUNTNUM);
376 g_mainth = pthread_self();
377 struct sigaction act = {};
378 act.sa_sigaction = &MyHandler;
379 sigaction(SIGPROF, &act, nullptr);
380 pthread_t th;
381 pthread_create(&th, nullptr, Sendsignal, nullptr);
382 Loop();
383 pthread_join(th, nullptr);
384 std::string bufferLog = GetBuffer(getpid());
385 bool findTsanLog = CheckTsanLog("ThreadSanitizer: signal handler spoils errno", bufferLog) &&
386 CheckTsanLog("Signal 27 handler invoked", bufferLog);
387 int checkRes = findTsanLog ? 1 : 0;
388 napi_value result = nullptr;
389 napi_create_int32(env, checkRes, &result);
390 return result;
391 }
392 }
393
394 namespace SignalHandlerUnsafe {
Handler(int,siginfo_t *,void *)395 static void Handler(int, siginfo_t*, void*)
396 {
397 volatile char *p = (char*)malloc(1);
398 if (p != nullptr) {
399 p[0] = 0;
400 }
401 free(static_cast<void *>(const_cast<char *>(p)));
402 }
403
SignalHandlerUnsafe(napi_env env,napi_callback_info info)404 __attribute__((optnone)) static napi_value SignalHandlerUnsafe(napi_env env, napi_callback_info info)
405 {
406 struct sigaction act = {};
407 act.sa_sigaction = &Handler;
408 sigaction(SIGPROF, &act, nullptr);
409 kill(getpid(), SIGPROF);
410 sleep(1); // let the signal handler run
411 std::string bufferLog = GetBuffer(getpid());
412 bool findTsanLog = CheckTsanLog("ThreadSanitizer: signal-unsafe call inside of a signal", bufferLog);
413 int checkRes = findTsanLog ? 1 : 0;
414 napi_value result = nullptr;
415 napi_create_int32(env, checkRes, &result);
416 return result;
417 }
418 }
419
420 EXTERN_C_START
Init(napi_env env,napi_value exports)421 static napi_value Init(napi_env env, napi_value exports)
422 {
423 napi_property_descriptor desc[] = {
424 { "dataRace", nullptr, DataRace::DataRace, nullptr, nullptr, nullptr, napi_default, nullptr },
425 { "dataRaceOnVptr", nullptr, DataRaceOnVptr::DataRaceOnVptr, nullptr, nullptr, nullptr,
426 napi_default, nullptr },
427 { "heapUseAfterFree", nullptr, HeapUseAfterFree::HeapUseAfterFree, nullptr, nullptr, nullptr,
428 napi_default, nullptr },
429 { "virtualCallVSFree", nullptr, VirtualCallVSFree::VirtualCallVSFree, nullptr, nullptr, nullptr,
430 napi_default, nullptr },
431 { "signalHandlerUnsafe", nullptr, SignalHandlerUnsafe::SignalHandlerUnsafe, nullptr, nullptr, nullptr,
432 napi_default, nullptr },
433 { "signalHandlerError", nullptr, SignalHandlerError::SignalHandlerError, nullptr, nullptr, nullptr,
434 napi_default, nullptr }
435 };
436 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
437 return exports;
438 }
439 EXTERN_C_END
440
441 static napi_module demoModule = {
442 .nm_version = 1,
443 .nm_flags = 0,
444 .nm_filename = nullptr,
445 .nm_register_func = Init,
446 .nm_modname = "entry",
447 .nm_priv = ((void*)0),
448 .reserved = { 0 },
449 };
450
RegisterEntryModule(void)451 extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
452 {
453 napi_module_register(&demoModule);
454 }
455