1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
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 "napi/native_api.h"
17 #include <cstddef>
18 #include <cstdlib>
19 #include <js_native_api.h>
20 #include <js_native_api_types.h>
21 #include <node_api.h>
22 #include <csignal>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 
SignalHandlerAbort(int signum)26 void SignalHandlerAbort(int signum)
27 {
28     kill(getpid(), SIGSTOP);
29 }
30 
31 // To detect reads from, or writes to, a misaligned pointer,
32 // or when you create a misaligned reference.
33 // A pointer misaligns if its address isn’t a multiple of its type’s alignment.
MisAlign(napi_env env, napi_callback_info info)34 static napi_value MisAlign(napi_env env, napi_callback_info info)
35 {
36     struct sigaction sigabrt = {
37         .sa_handler = SignalHandlerAbort,
38     };
39     sigaction(SIGABRT, &sigabrt, nullptr);
40 
41     int status;
42     int pid = fork();
43     int res = -1;
44     switch (pid) {
45         case -1: {
46             break;
47         }
48         case 0: {
49             // deliberately convert in C-style cast to trigger UBSan check
50             signed short int *buffer = (signed short int *)malloc(64);
51             signed long int *pointer = (signed long int *)(buffer + 1);
52             *pointer = 42; // 42 is an arbitrary number to deliberately trigger UBSan check
53             res = *pointer;
54             exit(0);
55         }
56         default: {
57             waitpid(pid, &status, WUNTRACED);
58             res = WSTOPSIG(status);
59             kill(pid, SIGCONT);
60             break;
61         }
62     }
63     napi_value result = nullptr;
64     napi_create_int32(env, res, &result);
65     return result;
66 }
67 
68 // To access indexes that exceed the array’s bounds.
Bounds(napi_env env, napi_callback_info info)69 static napi_value Bounds(napi_env env, napi_callback_info info)
70 {
71     struct sigaction sigabrt = {
72         .sa_handler = SignalHandlerAbort,
73     };
74     sigaction(SIGABRT, &sigabrt, nullptr);
75     size_t argc = 1;
76     napi_value args[1] = {nullptr};
77     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
78     int status;
79     int pid = fork();
80     int res = -1;
81     switch (pid) {
82         case -1: {
83             break;
84         }
85         case 0: {
86             int param;
87             napi_get_value_int32(env, args[0], &param);
88             int array[5] = {0};
89             res = array[param];
90             exit(0);
91         }
92         default: {
93             waitpid(pid, &status, WUNTRACED);
94             res = WSTOPSIG(status);
95             kill(pid, SIGCONT);
96             break;
97         }
98     }
99     napi_value result = nullptr;
100     napi_create_int32(env, res, &result);
101     return result;
102 }
103 
104 // To detect integer and float division where the divisor is zero.
IntegerDivBy0(napi_env env, napi_callback_info info)105 static napi_value IntegerDivBy0(napi_env env, napi_callback_info info)
106 {
107     struct sigaction sigabrt = {
108         .sa_handler = SignalHandlerAbort,
109     };
110     sigaction(SIGABRT, &sigabrt, nullptr);
111 
112     size_t argc = 1;
113     napi_value args[1] = {nullptr};
114     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
115     int status;
116     int pid = fork();
117     int res = -1;
118     switch (pid) {
119         case -1: {
120             break;
121         }
122         case 0: {
123             int param;
124             napi_get_value_int32(env, args[0], &param);
125             res = 2 / param; // deliberately assign divisor to 0 to trigger UBSan check
126             exit(0);
127         }
128         default: {
129             waitpid(pid, &status, WUNTRACED);
130             res = WSTOPSIG(status);
131             kill(pid, SIGCONT);
132             break;
133         }
134     }
135     napi_value result = nullptr;
136     napi_create_int32(env, res, &result);
137     return result;
138 }
139 
140 // To detect accesses of an enumeration variable
141 // when its value isn’t within the valid range for the type.
142 // This can occur for uninitialized enumeration values,
143 // or when using an integer as an enumeration value without an appropriate cast.
144 enum E {
145     A = 1,
146     B = 2
147 };
148 
EnumSan(napi_env env, napi_callback_info info)149 static napi_value EnumSan(napi_env env, napi_callback_info info)
150 {
151     struct sigaction sigabrt = {
152         .sa_handler = SignalHandlerAbort,
153     };
154     sigaction(SIGABRT, &sigabrt, nullptr);
155     size_t argc = 1;
156     napi_value args[1] = {nullptr};
157     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
158     int status;
159     int pid = fork();
160     int res = -1;
161     switch (pid) {
162         case -1: {
163             break;
164         }
165         case 0: {
166             int param;
167             napi_get_value_int32(env, args[0], &param);
168             // deliberately convert from integer to enum to trigger the undefined behavior of enum
169             enum E *e = (enum E *)&param;
170             if (*e == A) {
171                 res = 1;
172             }
173             exit(0);
174         }
175         default: {
176             waitpid(pid, &status, WUNTRACED);
177             res = WSTOPSIG(status);
178             kill(pid, SIGCONT);
179             break;
180         }
181     }
182     napi_value result = nullptr;
183     napi_create_int32(env, res, &result);
184     return result;
185 }
186 
187 // To detect out-of-range casts to, from, or between floating-point types.
FloatCastOverflow(napi_env env, napi_callback_info info)188 static napi_value FloatCastOverflow(napi_env env, napi_callback_info info)
189 {
190     struct sigaction sigabrt = {
191         .sa_handler = SignalHandlerAbort,
192     };
193     sigaction(SIGABRT, &sigabrt, nullptr);
194 
195     int status;
196     int pid = fork();
197     int res = -1;
198     switch (pid) {
199         case -1: {
200             break;
201         }
202         case 0: {
203             // deliberately convert from double to integer
204             // to trigger the undefined behavior of float-cast-overflow
205             double n = 10e50;
206             res = (int)n;
207             exit(0);
208         }
209         default: {
210             waitpid(pid, &status, WUNTRACED);
211             res = WSTOPSIG(status);
212             kill(pid, SIGCONT);
213             break;
214         }
215     }
216     napi_value result = nullptr;
217     napi_create_int32(env, res, &result);
218     return result;
219 }
220 
221 // To detect signed integer overflows in addition, subtraction, multiplication, and division.
SignedIntegerOverflow(napi_env env, napi_callback_info info)222 static napi_value SignedIntegerOverflow(napi_env env, napi_callback_info info)
223 {
224     struct sigaction sigabrt = {
225         .sa_handler = SignalHandlerAbort,
226     };
227     sigaction(SIGABRT, &sigabrt, nullptr);
228 
229     size_t argc = 1;
230     napi_value args[1] = {nullptr};
231     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
232     int status;
233     int pid = fork();
234     int res = -1;
235     switch (pid) {
236         case -1: {
237             break;
238         }
239         case 0: {
240             int param;
241             napi_get_value_int32(env, args[0], &param);
242             res = param * 42; // 42 is an arbitrary number to deliberately trigger UBSan check
243             exit(0);
244         }
245         default: {
246             waitpid(pid, &status, WUNTRACED);
247             res = WSTOPSIG(status);
248             kill(pid, SIGCONT);
249             break;
250         }
251     }
252     napi_value result = nullptr;
253     napi_create_int32(env, res, &result);
254     return result;
255 }
256 
257 struct Animal {
258     virtual const char* Speak() = 0;
259 };
260 
261 struct Cat: public Animal {
262     const char* Speak() override
263     {
264         return "meow";
265     }
266 };
267 
268 struct Dog: public Animal {
269     const char* Speak() override
270     {
271         return "woof";
272     }
273 };
274 
Vptr(Animal* obj)275 const char* Vptr(Animal* obj)
276 {
277     auto *dog = reinterpret_cast<Dog*>(obj);
278     return dog->Speak();
279 }
280 
281 // To detect when an object has the wrong dynamic type.
VptrCheck(napi_env env, napi_callback_info info)282 static napi_value VptrCheck(napi_env env, napi_callback_info info)
283 {
284     struct sigaction sigabrt = {
285         .sa_handler = SignalHandlerAbort,
286     };
287     sigaction(SIGABRT, &sigabrt, nullptr);
288 
289     int status;
290     int pid = fork();
291     int res = -1;
292     switch (pid) {
293         case -1: {
294             break;
295         }
296         case 0: {
297             Vptr(new Cat);
298             exit(0);
299         }
300         default: {
301             waitpid(pid, &status, WUNTRACED);
302             res = WSTOPSIG(status);
303             kill(pid, SIGCONT);
304             break;
305         }
306     }
307     napi_value result = nullptr;
308     napi_create_int32(env, res, &result);
309     return result;
310 }
311 
312 // To detect when a function that has an argument with the nonnull attribute receives a null value.
Foo0(__attribute__((nonnull)) void *p)313 int Foo0(__attribute__((nonnull)) void *p)
314 {
315     return 1;
316 }
317 
NonnullAttribute(napi_env env, napi_callback_info info)318 static napi_value NonnullAttribute(napi_env env, napi_callback_info info)
319 {
320     struct sigaction sigabrt = {
321         .sa_handler = SignalHandlerAbort,
322     };
323     sigaction(SIGABRT, &sigabrt, nullptr);
324     size_t argc = 1;
325     napi_value args[1] = {nullptr};
326     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
327     int status;
328     int pid = fork();
329     int res = -1;
330     switch (pid) {
331         case -1: {
332             break;
333         }
334         case 0: {
335             void* arr[3] = {nullptr, malloc(4), nullptr};
336             int param;
337             napi_get_value_int32(env, args[0], &param);
338             res = Foo0(arr[param]);
339             exit(0);
340         }
341         default: {
342             waitpid(pid, &status, WUNTRACED);
343             res = WSTOPSIG(status);
344             kill(pid, SIGCONT);
345             break;
346         }
347     }
348     napi_value result = nullptr;
349     napi_create_int32(env, res, &result);
350     return result;
351 }
352 
353 // To detect the creation of null references and null pointer dereferences.
354 // If the compiler finds a pointer dereference, it treats that pointer as nonnull.
355 // As a result, the optimizer may remove null equality checks for dereferenced pointers.
Foo1(int *x)356 int& Foo1(int *x)
357 {
358     return *(int *)x; // deliberately use of a null pointer to trigger the undefined behavior of null
359 }
360 
NullSanitize(napi_env env, napi_callback_info info)361 static napi_value NullSanitize(napi_env env, napi_callback_info info)
362 {
363     struct sigaction sigabrt = {
364         .sa_handler = SignalHandlerAbort,
365     };
366     sigaction(SIGABRT, &sigabrt, nullptr);
367     size_t argc = 1;
368     napi_value args[1] = {nullptr};
369     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
370     int status;
371     int pid = fork();
372     int res = -1;
373     switch (pid) {
374         case -1: {
375             break;
376         }
377         case 0: {
378             // deliberately convert in C-style cast to trigger UBSan check
379             int* arr[3] = {nullptr, (int *)malloc(4), nullptr};
380             int param;
381             napi_get_value_int32(env, args[0], &param);
382             res = Foo1(arr[param]);
383             exit(0);
384         }
385         default: {
386             waitpid(pid, &status, WUNTRACED);
387             res = WSTOPSIG(status);
388             kill(pid, SIGCONT);
389             break;
390         }
391     }
392     napi_value result = nullptr;
393     napi_create_int32(env, res, &result);
394     return result;
395 }
396 
397 // To detect pointer casts when the size of the source type
398 // is less than the size of the destination type.
399 class Base {
400 };
401 class Derived : public Base {
402     public:
403     int pad2;
Derived()404     Derived() : pad2(1) {}
405 };
406 
407 // To detect the pointer arithmetic which overflows,
408 // or where either the old or new pointer value is a null pointer
Foo2(int i)409 int* Foo2(int i)
410 {
411     int *p = (int *)UINTPTR_MAX;
412     return p + i;
413 }
414 
PointerOverflow(napi_env env, napi_callback_info info)415 static napi_value PointerOverflow(napi_env env, napi_callback_info info)
416 {
417     struct sigaction sigabrt = {
418         .sa_handler = SignalHandlerAbort,
419     };
420     sigaction(SIGABRT, &sigabrt, nullptr);
421 
422     size_t argc = 1;
423     napi_value args[1] = {nullptr};
424     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
425     int status;
426     int pid = fork();
427     int res = -1;
428     switch (pid) {
429         case -1: {
430             break;
431         }
432         case 0: {
433             int param;
434             napi_get_value_int32(env, args[0], &param);
435             res = *(Foo2(param));
436             exit(0);
437         }
438         default: {
439             waitpid(pid, &status, WUNTRACED);
440             res = WSTOPSIG(status);
441             kill(pid, SIGCONT);
442             break;
443         }
444     }
445     napi_value result = nullptr;
446     napi_create_int32(env, res, &result);
447     return result;
448 }
449 
450 // To detect when a function with the returns_nonnull attribute returns null.
Foo3(int *p)451 __attribute__((nonnull)) int *Foo3(int *p)
452 {
453     return p;
454 }
455 
ReturnNonnullAttribute(napi_env env, napi_callback_info info)456 static napi_value ReturnNonnullAttribute(napi_env env, napi_callback_info info)
457 {
458     struct sigaction sigabrt = {
459         .sa_handler = SignalHandlerAbort,
460     };
461     sigaction(SIGABRT, &sigabrt, nullptr);
462     size_t argc = 1;
463     napi_value args[1] = {nullptr};
464     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
465     int status;
466     int pid = fork();
467     int res = -1;
468     switch (pid) {
469         case -1: {
470             break;
471         }
472         case 0: {
473             // deliberately convert in C-style cast to trigger UBSan check
474             int* arr[3] = {nullptr, (int *)malloc(sizeof(int)), nullptr};
475             int param;
476             napi_get_value_int32(env, args[0], &param);
477             Foo3(arr[param]);
478             exit(0);
479         }
480         default: {
481             waitpid(pid, &status, WUNTRACED);
482             res = WSTOPSIG(status);
483             kill(pid, SIGCONT);
484             break;
485         }
486     }
487     napi_value result = nullptr;
488     napi_create_int32(env, res, &result);
489     return result;
490 }
491 
492 // To detect bitwise shifts with invalid shift amounts and shifts that might overflow
493 // shift-base is to check only left-hand side of shift operation
ShiftBase(napi_env env, napi_callback_info info)494 static napi_value ShiftBase(napi_env env, napi_callback_info info)
495 {
496     struct sigaction sigabrt = {
497         .sa_handler = SignalHandlerAbort,
498     };
499     sigaction(SIGABRT, &sigabrt, nullptr);
500 
501     size_t argc = 1;
502     napi_value args[1] = {nullptr};
503     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
504     int status;
505     int pid = fork();
506     int res = -1;
507     switch (pid) {
508         case -1: {
509             break;
510         }
511         case 0: {
512             int param;
513             napi_get_value_int32(env, args[0], &param);
514             int x = 1;
515             res = x << param;
516             exit(0);
517         }
518         default: {
519             waitpid(pid, &status, WUNTRACED);
520             res = WSTOPSIG(status);
521             kill(pid, SIGCONT);
522             break;
523         }
524     }
525     napi_value result = nullptr;
526     napi_create_int32(env, res, &result);
527     return result;
528 }
529 
530 // shift-exponent is to check only right-hand side of shift operation
ShiftExponent(napi_env env, napi_callback_info info)531 static napi_value ShiftExponent(napi_env env, napi_callback_info info)
532 {
533     struct sigaction sigabrt = {
534         .sa_handler = SignalHandlerAbort,
535     };
536     sigaction(SIGABRT, &sigabrt, nullptr);
537 
538     size_t argc = 1;
539     napi_value args[1] = {nullptr};
540     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
541     int status;
542     int pid = fork();
543     int res = -1;
544     switch (pid) {
545         case -1: {
546             break;
547         }
548         case 0: {
549             int param;
550             napi_get_value_int32(env, args[0], &param);
551             int x = -1;
552             res = x >> param;
553             exit(0);
554         }
555         default: {
556             waitpid(pid, &status, WUNTRACED);
557             res = WSTOPSIG(status);
558             kill(pid, SIGCONT);
559             break;
560         }
561     }
562     napi_value result = nullptr;
563     napi_create_int32(env, res, &result);
564     return result;
565 }
566 
567 // To detect accesses to a Boolean variable when its value isn’t true or false.
568 // This problem can occur when using an integer or pointer without an appropriate cast.
UndefinedBool(napi_env env, napi_callback_info info)569 static napi_value UndefinedBool(napi_env env, napi_callback_info info)
570 {
571     struct sigaction sigabrt = {
572         .sa_handler = SignalHandlerAbort,
573     };
574     sigaction(SIGABRT, &sigabrt, nullptr);
575 
576     int status;
577     int pid = fork();
578     int res = -1;
579     switch (pid) {
580         case -1: {
581             break;
582         }
583         case 0: {
584             // deliberately convert from integer to bool
585             // to trigger the undefined behavior of float-cast-overflow
586             int x = 42;
587             bool *predicate = (bool *)(&x);
588             if (*predicate) {
589                 res = 1;
590             } else {
591                 res = 0;
592             }
593             exit(0);
594         }
595         default: {
596             waitpid(pid, &status, WUNTRACED);
597             res = WSTOPSIG(status);
598             kill(pid, SIGCONT);
599             break;
600         }
601     }
602     napi_value result = nullptr;
603     napi_create_int32(env, res, &result);
604     return result;
605 }
606 
607 // To detect negative array bounds.
VlaBound(napi_env env, napi_callback_info info)608 static napi_value VlaBound(napi_env env, napi_callback_info info)
609 {
610     struct sigaction sigabrt = {
611         .sa_handler = SignalHandlerAbort,
612     };
613     sigaction(SIGABRT, &sigabrt, nullptr);
614 
615     size_t argc = 1;
616     napi_value args[1] = {nullptr};
617     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
618     int status;
619     int pid = fork();
620     int res = -1;
621     switch (pid) {
622         case -1: {
623             break;
624         }
625         case 0: {
626             int param;
627             napi_get_value_int32(env, args[0], &param);
628             int vla[param];
629             res = sizeof(vla);
630             exit(0);
631         }
632         default: {
633             waitpid(pid, &status, WUNTRACED);
634             res = WSTOPSIG(status);
635             kill(pid, SIGCONT);
636             break;
637         }
638     }
639     napi_value result = nullptr;
640     napi_create_int32(env, res, &result);
641     return result;
642 }
643 
644 EXTERN_C_START
Init(napi_env env, napi_value exports)645 static napi_value Init(napi_env env, napi_value exports)
646 {
647     napi_property_descriptor desc[] = {
648         { "misAlign", nullptr, MisAlign, nullptr, nullptr, nullptr, napi_default, nullptr },
649         { "bounds", nullptr, Bounds, nullptr, nullptr, nullptr, napi_default, nullptr },
650         { "integerDivBy0", nullptr, IntegerDivBy0, nullptr, nullptr, nullptr, napi_default, nullptr },
651         { "enumSan", nullptr, EnumSan, nullptr, nullptr, nullptr, napi_default, nullptr },
652         { "floatCastOverflow", nullptr, FloatCastOverflow, nullptr, nullptr, nullptr, napi_default, nullptr },
653         { "signedIntegerOverflow", nullptr, SignedIntegerOverflow, nullptr, nullptr, nullptr, napi_default, nullptr },
654         { "vptrCheck", nullptr, VptrCheck, nullptr, nullptr, nullptr, napi_default, nullptr },
655         { "nonnullAttribute", nullptr, NonnullAttribute, nullptr, nullptr, nullptr, napi_default, nullptr },
656         { "nullSanitize", nullptr, NullSanitize, nullptr, nullptr, nullptr, napi_default, nullptr },
657         { "pointerOverflow", nullptr, PointerOverflow, nullptr, nullptr, nullptr, napi_default, nullptr },
658         { "returnNonnullAttribute", nullptr, ReturnNonnullAttribute, nullptr, nullptr, nullptr, napi_default, nullptr },
659         { "shiftBase", nullptr, ShiftBase, nullptr, nullptr, nullptr, napi_default, nullptr },
660         { "shiftExponent", nullptr, ShiftExponent, nullptr, nullptr, nullptr, napi_default, nullptr },
661         { "undefinedBool", nullptr, UndefinedBool, nullptr, nullptr, nullptr, napi_default, nullptr },
662         { "vlaBound", nullptr, VlaBound, nullptr, nullptr, nullptr, napi_default, nullptr }
663     };
664     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
665     return exports;
666 }
667 EXTERN_C_END
668 
669 static napi_module demoModule = {
670     .nm_version = 1,
671     .nm_flags = 0,
672     .nm_filename = nullptr,
673     .nm_register_func = Init,
674     .nm_modname = "entry",
675     .nm_priv = ((void*)0),
676     .reserved = { 0 },
677 };
678 
RegisterEntryModule(void)679 extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
680 {
681     napi_module_register(&demoModule);
682 }
683