Skip to content

Commit 1570339

Browse files
build: adds a new fuzzer that targets node::LoadEnvironment()
Refs: nodejs#34761 Refs: nodejs#33724
1 parent ffa5b06 commit 1570339

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed

node.gyp

+43
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,49 @@
12031203
}],
12041204
],
12051205
}, # fuzz_url
1206+
{ # fuzz_env
1207+
'target_name': 'fuzz_env',
1208+
'type': 'executable',
1209+
'dependencies': [
1210+
'<(node_lib_target_name)',
1211+
'deps/histogram/histogram.gyp:histogram',
1212+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1213+
'node_dtrace_header',
1214+
'node_dtrace_ustack',
1215+
'node_dtrace_provider',
1216+
],
1217+
'includes': [
1218+
'node.gypi'
1219+
],
1220+
'include_dirs': [
1221+
'src',
1222+
'tools/msvs/genfiles',
1223+
'deps/v8/include',
1224+
'deps/cares/include',
1225+
'deps/uv/include',
1226+
'deps/uvwasi/include',
1227+
'test/cctest',
1228+
],
1229+
'defines': [
1230+
'NODE_ARCH="<(target_arch)"',
1231+
'NODE_PLATFORM="<(OS)"',
1232+
'NODE_WANT_INTERNALS=1',
1233+
],
1234+
'sources': [
1235+
'src/node_snapshot_stub.cc',
1236+
'src/node_code_cache_stub.cc',
1237+
'test/fuzzers/fuzz_env.cc',
1238+
],
1239+
'conditions': [
1240+
['OS=="linux"', {
1241+
'ldflags': [ '-fsanitize=fuzzer' ]
1242+
}],
1243+
# Ensure that ossfuzz flag has been set and that we are on Linux
1244+
[ 'OS!="linux" or ossfuzz!="true"', {
1245+
'type': 'none',
1246+
}],
1247+
],
1248+
}, # fuzz_env
12061249
{
12071250
'target_name': 'cctest',
12081251
'type': 'executable',

test/fuzzers/fuzz_env.cc

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* A fuzzer focused on the node::LoadEnvironment() function.
3+
*
4+
* Code here has been inspired by the cctest test case.
5+
*/
6+
7+
#include <stdlib.h>
8+
#include "node.h"
9+
#include "node_platform.h"
10+
#include "node_internals.h"
11+
#include "env-inl.h"
12+
#include "util-inl.h"
13+
#include "v8.h"
14+
#include "libplatform/libplatform.h"
15+
#include "aliased_buffer.h"
16+
#include "fuzz_helper.h"
17+
18+
using node::AliasedBufferBase;
19+
20+
/* General set up */
21+
using ArrayBufferUniquePtr = std::unique_ptr<node::ArrayBufferAllocator,
22+
decltype(&node::FreeArrayBufferAllocator)>;
23+
using TracingAgentUniquePtr = std::unique_ptr<node::tracing::Agent>;
24+
using NodePlatformUniquePtr = std::unique_ptr<node::NodePlatform>;
25+
26+
static TracingAgentUniquePtr tracing_agent;
27+
static NodePlatformUniquePtr platform;
28+
static uv_loop_t current_loop;
29+
30+
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
31+
{
32+
uv_os_unsetenv("NODE_OPTIONS");
33+
std::vector<std::string> node_argv{ "fuzz_env" };
34+
std::vector<std::string> exec_argv;
35+
std::vector<std::string> errors;
36+
37+
node::InitializeNodeWithArgs(&node_argv, &exec_argv, &errors);
38+
39+
tracing_agent = std::make_unique<node::tracing::Agent>();
40+
node::tracing::TraceEventHelper::SetAgent(tracing_agent.get());
41+
node::tracing::TracingController* tracing_controller =
42+
tracing_agent->GetTracingController();
43+
CHECK_EQ(0, uv_loop_init(&current_loop));
44+
static constexpr int kV8ThreadPoolSize = 4;
45+
platform.reset(
46+
new node::NodePlatform(kV8ThreadPoolSize, tracing_controller));
47+
v8::V8::InitializePlatform(platform.get());
48+
v8::V8::Initialize();
49+
return 0;
50+
}
51+
52+
class FuzzerFixtureHelper {
53+
public:
54+
v8::Isolate* isolate_;
55+
ArrayBufferUniquePtr allocator;
56+
57+
FuzzerFixtureHelper()
58+
: allocator(ArrayBufferUniquePtr(node::CreateArrayBufferAllocator(),
59+
&node::FreeArrayBufferAllocator))
60+
{
61+
isolate_ = NewIsolate(allocator.get(), &current_loop,
62+
platform.get());
63+
isolate_->Enter();
64+
};
65+
66+
void Teardown()
67+
{
68+
platform->DrainTasks(isolate_);
69+
isolate_->Exit();
70+
platform->UnregisterIsolate(isolate_);
71+
isolate_->Dispose();
72+
isolate_ = nullptr;
73+
}
74+
};
75+
76+
void EnvTest(v8::Isolate* isolate_, char* env_string)
77+
{
78+
const v8::HandleScope handle_scope(isolate_);
79+
Argv argv;
80+
81+
node::EnvironmentFlags::Flags flags = node::EnvironmentFlags::kDefaultFlags;
82+
auto isolate = handle_scope.GetIsolate();
83+
v8::Local<v8::Context> context_ = node::NewContext(isolate);
84+
context_->Enter();
85+
86+
node::IsolateData* isolate_data_ = node::CreateIsolateData(isolate, &current_loop,
87+
platform.get());
88+
std::vector<std::string> args(*argv, *argv + 1);
89+
std::vector<std::string> exec_args(*argv, *argv + 1);
90+
node::Environment* environment_ = node::CreateEnvironment(isolate_data_,
91+
context_, args, exec_args, flags);
92+
node::Environment* envi = environment_;
93+
SetProcessExitHandler(envi, [&](node::Environment* env_, int exit_code) {
94+
node::Stop(envi);
95+
});
96+
node::LoadEnvironment(envi, env_string);
97+
98+
// Cleanup!
99+
node::FreeEnvironment(environment_);
100+
node::FreeIsolateData(isolate_data_);
101+
context_->Exit();
102+
}
103+
104+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data2, size_t size)
105+
{
106+
FuzzerFixtureHelper ffh;
107+
108+
char* new_string = (char*)malloc(size + 1);
109+
new_string[size] = '\0';
110+
memcpy(new_string, data2, size);
111+
EnvTest(ffh.isolate_, new_string);
112+
free(new_string);
113+
114+
ffh.Teardown();
115+
return 0;
116+
}
117+

test/fuzzers/fuzz_helper.cc

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
struct Argv {
2+
public:
3+
Argv() : Argv({"node", "-p", "process.version"}) {}
4+
5+
Argv(const std::initializer_list<const char*> &args) {
6+
nr_args_ = args.size();
7+
int total_len = 0;
8+
for (auto it = args.begin(); it != args.end(); ++it) {
9+
total_len += strlen(*it) + 1;
10+
}
11+
argv_ = static_cast<char**>(malloc(nr_args_ * sizeof(char*)));
12+
argv_[0] = static_cast<char*>(malloc(total_len));
13+
int i = 0;
14+
int offset = 0;
15+
for (auto it = args.begin(); it != args.end(); ++it, ++i) {
16+
int len = strlen(*it) + 1;
17+
snprintf(argv_[0] + offset, len, "%s", *it);
18+
// Skip argv_[0] as it points the correct location already
19+
if (i > 0) {
20+
argv_[i] = argv_[0] + offset;
21+
}
22+
offset += len;
23+
}
24+
}
25+
26+
~Argv() {
27+
free(argv_[0]);
28+
free(argv_);
29+
}
30+
31+
int nr_args() const {
32+
return nr_args_;
33+
}
34+
35+
char** operator*() const {
36+
return argv_;
37+
}
38+
39+
private:
40+
char** argv_;
41+
int nr_args_;
42+
};

0 commit comments

Comments
 (0)