Skip to content

Commit 9c74d45

Browse files
DavidKorczynskiBethGriggs
authored andcommitted
build: fuzzer that targets node::LoadEnvironment()
Refs: #34761 Refs: #33724 PR-URL: #34844 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent 7685297 commit 9c74d45

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

node.gyp

+43
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,49 @@
11581158
}],
11591159
],
11601160
}, # fuzz_url
1161+
{ # fuzz_env
1162+
'target_name': 'fuzz_env',
1163+
'type': 'executable',
1164+
'dependencies': [
1165+
'<(node_lib_target_name)',
1166+
'deps/histogram/histogram.gyp:histogram',
1167+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1168+
'node_dtrace_header',
1169+
'node_dtrace_ustack',
1170+
'node_dtrace_provider',
1171+
],
1172+
'includes': [
1173+
'node.gypi'
1174+
],
1175+
'include_dirs': [
1176+
'src',
1177+
'tools/msvs/genfiles',
1178+
'deps/v8/include',
1179+
'deps/cares/include',
1180+
'deps/uv/include',
1181+
'deps/uvwasi/include',
1182+
'test/cctest',
1183+
],
1184+
'defines': [
1185+
'NODE_ARCH="<(target_arch)"',
1186+
'NODE_PLATFORM="<(OS)"',
1187+
'NODE_WANT_INTERNALS=1',
1188+
],
1189+
'sources': [
1190+
'src/node_snapshot_stub.cc',
1191+
'src/node_code_cache_stub.cc',
1192+
'test/fuzzers/fuzz_env.cc',
1193+
],
1194+
'conditions': [
1195+
['OS=="linux"', {
1196+
'ldflags': [ '-fsanitize=fuzzer' ]
1197+
}],
1198+
# Ensure that ossfuzz flag has been set and that we are on Linux
1199+
[ 'OS!="linux" or ossfuzz!="true"', {
1200+
'type': 'none',
1201+
}],
1202+
],
1203+
}, # fuzz_env
11611204
{
11621205
'target_name': 'cctest',
11631206
'type': 'executable',

test/fuzzers/fuzz_env.cc

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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+
uv_os_unsetenv("NODE_OPTIONS");
32+
std::vector<std::string> node_argv{ "fuzz_env" };
33+
std::vector<std::string> exec_argv;
34+
std::vector<std::string> errors;
35+
36+
node::InitializeNodeWithArgs(&node_argv, &exec_argv, &errors);
37+
38+
tracing_agent = std::make_unique<node::tracing::Agent>();
39+
node::tracing::TraceEventHelper::SetAgent(tracing_agent.get());
40+
node::tracing::TracingController* tracing_controller =
41+
tracing_agent->GetTracingController();
42+
CHECK_EQ(0, uv_loop_init(&current_loop));
43+
static constexpr int kV8ThreadPoolSize = 4;
44+
platform.reset(
45+
new node::NodePlatform(kV8ThreadPoolSize, tracing_controller));
46+
v8::V8::InitializePlatform(platform.get());
47+
v8::V8::Initialize();
48+
return 0;
49+
}
50+
51+
class FuzzerFixtureHelper {
52+
public:
53+
v8::Isolate* isolate_;
54+
ArrayBufferUniquePtr allocator;
55+
56+
FuzzerFixtureHelper()
57+
: allocator(ArrayBufferUniquePtr(node::CreateArrayBufferAllocator(),
58+
&node::FreeArrayBufferAllocator)) {
59+
isolate_ = NewIsolate(allocator.get(), &current_loop, platform.get());
60+
CHECK_NOT_NULL(isolate_);
61+
isolate_->Enter();
62+
};
63+
64+
void Teardown() {
65+
platform->DrainTasks(isolate_);
66+
isolate_->Exit();
67+
platform->UnregisterIsolate(isolate_);
68+
isolate_->Dispose();
69+
isolate_ = nullptr;
70+
}
71+
};
72+
73+
void EnvTest(v8::Isolate* isolate_, char* env_string) {
74+
const v8::HandleScope handle_scope(isolate_);
75+
Argv argv;
76+
77+
node::EnvironmentFlags::Flags flags = node::EnvironmentFlags::kDefaultFlags;
78+
auto isolate = handle_scope.GetIsolate();
79+
v8::Local<v8::Context> context_ = node::NewContext(isolate);
80+
context_->Enter();
81+
82+
node::IsolateData* isolate_data_ = node::CreateIsolateData(isolate, &current_loop,
83+
platform.get());
84+
std::vector<std::string> args(*argv, *argv + 1);
85+
std::vector<std::string> exec_args(*argv, *argv + 1);
86+
node::Environment* environment_ = node::CreateEnvironment(isolate_data_,
87+
context_, args, exec_args, flags);
88+
node::Environment* envi = environment_;
89+
SetProcessExitHandler(envi, [&](node::Environment* env_, int exit_code) {
90+
node::Stop(envi);
91+
});
92+
node::LoadEnvironment(envi, env_string);
93+
94+
// Cleanup!
95+
node::FreeEnvironment(environment_);
96+
node::FreeIsolateData(isolate_data_);
97+
context_->Exit();
98+
}
99+
100+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data2, size_t size) {
101+
FuzzerFixtureHelper ffh;
102+
std::string s(reinterpret_cast<const char*>(data2), size);
103+
EnvTest(ffh.isolate_, (char*)s.c_str());
104+
ffh.Teardown();
105+
return 0;
106+
}
107+

test/fuzzers/fuzz_helper.h

+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)