• 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 <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