1
- // Copyright 2022 Code Intelligence GmbH
1
+ // Copyright 2023 Code Intelligence GmbH
2
2
//
3
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
4
// you may not use this file except in compliance with the License.
15
15
#include " fuzzing_sync.h"
16
16
#include " shared/libfuzzer.h"
17
17
#include " utils.h"
18
+ #include < csignal>
18
19
#include < cstdlib>
19
20
#include < optional>
20
21
@@ -23,14 +24,22 @@ namespace {
23
24
struct FuzzTargetInfo {
24
25
Napi::Env env;
25
26
Napi::Function target;
27
+ Napi::Function stopFunction;
26
28
};
27
29
28
30
// The JS fuzz target. We need to store the function pointer in a global
29
31
// variable because libfuzzer doesn't give us a way to feed user-provided data
30
32
// to its target function.
31
33
std::optional<FuzzTargetInfo> gFuzzTarget ;
34
+
35
+ // Track if SIGINT signal handler was called.
36
+ // This is only necessary in the sync fuzzing case, as async can be handled
37
+ // much nicer directly in JavaScript.
38
+ volatile std::sig_atomic_t gSignalStatus ;
32
39
} // namespace
33
40
41
+ void sigintHandler (int signum) { gSignalStatus = signum; }
42
+
34
43
// The libFuzzer callback when fuzzing synchronously
35
44
int FuzzCallbackSync (const uint8_t *Data, size_t Size) {
36
45
// Create a new active scope so that handles for the buffer objects created in
@@ -60,6 +69,12 @@ int FuzzCallbackSync(const uint8_t *Data, size_t Size) {
60
69
} else {
61
70
SyncReturnsHandler ();
62
71
}
72
+
73
+ // Execute the signal handler in context of the node application.
74
+ if (gSignalStatus != 0 ) {
75
+ gFuzzTarget ->stopFunction .Call ({});
76
+ }
77
+
63
78
return EXIT_SUCCESS;
64
79
}
65
80
@@ -71,17 +86,24 @@ int FuzzCallbackSync(const uint8_t *Data, size_t Size) {
71
86
// parameter; the fuzz target's return value is ignored. The second argument
72
87
// is an array of (command-line) arguments to pass to libfuzzer.
73
88
void StartFuzzing (const Napi::CallbackInfo &info) {
74
- if (info.Length () != 2 || !info[0 ].IsFunction () || !info[1 ].IsArray ()) {
75
- throw Napi::Error::New (info.Env (),
76
- " Need two arguments, which must be the fuzz target "
77
- " function and an array of libfuzzer arguments" );
89
+ if (info.Length () != 3 || !info[0 ].IsFunction () || !info[1 ].IsArray () ||
90
+ !info[2 ].IsFunction ()) {
91
+ throw Napi::Error::New (
92
+ info.Env (),
93
+ " Need three arguments, which must be the fuzz target "
94
+ " function, an array of libfuzzer arguments, and a callback function "
95
+ " that the fuzzer will call in case of SIGINT or a segmentation fault" );
78
96
}
79
97
80
98
auto fuzzer_args = LibFuzzerArgs (info.Env (), info[1 ].As <Napi::Array>());
81
99
82
100
// Store the JS fuzz target and corresponding environment globally, so that
83
- // our C++ fuzz target can use them to call back into JS.
84
- gFuzzTarget = {info.Env (), info[0 ].As <Napi::Function>()};
101
+ // our C++ fuzz target can use them to call back into JS. Also store the stop
102
+ // function that will be called in case of a SIGINT.
103
+ gFuzzTarget = {info.Env (), info[0 ].As <Napi::Function>(),
104
+ info[2 ].As <Napi::Function>()};
105
+
106
+ signal (SIGINT, sigintHandler);
85
107
86
108
StartLibFuzzer (fuzzer_args, FuzzCallbackSync);
87
109
// Explicitly reset the global function pointer because the JS
@@ -93,9 +115,9 @@ void StartFuzzing(const Napi::CallbackInfo &info) {
93
115
void StopFuzzing (const Napi::CallbackInfo &info) {
94
116
int exitCode = StopFuzzingHandleExit (info);
95
117
96
- // If we ran in async mode and we only ever encountered synchronous results
118
+ // If we ran in async mode, and we only ever encountered synchronous results
97
119
// we'll give an indicator that running in synchronous mode is likely
98
- // benefical
120
+ // beneficial.
99
121
ReturnValueInfo (true );
100
122
101
123
// We call _Exit to immediately terminate the process without performing any
0 commit comments