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], ¶m);
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], ¶m);
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], ¶m);
168 // deliberately convert from integer to enum to trigger the undefined behavior of enum
169 enum E *e = (enum E *)¶m;
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], ¶m);
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], ¶m);
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], ¶m);
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], ¶m);
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], ¶m);
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], ¶m);
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], ¶m);
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], ¶m);
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