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 <sanitizer/tsan_interface.h>
32 #include "napi/native_api.h"
33
34 #ifdef __APPLE__
35 #include <mach/mach_time.h>
36 #endif
37
38 #if TSAN_VECTORIZE
39 #include <emmintrin.h>
40 #include <smmintrin.h>
41 #endif
42
43 // TSan-invisible barrier.
44 // Tests use it to establish necessary execution order in a way that does not
45 // interfere with tsan (does not establish synchronization between threads).
46 typedef unsigned invisible_barrier_t;
47
48 #ifdef __cplusplus
49 extern "C" {
50 #endif
51 void __tsan_testonly_barrier_init(invisible_barrier_t *barrier,
52 unsigned count);
53 void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier);
54 unsigned long __tsan_testonly_shadow_stack_current_size();
55 #ifdef __cplusplus
56 }
57 #endif
58
BarrierInit(invisible_barrier_t * barrier,unsigned count)59 static inline void BarrierInit(invisible_barrier_t *barrier, unsigned count)
60 {
61 __tsan_testonly_barrier_init(barrier, count);
62 }
63
BarrierWait(invisible_barrier_t * barrier)64 static inline void BarrierWait(invisible_barrier_t *barrier)
65 {
66 __tsan_testonly_barrier_wait(barrier);
67 }
68
69 // Default instance of the barrier, but a test can declare more manually.
70 invisible_barrier_t g_barrier;
71
72 const unsigned COUNTNUM = 2;
73 constexpr int MAX_BUFFER_SIZE = 128;
74 constexpr const char *TSAN_LOG_FILE_PATH = "/data/storage/el2/log/tsanXtsLog.appspawn";
75
GetBuffer(int pid)76 static std::string GetBuffer(int pid)
77 {
78 std::string buffer;
79 char file[MAX_BUFFER_SIZE];
80 int filePathRes = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s.%d", TSAN_LOG_FILE_PATH, pid);
81 if (filePathRes < 0) {
82 return buffer;
83 }
84 FILE *fp = fopen(file, "r+");
85 if (!fp) {
86 return buffer;
87 }
88 if (fseek(fp, 0, SEEK_END) == -1) {
89 return buffer;
90 }
91 int size = ftell(fp);
92 if (size <= 0) {
93 ftruncate(fileno(fp), 0);
94 rewind(fp);
95 fclose(fp);
96 return buffer;
97 }
98 buffer.resize(size);
99 if (fseek(fp, 0, SEEK_SET) == -1) {
100 ftruncate(fileno(fp), 0);
101 rewind(fp);
102 fclose(fp);
103 return buffer;
104 }
105 int rsize = fread(&buffer[0], 1, size, fp);
106 if (rsize == 0) {
107 ftruncate(fileno(fp), 0);
108 rewind(fp);
109 fclose(fp);
110 return buffer;
111 }
112 ftruncate(fileno(fp), 0);
113 rewind(fp);
114 fclose(fp);
115 return buffer;
116 }
117
CheckTsanLog(const std::string & errType,const std::string & buffer)118 static bool CheckTsanLog(const std::string& errType, const std::string& buffer)
119 {
120 if (buffer.empty()) {
121 return false;
122 }
123 bool checkEventTypeFail = buffer.find(errType.c_str()) == std::string::npos;
124 if (checkEventTypeFail) {
125 return false;
126 }
127 return true;
128 }
129
130 namespace DataRace {
131 const int NUMFORTYFOUR = 44;
132 const int NUMFORTYTWO = 42;
133 const int NUMNINETYNINE = 99;
134 const int NUMSIXTEEN = 16;
Thread1(void * p)135 void *Thread1(void *p)
136 {
137 *(int*)p = NUMFORTYTWO;
138 return nullptr;
139 }
140
Thread2(void * p)141 void *Thread2(void *p)
142 {
143 *(int*)p = NUMFORTYFOUR;
144 return nullptr;
145 }
146
Alloc()147 __attribute__((noinline)) void *Alloc()
148 {
149 return malloc(NUMNINETYNINE);
150 }
151
AllocThread(void * arg)152 void *AllocThread(void* arg)
153 {
154 return Alloc();
155 }
156
DataRace(napi_env env,napi_callback_info info)157 __attribute__((optnone)) static napi_value DataRace(napi_env env, napi_callback_info info)
158 {
159 void *p = nullptr;
160 pthread_t t[2];
161 pthread_create(&t[0], nullptr, AllocThread, nullptr);
162 pthread_join(t[0], &p);
163 pthread_create(&t[0], nullptr, Thread1, (char*)p + NUMSIXTEEN);
164 pthread_create(&t[1], nullptr, Thread2, (char*)p + NUMSIXTEEN);
165 pthread_join(t[0], nullptr);
166 pthread_join(t[1], nullptr);
167 std::string bufferLog = GetBuffer(getpid());
168 bool findTsanLog = CheckTsanLog("ThreadSanitizer: data race", bufferLog) &&
169 CheckTsanLog("Write of size 4", bufferLog) &&
170 CheckTsanLog("created by main thread", bufferLog);
171 int checkRes = findTsanLog ? 1 : 0;
172 napi_value result = nullptr;
173 napi_create_int32(env, checkRes, &result);
174 return result;
175 }
176 }
177
178 namespace DataRaceOnVptr {
179 struct A {
ADataRaceOnVptr::A180 A()
181 {
182 sem_init(&sem, 0, 0);
183 }
FDataRaceOnVptr::A184 virtual void F() {}
DoneDataRaceOnVptr::A185 void Done()
186 {
187 sem_post(&sem);
188 }
~ADataRaceOnVptr::A189 virtual ~A()
190 {
191 sem_wait(&sem);
192 sem_destroy(&sem);
193 }
194 sem_t sem;
195 };
196
197 struct B : A {
FDataRaceOnVptr::B198 void F() override {}
199 };
200
201 static A *g_obj = new B;
202
Thread1(void * x)203 void *Thread1(void *x)
204 {
205 g_obj->F();
206 BarrierWait(&g_barrier);
207 g_obj->Done();
208 return nullptr;
209 }
210
Thread2(void * x)211 void *Thread2(void *x)
212 {
213 BarrierWait(&g_barrier);
214 delete g_obj;
215 g_obj = nullptr;
216 return nullptr;
217 }
218
DataRaceOnVptr(napi_env env,napi_callback_info info)219 __attribute__((optnone)) static napi_value DataRaceOnVptr(napi_env env, napi_callback_info info)
220 {
221 BarrierInit(&g_barrier, COUNTNUM);
222 pthread_t t[2];
223 pthread_create(&t[0], nullptr, Thread1, nullptr);
224 pthread_create(&t[1], nullptr, Thread2, nullptr);
225 pthread_join(t[0], nullptr);
226 pthread_join(t[1], nullptr);
227 std::string bufferLog = GetBuffer(getpid());
228 bool findTsanLog = CheckTsanLog("ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call)", bufferLog) &&
229 CheckTsanLog("Write of size 8", bufferLog) &&
230 CheckTsanLog("allocated by main thread", bufferLog);
231 int checkRes = findTsanLog ? 1 : 0;
232 napi_value result = nullptr;
233 napi_create_int32(env, checkRes, &result);
234 return result;
235 }
236 }
237
238 namespace HeapUseAfterFree {
239 const int NUMFORTYTWO = 42;
240 const int NUMHUNDRED = 100;
241 int *g_mem;
242 pthread_mutex_t g_mtx;
243
Thread1(void * x)244 void *Thread1(void *x)
245 {
246 pthread_mutex_lock(&g_mtx);
247 free(g_mem);
248 pthread_mutex_unlock(&g_mtx);
249 BarrierWait(&g_barrier);
250 return nullptr;
251 }
252
Thread2(void * x)253 __attribute__((noinline)) void *Thread2(void *x)
254 {
255 BarrierWait(&g_barrier);
256 pthread_mutex_lock(&g_mtx);
257 g_mem[0] = NUMFORTYTWO;
258 pthread_mutex_unlock(&g_mtx);
259 return nullptr;
260 }
261
HeapUseAfterFree(napi_env env,napi_callback_info info)262 __attribute__((optnone)) static napi_value HeapUseAfterFree(napi_env env, napi_callback_info info)
263 {
264 BarrierInit(&g_barrier, COUNTNUM);
265 g_mem = (int*)malloc(NUMHUNDRED);
266 pthread_mutex_init(&g_mtx, nullptr);
267 pthread_t t;
268 pthread_create(&t, nullptr, Thread1, nullptr);
269 Thread2(nullptr);
270 pthread_join(t, nullptr);
271 pthread_mutex_destroy(&g_mtx);
272 std::string bufferLog = GetBuffer(getpid());
273 bool findTsanLog = CheckTsanLog("ThreadSanitizer: heap-use-after-free", bufferLog) &&
274 CheckTsanLog("Write of size 4", bufferLog) &&
275 CheckTsanLog("created by main thread", bufferLog);
276 int checkRes = findTsanLog ? 1 : 0;
277 napi_value result = nullptr;
278 napi_create_int32(env, checkRes, &result);
279 return result;
280 }
281 }
282
283 namespace VirtualCallVSFree {
284 struct A {
FVirtualCallVSFree::A285 virtual void F() {}
~AVirtualCallVSFree::A286 virtual ~A() {}
287 };
288
289 struct B : A {
FVirtualCallVSFree::B290 void F() override {}
291 };
292
Thread(void * x)293 void *Thread(void *x)
294 {
295 BarrierWait(&g_barrier);
296 ((A*)x)->F();
297 return nullptr;
298 }
299
VirtualCallVSFree(napi_env env,napi_callback_info info)300 __attribute__((optnone)) static napi_value VirtualCallVSFree(napi_env env, napi_callback_info info)
301 {
302 BarrierInit(&g_barrier, COUNTNUM);
303 A *obj = new B;
304 pthread_t t;
305 pthread_create(&t, nullptr, Thread, obj);
306 delete obj;
307 BarrierWait(&g_barrier);
308 pthread_join(t, nullptr);
309 std::string bufferLog = GetBuffer(getpid());
310 bool findTsanLog = CheckTsanLog("ThreadSanitizer: heap-use-after-free (virtual call vs free)", bufferLog) &&
311 CheckTsanLog("Read of size 8", bufferLog) &&
312 CheckTsanLog("created by main thread", bufferLog);
313 int checkRes = findTsanLog ? 1 : 0;
314 napi_value result = nullptr;
315 napi_create_int32(env, checkRes, &result);
316 return result;
317 }
318 }
319
320 namespace SignalHandlerError {
321 pthread_t g_mainth;
322 volatile int g_done;
323
MyHandler(int,siginfo_t * s,void * c)324 static void MyHandler(int, siginfo_t *s, void *c)
325 {
326 errno = 1;
327 g_done = 1;
328 }
329
Sendsignal(void * p)330 static void* Sendsignal(void *p)
331 {
332 BarrierWait(&g_barrier);
333 pthread_kill(g_mainth, SIGPROF);
334 return nullptr;
335 }
336
Loop()337 static __attribute__((noinline)) void Loop()
338 {
339 BarrierWait(&g_barrier);
340 while (g_done == 0) {
341 volatile char *p = (char*)malloc(1);
342 if (p != nullptr) {
343 p[0] = 0;
344 }
345 free(static_cast<void *>(const_cast<char *>(p)));
346 sched_yield();
347 }
348 }
349
SignalHandlerError(napi_env env,napi_callback_info info)350 __attribute__((optnone)) static napi_value SignalHandlerError(napi_env env, napi_callback_info info)
351 {
352 BarrierInit(&g_barrier, COUNTNUM);
353 g_mainth = pthread_self();
354 struct sigaction act = {};
355 act.sa_sigaction = &MyHandler;
356 sigaction(SIGPROF, &act, nullptr);
357 pthread_t th;
358 pthread_create(&th, nullptr, Sendsignal, nullptr);
359 Loop();
360 pthread_join(th, nullptr);
361 std::string bufferLog = GetBuffer(getpid());
362 bool findTsanLog = CheckTsanLog("ThreadSanitizer: signal handler spoils errno", bufferLog) &&
363 CheckTsanLog("Signal 27 handler invoked", bufferLog);
364 int checkRes = findTsanLog ? 1 : 0;
365 napi_value result = nullptr;
366 napi_create_int32(env, checkRes, &result);
367 return result;
368 }
369 }
370
371 namespace SignalHandlerUnsafe {
Handler(int,siginfo_t *,void *)372 static void Handler(int, siginfo_t*, void*)
373 {
374 volatile char *p = (char*)malloc(1);
375 if (p != nullptr) {
376 p[0] = 0;
377 }
378 free(static_cast<void *>(const_cast<char *>(p)));
379 }
380
SignalHandlerUnsafe(napi_env env,napi_callback_info info)381 __attribute__((optnone)) static napi_value SignalHandlerUnsafe(napi_env env, napi_callback_info info)
382 {
383 struct sigaction act = {};
384 act.sa_sigaction = &Handler;
385 sigaction(SIGPROF, &act, nullptr);
386 kill(getpid(), SIGPROF);
387 sleep(1); // let the signal handler run
388 std::string bufferLog = GetBuffer(getpid());
389 bool findTsanLog = CheckTsanLog("ThreadSanitizer: signal-unsafe call inside of a signal", bufferLog);
390 int checkRes = findTsanLog ? 1 : 0;
391 napi_value result = nullptr;
392 napi_create_int32(env, checkRes, &result);
393 return result;
394 }
395 }
396
397 EXTERN_C_START
Init(napi_env env,napi_value exports)398 static napi_value Init(napi_env env, napi_value exports)
399 {
400 napi_property_descriptor desc[] = {
401 { "dataRace", nullptr, DataRace::DataRace, nullptr, nullptr, nullptr, napi_default, nullptr },
402 { "dataRaceOnVptr", nullptr, DataRaceOnVptr::DataRaceOnVptr, nullptr, nullptr, nullptr,
403 napi_default, nullptr },
404 { "heapUseAfterFree", nullptr, HeapUseAfterFree::HeapUseAfterFree, nullptr, nullptr, nullptr,
405 napi_default, nullptr },
406 { "virtualCallVSFree", nullptr, VirtualCallVSFree::VirtualCallVSFree, nullptr, nullptr, nullptr,
407 napi_default, nullptr },
408 { "signalHandlerUnsafe", nullptr, SignalHandlerUnsafe::SignalHandlerUnsafe, nullptr, nullptr, nullptr,
409 napi_default, nullptr },
410 { "signalHandlerError", nullptr, SignalHandlerError::SignalHandlerError, nullptr, nullptr, nullptr,
411 napi_default, nullptr }
412 };
413 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
414 return exports;
415 }
416 EXTERN_C_END
417
418 static napi_module demoModule = {
419 .nm_version = 1,
420 .nm_flags = 0,
421 .nm_filename = nullptr,
422 .nm_register_func = Init,
423 .nm_modname = "entry",
424 .nm_priv = ((void*)0),
425 .reserved = { 0 },
426 };
427
RegisterEntryModule(void)428 extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
429 {
430 napi_module_register(&demoModule);
431 }
432