From c8805b8043796439ab2a2bf09faefe9497a8aa1a Mon Sep 17 00:00:00 2001 From: Adam Korczynski Date: Mon, 11 Dec 2023 15:17:28 +0000 Subject: [PATCH] test: add fuzzer for native/js string conversion Signed-off-by: Adam Korczynski PR-URL: https://github.com/nodejs/node/pull/51120 Reviewed-By: Yagiz Nizipli --- node.gyp | 45 ++++++++++++ test/fuzzers/fuzz_strings.cc | 135 +++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 test/fuzzers/fuzz_strings.cc diff --git a/node.gyp b/node.gyp index 0a31a15c403..14ef5521329 100644 --- a/node.gyp +++ b/node.gyp @@ -1109,6 +1109,51 @@ }], ], }, # fuzz_ClientHelloParser.cc + { # fuzz_strings + 'target_name': 'fuzz_strings', + 'type': 'executable', + 'dependencies': [ + '<(node_lib_target_name)', + 'deps/googletest/googletest.gyp:gtest_prod', + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + 'deps/ada/ada.gyp:ada', + ], + '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', + 'test/fuzzers/fuzz_strings.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', + }], + # Avoid excessive LTO + ['enable_lto=="true"', { + 'ldflags': [ '-fno-lto' ], + }], + ], + }, # fuzz_strings { 'target_name': 'cctest', 'type': 'executable', diff --git a/test/fuzzers/fuzz_strings.cc b/test/fuzzers/fuzz_strings.cc new file mode 100644 index 00000000000..8f5e1a473e3 --- /dev/null +++ b/test/fuzzers/fuzz_strings.cc @@ -0,0 +1,135 @@ +/* + * A fuzzer focused on C string -> Javascript String. +*/ + +#include +#include "js_native_api.h" +#include "js_native_api_v8.h" +#include "node.h" +#include "node_platform.h" +#include "node_internals.h" +#include "node_api_internals.h" +#include "node_url.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; +static napi_env addon_env; + +inline napi_env NewEnv(v8::Local context, + const std::string& module_filename, + int32_t module_api_version) ; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + uv_os_unsetenv("NODE_OPTIONS"); + std::vector node_argv{ "fuzz_string_conversion" }; + 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()); + cppgc::InitializeProcess(platform->GetPageAllocator()); + 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()); + CHECK_NOT_NULL(isolate_); + isolate_->Enter(); + }; + + void Teardown() { + platform->DrainTasks(isolate_); + isolate_->Exit(); + platform->UnregisterIsolate(isolate_); + isolate_->Dispose(); + isolate_ = nullptr; + } +}; + +void EnvTest(v8::Isolate* isolate_, char* env_string, size_t size) { + 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, ""); + + + napi_addon_register_func init = [](napi_env env, napi_value exports) { + addon_env = env; + return exports; + }; + v8::Local module_obj = v8::Object::New(isolate); + v8::Local exports_obj = v8::Object::New(isolate); + napi_module_register_by_symbol( + exports_obj, module_obj, context_, init, NAPI_VERSION); + size_t copied1, copied2; + napi_value output1, output2; + char *buf1 = (char *)malloc(size); + char *buf2 = (char *)malloc(size); + napi_create_string_utf8(addon_env, env_string, size, &output1); + napi_get_value_string_utf8(addon_env, output1, buf1, size, &copied1); + napi_create_string_latin1(addon_env, env_string, size, &output2); + napi_get_value_string_latin1(addon_env, output2, buf2, size, &copied2); + free(buf1); + free(buf2); + + // Cleanup! + node::FreeEnvironment(environment_); + node::FreeIsolateData(isolate_data_); + context_->Exit(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data2, size_t size) { + FuzzerFixtureHelper ffh; + std::string s(reinterpret_cast(data2), size); + EnvTest(ffh.isolate_, (char*)s.c_str(), size); + ffh.Teardown(); + return 0; +} +