• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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