Skip to content

Commit c676e52

Browse files
AdamKorcztargos
authored andcommitted
test: add fuzzer for native/js string conversion
Signed-off-by: Adam Korczynski <[email protected]> PR-URL: #51120 Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent 5f6415b commit c676e52

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed

node.gyp

+45
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,51 @@
11101110
}],
11111111
],
11121112
}, # fuzz_ClientHelloParser.cc
1113+
{ # fuzz_strings
1114+
'target_name': 'fuzz_strings',
1115+
'type': 'executable',
1116+
'dependencies': [
1117+
'<(node_lib_target_name)',
1118+
'deps/googletest/googletest.gyp:gtest_prod',
1119+
'deps/histogram/histogram.gyp:histogram',
1120+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1121+
'deps/ada/ada.gyp:ada',
1122+
],
1123+
'includes': [
1124+
'node.gypi'
1125+
],
1126+
'include_dirs': [
1127+
'src',
1128+
'tools/msvs/genfiles',
1129+
'deps/v8/include',
1130+
'deps/cares/include',
1131+
'deps/uv/include',
1132+
'deps/uvwasi/include',
1133+
'test/cctest',
1134+
],
1135+
'defines': [
1136+
'NODE_ARCH="<(target_arch)"',
1137+
'NODE_PLATFORM="<(OS)"',
1138+
'NODE_WANT_INTERNALS=1',
1139+
],
1140+
'sources': [
1141+
'src/node_snapshot_stub.cc',
1142+
'test/fuzzers/fuzz_strings.cc',
1143+
],
1144+
'conditions': [
1145+
['OS=="linux"', {
1146+
'ldflags': [ '-fsanitize=fuzzer' ]
1147+
}],
1148+
# Ensure that ossfuzz flag has been set and that we are on Linux
1149+
[ 'OS!="linux" or ossfuzz!="true"', {
1150+
'type': 'none',
1151+
}],
1152+
# Avoid excessive LTO
1153+
['enable_lto=="true"', {
1154+
'ldflags': [ '-fno-lto' ],
1155+
}],
1156+
],
1157+
}, # fuzz_strings
11131158
{
11141159
'target_name': 'cctest',
11151160
'type': 'executable',

test/fuzzers/fuzz_strings.cc

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* A fuzzer focused on C string -> Javascript String.
3+
*/
4+
5+
#include <stdlib.h>
6+
#include "js_native_api.h"
7+
#include "js_native_api_v8.h"
8+
#include "node.h"
9+
#include "node_platform.h"
10+
#include "node_internals.h"
11+
#include "node_api_internals.h"
12+
#include "node_url.h"
13+
#include "env-inl.h"
14+
#include "util-inl.h"
15+
#include "v8.h"
16+
#include "libplatform/libplatform.h"
17+
#include "aliased_buffer.h"
18+
#include "fuzz_helper.h"
19+
20+
using node::AliasedBufferBase;
21+
22+
/* General set up */
23+
using ArrayBufferUniquePtr = std::unique_ptr<node::ArrayBufferAllocator,
24+
decltype(&node::FreeArrayBufferAllocator)>;
25+
using TracingAgentUniquePtr = std::unique_ptr<node::tracing::Agent>;
26+
using NodePlatformUniquePtr = std::unique_ptr<node::NodePlatform>;
27+
28+
static TracingAgentUniquePtr tracing_agent;
29+
static NodePlatformUniquePtr platform;
30+
static uv_loop_t current_loop;
31+
static napi_env addon_env;
32+
33+
inline napi_env NewEnv(v8::Local<v8::Context> context,
34+
const std::string& module_filename,
35+
int32_t module_api_version) ;
36+
37+
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
38+
uv_os_unsetenv("NODE_OPTIONS");
39+
std::vector<std::string> node_argv{ "fuzz_string_conversion" };
40+
std::vector<std::string> exec_argv;
41+
std::vector<std::string> errors;
42+
43+
node::InitializeNodeWithArgs(&node_argv, &exec_argv, &errors);
44+
45+
tracing_agent = std::make_unique<node::tracing::Agent>();
46+
node::tracing::TraceEventHelper::SetAgent(tracing_agent.get());
47+
node::tracing::TracingController* tracing_controller =
48+
tracing_agent->GetTracingController();
49+
CHECK_EQ(0, uv_loop_init(&current_loop));
50+
static constexpr int kV8ThreadPoolSize = 4;
51+
platform.reset(
52+
new node::NodePlatform(kV8ThreadPoolSize, tracing_controller));
53+
v8::V8::InitializePlatform(platform.get());
54+
cppgc::InitializeProcess(platform->GetPageAllocator());
55+
v8::V8::Initialize();
56+
return 0;
57+
}
58+
59+
class FuzzerFixtureHelper {
60+
public:
61+
v8::Isolate* isolate_;
62+
ArrayBufferUniquePtr allocator;
63+
64+
FuzzerFixtureHelper()
65+
: allocator(ArrayBufferUniquePtr(node::CreateArrayBufferAllocator(),
66+
&node::FreeArrayBufferAllocator)) {
67+
isolate_ = NewIsolate(allocator.get(), &current_loop, platform.get());
68+
CHECK_NOT_NULL(isolate_);
69+
isolate_->Enter();
70+
};
71+
72+
void Teardown() {
73+
platform->DrainTasks(isolate_);
74+
isolate_->Exit();
75+
platform->UnregisterIsolate(isolate_);
76+
isolate_->Dispose();
77+
isolate_ = nullptr;
78+
}
79+
};
80+
81+
void EnvTest(v8::Isolate* isolate_, char* env_string, size_t size) {
82+
const v8::HandleScope handle_scope(isolate_);
83+
Argv argv;
84+
85+
node::EnvironmentFlags::Flags flags = node::EnvironmentFlags::kDefaultFlags;
86+
auto isolate = handle_scope.GetIsolate();
87+
v8::Local<v8::Context> context_ = node::NewContext(isolate);
88+
context_->Enter();
89+
90+
node::IsolateData* isolate_data_ = node::CreateIsolateData(isolate, &current_loop,
91+
platform.get());
92+
std::vector<std::string> args(*argv, *argv + 1);
93+
std::vector<std::string> exec_args(*argv, *argv + 1);
94+
node::Environment* environment_ = node::CreateEnvironment(isolate_data_,
95+
context_, args, exec_args, flags);
96+
node::Environment* envi = environment_;
97+
SetProcessExitHandler(envi, [&](node::Environment* env_, int exit_code) {
98+
node::Stop(envi);
99+
});
100+
node::LoadEnvironment(envi, "");
101+
102+
103+
napi_addon_register_func init = [](napi_env env, napi_value exports) {
104+
addon_env = env;
105+
return exports;
106+
};
107+
v8::Local<v8::Object> module_obj = v8::Object::New(isolate);
108+
v8::Local<v8::Object> exports_obj = v8::Object::New(isolate);
109+
napi_module_register_by_symbol(
110+
exports_obj, module_obj, context_, init, NAPI_VERSION);
111+
size_t copied1, copied2;
112+
napi_value output1, output2;
113+
char *buf1 = (char *)malloc(size);
114+
char *buf2 = (char *)malloc(size);
115+
napi_create_string_utf8(addon_env, env_string, size, &output1);
116+
napi_get_value_string_utf8(addon_env, output1, buf1, size, &copied1);
117+
napi_create_string_latin1(addon_env, env_string, size, &output2);
118+
napi_get_value_string_latin1(addon_env, output2, buf2, size, &copied2);
119+
free(buf1);
120+
free(buf2);
121+
122+
// Cleanup!
123+
node::FreeEnvironment(environment_);
124+
node::FreeIsolateData(isolate_data_);
125+
context_->Exit();
126+
}
127+
128+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data2, size_t size) {
129+
FuzzerFixtureHelper ffh;
130+
std::string s(reinterpret_cast<const char*>(data2), size);
131+
EnvTest(ffh.isolate_, (char*)s.c_str(), size);
132+
ffh.Teardown();
133+
return 0;
134+
}
135+

0 commit comments

Comments
 (0)