diff --git a/node.gyp b/node.gyp index 22c270123f1658..05e67b16d8b03c 100644 --- a/node.gyp +++ b/node.gyp @@ -1203,6 +1203,49 @@ }], ], }, # fuzz_url + { # fuzz_env + 'target_name': 'fuzz_env', + 'type': 'executable', + 'dependencies': [ + '<(node_lib_target_name)', + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + 'node_dtrace_header', + 'node_dtrace_ustack', + 'node_dtrace_provider', + ], + 'includes': [ + 'node.gypi' + ], + 'include_dirs': [ + 'src', + 'tools/msvs/genfiles', + 'deps/v8/include', + 'deps/cares/include', + 'deps/uv/include', + 'deps/uvwasi/include', + 'test/cctest', + ], + 'defines': [ + 'NODE_ARCH="<(target_arch)"', + 'NODE_PLATFORM="<(OS)"', + 'NODE_WANT_INTERNALS=1', + ], + 'sources': [ + 'src/node_snapshot_stub.cc', + 'src/node_code_cache_stub.cc', + 'test/fuzzers/fuzz_env.cc', + ], + 'conditions': [ + ['OS=="linux"', { + 'ldflags': [ '-fsanitize=fuzzer' ] + }], + # Ensure that ossfuzz flag has been set and that we are on Linux + [ 'OS!="linux" or ossfuzz!="true"', { + 'type': 'none', + }], + ], + }, # fuzz_env { 'target_name': 'cctest', 'type': 'executable', diff --git a/test/fuzzers/fuzz_env.cc b/test/fuzzers/fuzz_env.cc new file mode 100644 index 00000000000000..0a7c3f392fe385 --- /dev/null +++ b/test/fuzzers/fuzz_env.cc @@ -0,0 +1,117 @@ +/* + * A fuzzer focused on the node::LoadEnvironment() function. + * + * Code here has been inspired by the cctest test case. + */ + +#include +#include "node.h" +#include "node_platform.h" +#include "node_internals.h" +#include "env-inl.h" +#include "util-inl.h" +#include "v8.h" +#include "libplatform/libplatform.h" +#include "aliased_buffer.h" +#include "fuzz_helper.h" + +using node::AliasedBufferBase; + +/* General set up */ +using ArrayBufferUniquePtr = std::unique_ptr; +using TracingAgentUniquePtr = std::unique_ptr; +using NodePlatformUniquePtr = std::unique_ptr; + +static TracingAgentUniquePtr tracing_agent; +static NodePlatformUniquePtr platform; +static uv_loop_t current_loop; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) +{ + uv_os_unsetenv("NODE_OPTIONS"); + std::vector node_argv{ "fuzz_env" }; + std::vector exec_argv; + std::vector errors; + + node::InitializeNodeWithArgs(&node_argv, &exec_argv, &errors); + + tracing_agent = std::make_unique(); + node::tracing::TraceEventHelper::SetAgent(tracing_agent.get()); + node::tracing::TracingController* tracing_controller = + tracing_agent->GetTracingController(); + CHECK_EQ(0, uv_loop_init(¤t_loop)); + static constexpr int kV8ThreadPoolSize = 4; + platform.reset( + new node::NodePlatform(kV8ThreadPoolSize, tracing_controller)); + v8::V8::InitializePlatform(platform.get()); + v8::V8::Initialize(); + return 0; +} + +class FuzzerFixtureHelper { +public: + v8::Isolate* isolate_; + ArrayBufferUniquePtr allocator; + + FuzzerFixtureHelper() + : allocator(ArrayBufferUniquePtr(node::CreateArrayBufferAllocator(), + &node::FreeArrayBufferAllocator)) + { + isolate_ = NewIsolate(allocator.get(), ¤t_loop, + platform.get()); + isolate_->Enter(); + }; + + void Teardown() + { + platform->DrainTasks(isolate_); + isolate_->Exit(); + platform->UnregisterIsolate(isolate_); + isolate_->Dispose(); + isolate_ = nullptr; + } +}; + +void EnvTest(v8::Isolate* isolate_, char* env_string) +{ + const v8::HandleScope handle_scope(isolate_); + Argv argv; + + node::EnvironmentFlags::Flags flags = node::EnvironmentFlags::kDefaultFlags; + auto isolate = handle_scope.GetIsolate(); + v8::Local context_ = node::NewContext(isolate); + context_->Enter(); + + node::IsolateData* isolate_data_ = node::CreateIsolateData(isolate, ¤t_loop, + platform.get()); + std::vector args(*argv, *argv + 1); + std::vector exec_args(*argv, *argv + 1); + node::Environment* environment_ = node::CreateEnvironment(isolate_data_, + context_, args, exec_args, flags); + node::Environment* envi = environment_; + SetProcessExitHandler(envi, [&](node::Environment* env_, int exit_code) { + node::Stop(envi); + }); + node::LoadEnvironment(envi, env_string); + + // Cleanup! + node::FreeEnvironment(environment_); + node::FreeIsolateData(isolate_data_); + context_->Exit(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data2, size_t size) +{ + FuzzerFixtureHelper ffh; + + char* new_string = (char*)malloc(size + 1); + new_string[size] = '\0'; + memcpy(new_string, data2, size); + EnvTest(ffh.isolate_, new_string); + free(new_string); + + ffh.Teardown(); + return 0; +} + diff --git a/test/fuzzers/fuzz_helper.cc b/test/fuzzers/fuzz_helper.cc new file mode 100644 index 00000000000000..707a8183dbd274 --- /dev/null +++ b/test/fuzzers/fuzz_helper.cc @@ -0,0 +1,42 @@ +struct Argv { + public: + Argv() : Argv({"node", "-p", "process.version"}) {} + + Argv(const std::initializer_list &args) { + nr_args_ = args.size(); + int total_len = 0; + for (auto it = args.begin(); it != args.end(); ++it) { + total_len += strlen(*it) + 1; + } + argv_ = static_cast(malloc(nr_args_ * sizeof(char*))); + argv_[0] = static_cast(malloc(total_len)); + int i = 0; + int offset = 0; + for (auto it = args.begin(); it != args.end(); ++it, ++i) { + int len = strlen(*it) + 1; + snprintf(argv_[0] + offset, len, "%s", *it); + // Skip argv_[0] as it points the correct location already + if (i > 0) { + argv_[i] = argv_[0] + offset; + } + offset += len; + } + } + + ~Argv() { + free(argv_[0]); + free(argv_); + } + + int nr_args() const { + return nr_args_; + } + + char** operator*() const { + return argv_; + } + + private: + char** argv_; + int nr_args_; +};