Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

34
deps/v8/test/fuzzer/BUILD.gn vendored Normal file
View File

@ -0,0 +1,34 @@
# Copyright 2018 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("../../gni/v8.gni")
group("v8_fuzzer") {
testonly = true
data_deps = [
"../..:v8_fuzzers",
"../../tools:v8_testrunner",
]
data = [
"./fuzzer.status",
"./testcfg.py",
"./inspector/",
"./json/",
"./parser/",
"./regexp/",
]
if (v8_enable_webassembly) {
data += [
"./multi_return/",
"./wasm/async/",
"./wasm/code/",
"./wasm/compile/",
"./wasm/module/",
"./wasm/streaming/",
]
}
}

4
deps/v8/test/fuzzer/DEPS vendored Normal file
View File

@ -0,0 +1,4 @@
include_rules = [
"+src",
"+tools/wasm",
]

83
deps/v8/test/fuzzer/README.md vendored Normal file
View File

@ -0,0 +1,83 @@
# How to make a libFuzzer fuzzer in V8
This document describes how to make a new libFuzzer fuzzer for V8. A general
introduction to libFuzzer can be found
[here](https://chromium.googlesource.com/chromium/src/+/master/testing/libfuzzer/README.md).
In short, libFuzzer is an in-process coverage-driven evolutionary fuzzer.
libFuzzer serves you with a sequence of byte arrays that you can use to test
your code. libFuzzer tries to generate this sequence of byte arrays in a way
that maximizes test coverage.
**Warning**: By itself libFuzzer typically does not generate valid JavaScript code.
## Changes to V8
**tldr:** Do the same as https://codereview.chromium.org/2280623002 to introduce
a new fuzzer to V8.
This is a step by step guide on how to make a new fuzzer in V8. In the example
the fuzzer is called `foo`.
1. Copy one of the existing fuzzer implementations in
[test/fuzzer/](https://cs.chromium.org/chromium/src/v8/test/fuzzer/), e.g. `cp wasm.cc foo.cc`
* Copying an existing fuzzer is a good idea to get all the required setup,
e.g. setting up the isolate
2. Create a directory called `foo` in
[test/fuzzer/](https://cs.chromium.org/chromium/src/v8/test/fuzzer/) which
contains at least one file
* The file is used by the trybots to check whether the fuzzer actually
compiles and runs
3. Copy the build rules of an existing fuzzer in
[BUILD.gn](https://cs.chromium.org/chromium/src/v8/BUILD.gn), e.g. the build
rules for the
[wasm.cc](https://cs.chromium.org/chromium/src/v8/test/fuzzer/wasm.cc) fuzzer
are `v8_source_set("wasm_fuzzer")` and `v8_fuzzer("wasm_fuzzer")`. Note that
the name has to be the name of the directory created in Step 2 + `_fuzzer` so
that the scripts on the trybots work
4. Now you can already compile the fuzzer, e.g. with `ninja -j 1000 -C
out/x64.debug/v8_simple_foo_fuzzer`
* Use this binary to reproduce issues found by cluster fuzz, e.g.
`out/x64.debug/v8_simple_foo_fuzzer testcase.foo`
5. Copy the binary name and the test directory name in
[test/fuzzer/fuzzer.isolate](https://cs.chromium.org/chromium/src/v8/test/fuzzer/fuzzer.isolate)
6. Add the fuzzer to the FuzzerTestSuite in
[test/fuzzer/testcfg.py](https://cs.chromium.org/chromium/src/v8/test/fuzzer/testcfg.py)
* This step is needed to run the fuzzer with the files created in Step 2 on
the trybots
8. Commit the changes described above to the V8 repository
## Changes to Chromium
**tldr:** Do the same as https://codereview.chromium.org/2344823002 to add the
new fuzzer to cluster fuzz.
1. Copy the build rules of an existing fuzzer in
[testing/libfuzzer/fuzzers/BUILD.gn](https://cs.chromium.org/chromium/src/testing/libfuzzer/fuzzers/BUILD.gn),
e.g. the build rule for the
[wasm.cc](https://cs.chromium.org/chromium/src/v8/test/fuzzer/wasm.cc) fuzzer
is `v8_wasm_fuzzer`. There is no need to set a `dictionary` , or a `seed_corpus`.
See
[chromium-fuzzing-getting-started](https://chromium.googlesource.com/chromium/src/+/master/testing/libfuzzer/getting_started.md)
for more information.
2. Compile the fuzzer in chromium (for different configurations see:
https://chromium.googlesource.com/chromium/src/+/master/testing/libfuzzer/reference.md):
* `gn gen out/libfuzzer '--args=use_libfuzzer=true is_asan=true is_debug=false enable_nacl=false'`
* `ninja -j 1000 -C out/libfuzzer/ v8_foo_fuzzer`
3. Run the fuzzer locally
* `mkdir /tmp/empty_corpus && out/libfuzzer/v8_foo_fuzzer /tmp/empty_corpus`

139
deps/v8/test/fuzzer/fuzzer-support.cc vendored Normal file
View File

@ -0,0 +1,139 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/fuzzer/fuzzer-support.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include "include/libplatform/libplatform.h"
#include "include/v8-context.h"
#include "include/v8-initialization.h"
#include "src/flags/flags.h"
#include "src/trap-handler/trap-handler.h"
namespace v8_fuzzer {
FuzzerSupport::FuzzerSupport(int* argc, char*** argv) {
// Disable hard abort, which generates a trap instead of a proper abortion.
// Traps by default do not cause libfuzzer to generate a crash file.
i::v8_flags.hard_abort = false;
i::v8_flags.expose_gc = true;
i::v8_flags.fuzzing = true;
// Allow changing flags in fuzzers.
// TODO(12887): Refactor fuzzers to not change flags after initialization.
i::v8_flags.freeze_flags_after_init = false;
#if V8_ENABLE_WEBASSEMBLY
if (V8_TRAP_HANDLER_SUPPORTED) {
constexpr bool kUseDefaultTrapHandler = true;
if (!v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultTrapHandler)) {
FATAL("Could not register trap handler");
}
}
#endif // V8_ENABLE_WEBASSEMBLY
v8::V8::SetFlagsFromCommandLine(argc, *argv, true);
for (int arg_idx = 1; arg_idx < *argc; ++arg_idx) {
const char* const arg = (*argv)[arg_idx];
if (arg[0] != '-' || arg[1] != '-') continue;
// Stop processing args at '--'.
if (arg[2] == '\0') break;
fprintf(stderr, "Unrecognized flag %s\n", arg);
// Move remaining flags down.
std::move(*argv + arg_idx + 1, *argv + *argc, *argv + arg_idx);
--*argc, --arg_idx;
}
i::FlagList::ResolveContradictionsWhenFuzzing();
v8::V8::InitializeICUDefaultLocation((*argv)[0]);
v8::V8::InitializeExternalStartupData((*argv)[0]);
platform_ = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform_.get());
v8::V8::Initialize();
allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = allocator_;
create_params.allow_atomics_wait = false;
isolate_ = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope(isolate_);
context_.Reset(isolate_, v8::Context::New(isolate_));
}
}
FuzzerSupport::~FuzzerSupport() {
{
v8::Isolate::Scope isolate_scope(isolate_);
{
while (PumpMessageLoop()) {
// empty
}
v8::HandleScope handle_scope(isolate_);
context_.Reset();
}
isolate_->LowMemoryNotification();
}
v8::platform::NotifyIsolateShutdown(platform_.get(), isolate_);
isolate_->Dispose();
isolate_ = nullptr;
delete allocator_;
allocator_ = nullptr;
v8::V8::Dispose();
v8::V8::DisposePlatform();
}
std::unique_ptr<FuzzerSupport> FuzzerSupport::fuzzer_support_;
// static
void FuzzerSupport::InitializeFuzzerSupport(int* argc, char*** argv) {
DCHECK_NULL(FuzzerSupport::fuzzer_support_);
FuzzerSupport::fuzzer_support_ =
std::make_unique<v8_fuzzer::FuzzerSupport>(argc, argv);
}
// static
FuzzerSupport* FuzzerSupport::Get() {
DCHECK_NOT_NULL(FuzzerSupport::fuzzer_support_);
return FuzzerSupport::fuzzer_support_.get();
}
v8::Local<v8::Context> FuzzerSupport::GetContext() {
v8::Isolate::Scope isolate_scope(isolate_);
v8::EscapableHandleScope handle_scope(isolate_);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, context_);
return handle_scope.Escape(context);
}
bool FuzzerSupport::PumpMessageLoop(
v8::platform::MessageLoopBehavior behavior) {
return v8::platform::PumpMessageLoop(platform_.get(), isolate_, behavior);
}
} // namespace v8_fuzzer
// Explicitly specify some attributes to avoid issues with the linker dead-
// stripping the following function on macOS, as it is not called directly
// by fuzz target. LibFuzzer runtime uses dlsym() to resolve that function.
#if V8_OS_DARWIN
__attribute__((used)) __attribute__((visibility("default")))
#endif // V8_OS_DARWIN
extern "C" int
LLVMFuzzerInitialize(int* argc, char*** argv) {
v8_fuzzer::FuzzerSupport::InitializeFuzzerSupport(argc, argv);
return 0;
}

51
deps/v8/test/fuzzer/fuzzer-support.h vendored Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TEST_FUZZER_FUZZER_SUPPORT_H_
#define TEST_FUZZER_FUZZER_SUPPORT_H_
#include <memory>
#include "include/libplatform/libplatform.h"
#include "include/v8-array-buffer.h"
#include "include/v8-local-handle.h"
#include "include/v8-persistent-handle.h"
namespace v8 {
class Context;
class Isolate;
} // namespace v8
namespace v8_fuzzer {
class FuzzerSupport {
public:
FuzzerSupport(int* argc, char*** argv);
FuzzerSupport(const FuzzerSupport&) = delete;
FuzzerSupport& operator=(const FuzzerSupport&) = delete;
~FuzzerSupport();
static void InitializeFuzzerSupport(int* argc, char*** argv);
static FuzzerSupport* Get();
v8::Isolate* GetIsolate() const { return isolate_; }
v8::Local<v8::Context> GetContext();
bool PumpMessageLoop(v8::platform::MessageLoopBehavior =
v8::platform::MessageLoopBehavior::kDoNotWait);
private:
static std::unique_ptr<FuzzerSupport> fuzzer_support_;
std::unique_ptr<v8::Platform> platform_;
v8::ArrayBuffer::Allocator* allocator_;
v8::Isolate* isolate_;
v8::Global<v8::Context> context_;
};
} // namespace v8_fuzzer
#endif // TEST_FUZZER_FUZZER_SUPPORT_H_

59
deps/v8/test/fuzzer/fuzzer.cc vendored Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
int main(int argc, char* argv[]) {
if (LLVMFuzzerInitialize(&argc, &argv)) {
fprintf(stderr, "Failed to initialize fuzzer target\n");
return 1;
}
std::vector<uint8_t> input_data;
bool after_dash_dash = false;
for (int arg_idx = 1; arg_idx < argc; ++arg_idx) {
const char* const arg = argv[arg_idx];
// Ignore first '--' argument.
if (!after_dash_dash && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') {
after_dash_dash = true;
continue;
}
FILE* input = fopen(arg, "rb");
if (!input) {
fprintf(stderr, "Failed to open '%s'\n", arg);
return 1;
}
fseek(input, 0, SEEK_END);
size_t size = ftell(input);
fseek(input, 0, SEEK_SET);
size_t old_size = input_data.size();
input_data.resize(old_size + size);
size_t bytes_read = fread(input_data.data() + old_size, 1, size, input);
fclose(input);
if (bytes_read != size) {
fprintf(stderr, "Failed to read %zu bytes from %s\n", size, arg);
return 1;
}
}
// Ensure that {input_data.data()} is not {nullptr} to avoid having to handle
// this in specific fuzzers.
if (input_data.empty()) input_data.reserve(1);
return LLVMFuzzerTestOneInput(input_data.data(), input_data.size());
}

30
deps/v8/test/fuzzer/fuzzer.status vendored Normal file
View File

@ -0,0 +1,30 @@
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
[
##############################################################################
# TODO(v8:7777): Change this once wasm is supported in jitless mode.
['not has_webassembly or variant == jitless', {
'multi_return/*': [SKIP],
'wasm/*': [SKIP],
}], # not has_webassembly or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
##############################################################################
['no_simd_hardware == True', {
'wasm/module/regress-1464231.wasm': [SKIP],
}], # no_simd_hardware == True
##############################################################################
['wasm_random_fuzzers == False', {
# Random Wasm module generation is not available on official builds.
'wasm/compile/*': [FAIL],
}], # 'wasm_random_fuzzers == False'
]

642
deps/v8/test/fuzzer/inspector-fuzzer.cc vendored Normal file
View File

@ -0,0 +1,642 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <locale.h>
#include <optional>
#include <string>
#include <vector>
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "include/v8-object.h"
#include "include/v8-primitive.h"
#include "include/v8-template.h"
#include "src/api/api.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/base/small-vector.h"
#include "src/base/vector.h"
#include "test/inspector/frontend-channel.h"
#include "test/inspector/isolate-data.h"
#include "test/inspector/task-runner.h"
#include "test/inspector/tasks.h"
#if !defined(V8_OS_WIN)
#include <unistd.h>
#endif // !defined(V8_OS_WIN)
namespace v8 {
namespace internal {
namespace {
base::SmallVector<TaskRunner*, 2> task_runners;
class UtilsExtension : public InspectorIsolateData::SetupGlobalTask {
public:
~UtilsExtension() override = default;
void Run(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> global) override {
v8::Local<v8::ObjectTemplate> utils = v8::ObjectTemplate::New(isolate);
auto Set = [isolate](v8::Local<v8::ObjectTemplate> tmpl, const char* str,
v8::Local<v8::Data> value) {
// Do not set {ReadOnly}, because fuzzer inputs might overwrite individual
// methods, or the whole "utils" global. See the
// `testing/libfuzzer/fuzzers/generate_v8_inspector_fuzzer_corpus.py` file
// in chromium.
tmpl->Set(ToV8String(isolate, str), value,
static_cast<v8::PropertyAttribute>(
v8::PropertyAttribute::DontDelete));
};
Set(utils, "quit",
v8::FunctionTemplate::New(isolate, &UtilsExtension::Quit));
Set(utils, "compileAndRunWithOrigin",
v8::FunctionTemplate::New(isolate,
&UtilsExtension::CompileAndRunWithOrigin));
Set(utils, "schedulePauseOnNextStatement",
v8::FunctionTemplate::New(
isolate, &UtilsExtension::SchedulePauseOnNextStatement));
Set(utils, "cancelPauseOnNextStatement",
v8::FunctionTemplate::New(isolate,
&UtilsExtension::CancelPauseOnNextStatement));
Set(utils, "createContextGroup",
v8::FunctionTemplate::New(isolate,
&UtilsExtension::CreateContextGroup));
Set(utils, "resetContextGroup",
v8::FunctionTemplate::New(isolate, &UtilsExtension::ResetContextGroup));
Set(utils, "connectSession",
v8::FunctionTemplate::New(isolate, &UtilsExtension::ConnectSession));
Set(utils, "disconnectSession",
v8::FunctionTemplate::New(isolate, &UtilsExtension::DisconnectSession));
Set(utils, "sendMessageToBackend",
v8::FunctionTemplate::New(isolate,
&UtilsExtension::SendMessageToBackend));
Set(global, "utils", utils);
}
static void set_backend_task_runner(TaskRunner* runner) {
backend_runner_ = runner;
}
private:
static TaskRunner* backend_runner_;
static void Quit(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
// Only terminate, so not join the threads here, since joining concurrently
// from multiple threads can be undefined behaviour (see pthread_join).
for (TaskRunner* task_runner : task_runners) task_runner->Terminate();
}
static void CompileAndRunWithOrigin(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 6 || !info[0]->IsInt32() || !info[1]->IsString() ||
!info[2]->IsString() || !info[3]->IsInt32() || !info[4]->IsInt32() ||
!info[5]->IsBoolean()) {
return;
}
backend_runner_->Append(std::make_unique<ExecuteStringTask>(
info.GetIsolate(), info[0].As<v8::Int32>()->Value(),
ToVector(info.GetIsolate(), info[1].As<v8::String>()),
info[2].As<v8::String>(), info[3].As<v8::Int32>(),
info[4].As<v8::Int32>(), info[5].As<v8::Boolean>()));
}
static void SchedulePauseOnNextStatement(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 3 || !info[0]->IsInt32() || !info[1]->IsString() ||
!info[2]->IsString()) {
return;
}
std::vector<uint16_t> reason =
ToVector(info.GetIsolate(), info[1].As<v8::String>());
std::vector<uint16_t> details =
ToVector(info.GetIsolate(), info[2].As<v8::String>());
int context_group_id = info[0].As<v8::Int32>()->Value();
RunSyncTask(backend_runner_,
[&context_group_id, &reason,
&details](InspectorIsolateData* data) {
data->SchedulePauseOnNextStatement(
context_group_id,
v8_inspector::StringView(reason.data(), reason.size()),
v8_inspector::StringView(details.data(), details.size()));
});
}
static void CancelPauseOnNextStatement(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsInt32()) {
return;
}
int context_group_id = info[0].As<v8::Int32>()->Value();
RunSyncTask(backend_runner_,
[&context_group_id](InspectorIsolateData* data) {
data->CancelPauseOnNextStatement(context_group_id);
});
}
static void CreateContextGroup(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 0) {
return;
}
int context_group_id = 0;
RunSyncTask(backend_runner_,
[&context_group_id](InspectorIsolateData* data) {
context_group_id = data->CreateContextGroup();
});
info.GetReturnValue().Set(
v8::Int32::New(info.GetIsolate(), context_group_id));
}
static void ResetContextGroup(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsInt32()) {
return;
}
int context_group_id = info[0].As<v8::Int32>()->Value();
RunSyncTask(backend_runner_,
[&context_group_id](InspectorIsolateData* data) {
data->ResetContextGroup(context_group_id);
});
}
static bool IsValidConnectSessionArgs(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 3 || info.Length() > 4) return false;
if (!info[0]->IsInt32() || !info[1]->IsString() || !info[2]->IsFunction()) {
return false;
}
return info.Length() == 3 || info[3]->IsBoolean();
}
static void ConnectSession(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (!IsValidConnectSessionArgs(info)) return;
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
std::unique_ptr<FrontendChannelImpl> channel =
std::make_unique<FrontendChannelImpl>(
InspectorIsolateData::FromContext(context)->task_runner(),
InspectorIsolateData::FromContext(context)->GetContextGroupId(
context),
info.GetIsolate(), info[2].As<v8::Function>());
std::vector<uint8_t> state =
ToBytes(info.GetIsolate(), info[1].As<v8::String>());
int context_group_id = info[0].As<v8::Int32>()->Value();
bool is_fully_trusted =
info.Length() == 3 || info[3].As<v8::Boolean>()->Value();
std::optional<int> session_id;
RunSyncTask(backend_runner_,
[context_group_id, &session_id, &channel, &state,
is_fully_trusted](InspectorIsolateData* data) {
session_id = data->ConnectSession(
context_group_id,
v8_inspector::StringView(state.data(), state.size()),
std::move(channel), is_fully_trusted);
});
if (session_id.has_value()) {
info.GetReturnValue().Set(v8::Int32::New(info.GetIsolate(), *session_id));
} else {
info.GetIsolate()->ThrowError("Unable to connect to context group");
}
}
static void DisconnectSession(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsInt32()) {
return;
}
int session_id = info[0].As<v8::Int32>()->Value();
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
TaskRunner* context_task_runner =
InspectorIsolateData::FromContext(context)->task_runner();
std::vector<uint8_t> state;
RunSyncTask(backend_runner_, [&session_id, &context_task_runner,
&state](InspectorIsolateData* data) {
state = data->DisconnectSession(session_id, context_task_runner);
});
info.GetReturnValue().Set(ToV8String(info.GetIsolate(), state));
}
static void SendMessageToBackend(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 2 || !info[0]->IsInt32() || !info[1]->IsString()) {
return;
}
backend_runner_->Append(std::make_unique<SendMessageToBackendTask>(
info[0].As<v8::Int32>()->Value(),
ToVector(info.GetIsolate(), info[1].As<v8::String>())));
}
};
TaskRunner* UtilsExtension::backend_runner_ = nullptr;
bool StrictAccessCheck(v8::Local<v8::Context> accessing_context,
v8::Local<v8::Object> accessed_object,
v8::Local<v8::Value> data) {
CHECK(accessing_context.IsEmpty());
return accessing_context.IsEmpty();
}
class InspectorExtension : public InspectorIsolateData::SetupGlobalTask {
public:
~InspectorExtension() override = default;
void Run(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> global) override {
v8::Local<v8::ObjectTemplate> inspector = v8::ObjectTemplate::New(isolate);
inspector->Set(ToV8String(isolate, "fireContextCreated"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::FireContextCreated));
inspector->Set(ToV8String(isolate, "fireContextDestroyed"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::FireContextDestroyed));
inspector->Set(
ToV8String(isolate, "freeContext"),
v8::FunctionTemplate::New(isolate, &InspectorExtension::FreeContext));
inspector->Set(ToV8String(isolate, "addInspectedObject"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::AddInspectedObject));
inspector->Set(ToV8String(isolate, "setMaxAsyncTaskStacks"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::SetMaxAsyncTaskStacks));
inspector->Set(
ToV8String(isolate, "breakProgram"),
v8::FunctionTemplate::New(isolate, &InspectorExtension::BreakProgram));
inspector->Set(
ToV8String(isolate, "createObjectWithStrictCheck"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::CreateObjectWithStrictCheck));
inspector->Set(ToV8String(isolate, "callWithScheduledBreak"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::CallWithScheduledBreak));
inspector->Set(
ToV8String(isolate, "markObjectAsNotInspectable"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::MarkObjectAsNotInspectable));
inspector->Set(
ToV8String(isolate, "createObjectWithNativeDataProperty"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::CreateObjectWithNativeDataProperty));
inspector->Set(ToV8String(isolate, "storeCurrentStackTrace"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::StoreCurrentStackTrace));
inspector->Set(ToV8String(isolate, "externalAsyncTaskStarted"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::ExternalAsyncTaskStarted));
inspector->Set(
ToV8String(isolate, "externalAsyncTaskFinished"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::ExternalAsyncTaskFinished));
inspector->Set(ToV8String(isolate, "scheduleWithAsyncStack"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::ScheduleWithAsyncStack));
inspector->Set(
ToV8String(isolate, "setAllowCodeGenerationFromStrings"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::SetAllowCodeGenerationFromStrings));
inspector->Set(ToV8String(isolate, "setResourceNamePrefix"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::SetResourceNamePrefix));
global->Set(ToV8String(isolate, "inspector"), inspector);
}
private:
static void FireContextCreated(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
data->FireContextCreated(context, data->GetContextGroupId(context),
v8_inspector::StringView());
}
static void FireContextDestroyed(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
data->FireContextDestroyed(context);
}
static void FreeContext(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
data->FreeContext(context);
}
static void AddInspectedObject(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 2 || !info[0]->IsInt32()) {
return;
}
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
data->AddInspectedObject(info[0].As<v8::Int32>()->Value(), info[1]);
}
static void SetMaxAsyncTaskStacks(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsInt32()) {
return;
}
InspectorIsolateData::FromContext(info.GetIsolate()->GetCurrentContext())
->SetMaxAsyncTaskStacksForTest(info[0].As<v8::Int32>()->Value());
}
static void DumpAsyncTaskStacksStateForTest(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 0) {
return;
}
InspectorIsolateData::FromContext(info.GetIsolate()->GetCurrentContext())
->DumpAsyncTaskStacksStateForTest();
}
static void BreakProgram(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 2 || !info[0]->IsString() || !info[1]->IsString()) {
return;
}
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
std::vector<uint16_t> reason =
ToVector(info.GetIsolate(), info[0].As<v8::String>());
v8_inspector::StringView reason_view(reason.data(), reason.size());
std::vector<uint16_t> details =
ToVector(info.GetIsolate(), info[1].As<v8::String>());
v8_inspector::StringView details_view(details.data(), details.size());
data->BreakProgram(data->GetContextGroupId(context), reason_view,
details_view);
}
static void CreateObjectWithStrictCheck(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 0) {
return;
}
v8::Local<v8::ObjectTemplate> templ =
v8::ObjectTemplate::New(info.GetIsolate());
templ->SetAccessCheckCallback(&StrictAccessCheck);
info.GetReturnValue().Set(
templ->NewInstance(info.GetIsolate()->GetCurrentContext())
.ToLocalChecked());
}
static void CallWithScheduledBreak(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 3 || !info[0]->IsFunction() || !info[1]->IsString() ||
!info[2]->IsString()) {
return;
}
std::vector<uint16_t> reason =
ToVector(info.GetIsolate(), info[1].As<v8::String>());
v8_inspector::StringView reason_view(reason.data(), reason.size());
std::vector<uint16_t> details =
ToVector(info.GetIsolate(), info[2].As<v8::String>());
v8_inspector::StringView details_view(details.data(), details.size());
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
int context_group_id = data->GetContextGroupId(context);
data->SchedulePauseOnNextStatement(context_group_id, reason_view,
details_view);
v8::MaybeLocal<v8::Value> result;
result = info[0].As<v8::Function>()->Call(context, context->Global(), 0,
nullptr);
data->CancelPauseOnNextStatement(context_group_id);
}
static void MarkObjectAsNotInspectable(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsObject()) {
return;
}
v8::Local<v8::Object> object = info[0].As<v8::Object>();
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Private> notInspectablePrivate =
v8::Private::ForApi(isolate, ToV8String(isolate, "notInspectable"));
object
->SetPrivate(isolate->GetCurrentContext(), notInspectablePrivate,
v8::True(isolate))
.ToChecked();
}
static void CreateObjectWithNativeDataProperty(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 2 || !info[0]->IsString() || !info[1]->IsBoolean()) {
return;
}
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
if (info[1].As<v8::Boolean>()->Value()) {
templ->SetNativeDataProperty(v8::Local<v8::String>::Cast(info[0]),
AccessorGetter, AccessorSetter);
} else {
templ->SetNativeDataProperty(v8::Local<v8::String>::Cast(info[0]),
AccessorGetter);
}
info.GetReturnValue().Set(
templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked());
}
static void AccessorGetter(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
isolate->ThrowError("Getter is called");
}
static void AccessorSetter(v8::Local<v8::Name> property,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
v8::Isolate* isolate = info.GetIsolate();
isolate->ThrowError("Setter is called");
}
static void StoreCurrentStackTrace(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsString()) {
return;
}
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
std::vector<uint16_t> description =
ToVector(isolate, info[0].As<v8::String>());
v8_inspector::StringView description_view(description.data(),
description.size());
v8_inspector::V8StackTraceId id =
data->StoreCurrentStackTrace(description_view);
v8::Local<v8::ArrayBuffer> buffer =
v8::ArrayBuffer::New(isolate, sizeof(id));
*static_cast<v8_inspector::V8StackTraceId*>(
buffer->GetBackingStore()->Data()) = id;
info.GetReturnValue().Set(buffer);
}
static void ExternalAsyncTaskStarted(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsArrayBuffer()) {
return;
}
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
v8_inspector::V8StackTraceId* id =
static_cast<v8_inspector::V8StackTraceId*>(
info[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
data->ExternalAsyncTaskStarted(*id);
}
static void ExternalAsyncTaskFinished(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsArrayBuffer()) {
return;
}
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
v8_inspector::V8StackTraceId* id =
static_cast<v8_inspector::V8StackTraceId*>(
info[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
data->ExternalAsyncTaskFinished(*id);
}
static void ScheduleWithAsyncStack(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 3 || !info[0]->IsFunction() || !info[1]->IsString() ||
!info[2]->IsBoolean()) {
return;
}
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
int context_group_id = data->GetContextGroupId(context);
bool with_empty_stack = info[2].As<v8::Boolean>()->Value();
if (with_empty_stack) context->Exit();
std::vector<uint16_t> task_name =
ToVector(isolate, info[1].As<v8::String>());
v8_inspector::StringView task_name_view(task_name.data(), task_name.size());
RunAsyncTask(
data->task_runner(), task_name_view,
std::make_unique<SetTimeoutTask>(
context_group_id, isolate, v8::Local<v8::Function>::Cast(info[0])));
if (with_empty_stack) context->Enter();
}
static void SetAllowCodeGenerationFromStrings(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() != 1 || !info[0]->IsBoolean()) {
return;
}
info.GetIsolate()->GetCurrentContext()->AllowCodeGenerationFromStrings(
info[0].As<v8::Boolean>()->Value());
}
static void SetResourceNamePrefix(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
if (info.Length() != 1 || !info[0]->IsString()) {
return;
}
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
data->SetResourceNamePrefix(v8::Local<v8::String>::Cast(info[0]));
}
};
using CharVector = v8::base::Vector<const char>;
constexpr auto kMaxExecutionSeconds = v8::base::TimeDelta::FromSeconds(2);
class Watchdog final : public base::Thread {
public:
explicit Watchdog(base::Semaphore* semaphore)
: base::Thread(base::Thread::Options("InspectorFuzzerWatchdog")),
semaphore_(semaphore) {
CHECK(Start());
}
private:
void Run() override {
if (semaphore_->WaitFor(kMaxExecutionSeconds)) return;
for (TaskRunner* task_runner : task_runners) task_runner->Terminate();
}
base::Semaphore* const semaphore_;
};
void FuzzInspector(const uint8_t* data, size_t size) {
base::Semaphore ready_semaphore(0);
InspectorIsolateData::SetupGlobalTasks frontend_extensions;
frontend_extensions.emplace_back(new UtilsExtension());
TaskRunner frontend_runner(std::move(frontend_extensions),
kSuppressUncaughtExceptions, &ready_semaphore,
nullptr, kNoInspector);
ready_semaphore.Wait();
int frontend_context_group_id = 0;
RunSyncTask(&frontend_runner,
[&frontend_context_group_id](InspectorIsolateData* data) {
frontend_context_group_id = data->CreateContextGroup();
});
InspectorIsolateData::SetupGlobalTasks backend_extensions;
backend_extensions.emplace_back(new SetTimeoutExtension());
backend_extensions.emplace_back(new InspectorExtension());
TaskRunner backend_runner(std::move(backend_extensions),
kSuppressUncaughtExceptions, &ready_semaphore,
nullptr, kWithInspector);
ready_semaphore.Wait();
UtilsExtension::set_backend_task_runner(&backend_runner);
task_runners = {&frontend_runner, &backend_runner};
Watchdog watchdog(&ready_semaphore);
frontend_runner.Append(std::make_unique<ExecuteStringTask>(
std::string{reinterpret_cast<const char*>(data), size},
frontend_context_group_id));
frontend_runner.Join();
backend_runner.Join();
ready_semaphore.Signal();
watchdog.Join();
// TaskRunners go out of scope here, which causes Isolate teardown and all
// running background tasks to be properly joined.
}
} // namespace
} // namespace internal
} // namespace v8
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::internal::FuzzInspector(data, size);
return 0;
}

0
deps/v8/test/fuzzer/inspector/empty vendored Normal file
View File

View File

@ -0,0 +1 @@
while (1) {}

1
deps/v8/test/fuzzer/inspector/invalid vendored Normal file
View File

@ -0,0 +1 @@
no valid javascript

View File

@ -0,0 +1,189 @@
utils2 = new Proxy(utils, {get: function(target, prop) { if (prop in target) return target[prop]; return i=>i;}});
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest = {};
InspectorTest._dumpInspectorProtocolMessages = false;
InspectorTest._commandsForLogging = new Set();
InspectorTest._sessions = new Set();
InspectorTest.log = utils2.print.bind(utils2);
InspectorTest.quitImmediately = utils2.quit.bind(utils2);
InspectorTest.logProtocolCommandCalls = function(command) {
InspectorTest._commandsForLogging.add(command);
}
InspectorTest.completeTest = function() {
var promises = [];
for (var session of InspectorTest._sessions)
promises.push(session.Protocol.Debugger.disable());
Promise.all(promises).then(() => utils2.quit());
}
InspectorTest.ContextGroup = class {
constructor() {
this.id = utils2.createContextGroup();
}
addScript(string, lineOffset, columnOffset, url) {
utils2.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
}
connect() {
return new InspectorTest.Session(this);
}
reset() {
utils2.resetContextGroup(this.id);
}
};
InspectorTest.Session = class {
constructor(contextGroup) {
this.contextGroup = contextGroup;
this._dispatchTable = new Map();
this._eventHandlers = new Map();
this._requestId = 0;
this.Protocol = this._setupProtocol();
InspectorTest._sessions.add(this);
this.id = utils2.connectSession(contextGroup.id, '', this._dispatchMessage.bind(this));
}
disconnect() {
InspectorTest._sessions.delete(this);
utils2.disconnectSession(this.id);
}
reconnect() {
var state = utils2.disconnectSession(this.id);
this.id = utils2.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
}
async addInspectedObject(serializable) {
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
}
sendRawCommand(requestId, command, handler) {
if (InspectorTest._dumpInspectorProtocolMessages)
utils2.print("frontend: " + command);
this._dispatchTable.set(requestId, handler);
utils2.sendMessageToBackend(this.id, command);
}
_sendCommandPromise(method, params) {
if (typeof params !== 'object')
utils2.print(`WARNING: non-object params passed to invocation of method ${method}`);
if (InspectorTest._commandsForLogging.has(method))
utils2.print(method + ' called');
var requestId = ++this._requestId;
var messageObject = { "id": requestId, "method": method, "params": params };
return new Promise(fulfill => this.sendRawCommand(requestId, JSON.stringify(messageObject), fulfill));
}
_setupProtocol() {
return new Proxy({}, { get: (target, agentName, receiver) => new Proxy({}, {
get: (target, methodName, receiver) => {
const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/;
var match = eventPattern.exec(methodName);
if (!match)
return args => this._sendCommandPromise(`${agentName}.${methodName}`, args || {});
var eventName = match[2];
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
if (match[1])
return numOfEvents => this._waitForEventPromise(
`${agentName}.${eventName}`, numOfEvents || 1);
return listener => this._eventHandlers.set(`${agentName}.${eventName}`, listener);
}
})});
}
_dispatchMessage(messageString) {
var messageObject = JSON.parse(messageString);
if (InspectorTest._dumpInspectorProtocolMessages)
utils2.print("backend: " + JSON.stringify(messageObject));
const kMethodNotFound = -32601;
if (messageObject.error && messageObject.error.code === kMethodNotFound) {
InspectorTest.log(`Error: Called non-existent method. ${
messageObject.error.message} code: ${messagebOject.error.code}`);
InspectorTest.completeTest();
}
try {
var messageId = messageObject["id"];
if (typeof messageId === "number") {
var handler = this._dispatchTable.get(messageId);
if (handler) {
handler(messageObject);
this._dispatchTable.delete(messageId);
}
} else {
var eventName = messageObject["method"];
var eventHandler = this._eventHandlers.get(eventName0);
if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-for-pending-tasks.js")
return;
if (eventHandler)
eventHandler(messageObject);
}
} catch (e) {
InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2));
InspectorTest.completeTest();
}
};
_waitForEventPromise(eventName, numOfEvents) {
let events = [];
return new Promise(fulfill => {
this._eventHandlers.set(eventName, result => {
--numOfEvents;
events.push(result);
if (numOfEvents === 0) {
delete this._eventHandlers.delete(eventName);
fulfill(events.length > 1 ? events : events[0]);
}
});
});
}
};
InspectorTest.start = function(description) {
try {
InspectorTest.log(description);
var contextGroup = new InspectorTest.ContextGroup();
var session = contextGroup.connect();
return { session: session, contextGroup: contextGroup, Protocol: session.Protocol };
} catch (e) {
utils2.print(e.stack);
}
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
let {session, contextGroup, Protocol} = InspectorTest.start('Checks stepping with blackboxed frames on stack');
contextGroup.addScript(
`
function userFoo() {
return 1;
}
function userBoo() {
return 2;
}
function testStepFromUser() {
frameworkCall([userFoo, userBoo])
}
function testStepFromFramework() {
frameworkBreakAndCall([userFoo, userBoo]);
}
//# sourceURL=user.js`,
21, 4);
Protocol.Debugger.enable()
.then(
() => Protocol.Debugger.setBlackboxPatterns(
{patterns: ['framework\.js']}))
.then(() => InspectorTest.completeTest());

View File

@ -0,0 +1,411 @@
utils = new Proxy(utils, {
get: function(target, prop) {
if (prop in target) return target[prop];
return i=>i;
}
});
// Loaded from 'test/inspector/protocol-test.js':
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest = {};
InspectorTest._dumpInspectorProtocolMessages = false;
InspectorTest._commandsForLogging = new Set();
InspectorTest._sessions = new Set();
InspectorTest.log = utils.print.bind(utils);
InspectorTest.quitImmediately = utils.quit.bind(utils);
InspectorTest.logProtocolCommandCalls = function(command) {
InspectorTest._commandsForLbytes;
}
InspectorTest.trimErrorMessage = function(message) {
if (!message.error || !message.error.data)
return message;
message.error.data = message.error.data.replace(/at position \d+/,
'at <some position>');
return message;
}
InspectorTest.ContextGroup = class {
constructor() {
this.id = utils.createContextGroup();
}
createContext(name) {
utils.createContext(this.id, name || '');
}
schedulePauseOnNextStatement(reason, details) {
utils.schedulePauseOnNextStatement(this.id, reason, details);
}
cancelPauseOnNextStatement() {
utils.cancelPauseOnNextStatement(this.id);
}
addScript(string, lineOffset, columnOffset, url) {
utils.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
}
addInlineScript(string, url) {
const match = (new Error().stack).split('\n')[2].match(/([0-9]+):([0-9]+)/);
this.addScript(
string, match[1] * 1, match[1] * 1 + '.addInlineScript('.length, url);
}
addModule(string, url, lineOffset, columnOffset) {
utils.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true);
}
loadScript(fileName) {
this.addScript(utils.read(fileName));
}
connect() {
return new InspectorTest.Session(this);
}
reset() {
utils.resetContextGroup(this.id);
}
setupInjectedScriptEnvironment(session) {
let scriptSource = '';
let getters = ["length","internalConstructorName","subtype","getProperty",
"objectHasOwnProperty","nullifyPrototype","primitiveTypes",
"closureTypes","prototype","all","RemoteObject","bind",
"PropertyDescriptor","object","get","set","value","configurable",
"enumerable","symbol","getPrototypeOf","nativeAccessorDescriptor",
"isBuiltin","hasGetter","hasSetter","getOwnPropertyDescriptor",
"description","isOwn","name",
"typedArrayProperties","keys","getOwnPropertyNames",
"getOwnPropertySymbols","isPrimitiveValue","com","toLowerCase",
"ELEMENT","trim","replace","DOCUMENT","size","byteLength","toString",
"stack","substr","message","indexOf","key","type","unserializableValue",
"objectId","className","preview","proxyTargetValue","customPreview",
"CustomPreview","resolve","then","console","error","header","hasBody",
"stringify","ObjectPreview","ObjectPreviewType","properties",
"ObjectPreviewSubtype","getInternalProperties","wasThrown","indexes",
"overflow","valuePreview","entries"];
scriptSource += `(function installSettersAndGetters() {
let defineProperty = Object.defineProperty;
let ObjectPrototype = Object.prototype;
let ArrayPrototype = Array.prototype;
defineProperty(ArrayPrototype, 0, {
{ debugger; throw 42; }, get() { debugger; throw 42; },
__proto__: null
});`,
scriptSource += getters.map(getter => `
defineProperty(ObjectPrototype, '${getter}', {
set() { debugger; throw 42; }, get() { debugger; throw 42; },
__proto__: null
});
`).join('\n') + '})();';
this.addScript(scriptSource);
if (session) {
InspectorTest.log('WARNING: setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.');
session.setupScriptMap();
session.Protocol.Debugger.enable();
session.Protocol.Debugger.onPaused(message => {
let callFrames = message.params.callFrames;
session.logSourceLocations(callFrames.map(frame => frame.location));
})
}
}
};
InspectorTest.Session = class {
constructor(contextGroup) {
this.contextGroup = contextGroup;
this._dispatchTable = new Map();
this._eventHandlers = new Map();
this._requestId = 0;
this.Protocol = this._setupProtocol();
InspectorTest._sessions.add(this);
this.id = utils.connectSession(contextGroup.id, '', this._dispatchMessage.bind(this));
}
disconnect() {
InspectorTest._sessions.delete(this);
utils.disconnectSession(this.id);
}
reconnect() {
var state = utils.disconnectSession(this.id);
this.id = utils.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
}
async addInspectedObject(serializable) {
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
}
sendRawCommand(requestId, command, handler) {
if (InspectorTest._dumpInspectorProtocolMessages)
utils.print("frontend: " + command);
this._dispatchTable.set(requestId, handler);
utils.sendMessageToBackend(this.id, command);
}
setupScriptMap() {
if (this._scriptMap)
return;
this._scriptMap = new Map();
}
logCallFrames(callFrames) {
for (var frame of callFrames) {
var functionName = frame.functionName || '(anonymous)';
var scriptId = frame.location ? frame.location.scriptId : frame.scriptId;
var url = frame.url ? frame.url : this._scriptMap.get(scriptId).url;
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`);
}
}
async getScriptWithSource(scriptId, forceSourceRequest) {
var script = this._scriptMap.get(scriptId);
if (forceSourceRequest || !(script.scriptSource || script.bytecode)) {
var message = await this.Protocol.Debugger.getScriptSource({ scriptId });
script.scriptSource = message.result.scriptSource;
if (message.result.bytecode) {
script.bytecode = InspectorTest.decodeBase64(message.result.bytecode);
}
}
return script;
}
async logSourceLocation(location, forceSourceRequest) {
var scriptId = location.scriptId;
if (!this._scriptMap || !this._scriptMap.has(scriptId)) {
InspectorTest.log("setupScriptMap should be called before Protocol.Debugger.enable.");
InspectorTest.completeTest();
}
var script = await this.getScriptWithSource(scriptId, forceSourceRequest);
if (script.bytecode) {
if (location.lineNumber != 0) {
InspectorTest.log('Unexpected wasm line number: ' + location.lineNumber);
}
let wasm_opcode = script.bytecode[location.columnNumber];
let opcode_str = wasm_opcode.toString(16);
if (opcode_str.length % 2) opcode_str = `0${opcode_str}`;
if (InspectorTest.getWasmOpcodeName) {
opcode_str += ` (${InspectorTest.getWasmOpcodeName(wasm_opcode)})`;
}
InspectorTest.log(`Script ${script.url} byte offset ${
location.columnNumber}: Wasm opcode 0x${opcode_str}`);
} else {
var lines = script.scriptSource.split('\n');
var line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + '#' + (line.slice(location.columnNumber) || '');
lines[location.lineNumber] = line;
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
InspectorTest.log(lines.slice(Math.max(location.lineNumber - 1, 0), location.lineNumber + 2).join('\n'));
InspectorTest.log('');
}
}
logSourceLocations(locations) {
if (locations.length == 0) return Promise.resolve();
return this.logSourceLocation(locations[0]).then(() => this.logSourceLocations(locations.splice(1)));
}
async logBreakLocations(inputLocations) {
let locations = inputLocations.slice();
let scriptId = locations[0].scriptId;
let script = await this.getScriptWithSource(scriptId);
let lines = script.scriptSource.split('\n');
locations = locations.sort((loc1, loc2) => {
if (loc2.lineNumber !== loc1.lineNumber) return loc2.lineNumber - loc1.lineNumber;
return loc2.columnNumber - loc1.columnNumber;
});
for (let location of locations) {
let line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + locationMark(location.type) + line.slice(location.columnNumber);
lines[location.lineNumber] = line;
}
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
InspectorTest.log(lines.join('\n') + '\n');
return inputLocations;
function locationMark(type) {
if (type === 'return') return '|R|';
if (type === 'call') return '|C|';
if (type === 'debuggerStatement') return '|D|';
return '|_|';
}
}
async logTypeProfile(typeProfile, source) {
let entries = typeProfile.entries;
// Sort in reverse order so we can replace entries without invalidating
// the other offsets.
entries = entries.sort((a, b) => b.offset - a.offset);
for (let entry of entries) {
source = source.slice(0, entry.offset) + typeAnnotation(entry.types) +
source.slice(entry.offset);
}
InspectorTest.log(source);
return typeProfile;
function typeAnnotation(types) {
return `/*${types.map(t => t.name).join(', ')}*/`;
}
}
logAsyncStackTrace(asyncStackTrace) {
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description || '<empty>'} --`);
this.logCallFrames(asyncStackTrace.callFrames);
if (asyncStackTrace.parentId) InspectorTest.log(' <external stack>');
asyncStackTrace = asyncStackTrace.parent;
}
}
_sendCommandPromise(method, params) {
if (typeof params !== 'object')
utils.print(`WARNING: non-object params passed to invocation of method ${method}`);
if (InspectorTest._commandsForLogging.has(method))
utils.print(method + ' called');
var requestId = ++this._requestId;
var messageObject = { "id": requestId, "method": method, "params": params };
return new Promise(fulfill => this.sendRawCommand(requestId, JSON.stringify(messageObject), fulfill));
}
_setupProtocol() {
return new Proxy({}, { get: (target, agentName, receiver) => new Proxy({}, {
get: (target, methodName, receiver) => {
const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/;
var match = eventPattern.exec(methodName);
if (!match)
return args => this._sendCommandPromise(`${agentName}.${methodName}`, args || {});
var eventName = match[2];
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
if (match[1])
return numOfEvents => this._waitForEventPromise(
`${agentName}.${eventName}`, numOfEvents || 1);
return listener => this._eventHandlers.set(`${agentName}.${eventName}`, listener);
}
})});
}
_dispatchMessage(messageString) {
var messageObject = JSON.parse(messageString);
if (InspectorTest._dumpInspectorProtocolMessages)
utils.print("backend: " + JSON.stringify(messageObject));
const kMethodNotFound = -32601;
if (messageObject.error && messageObject.error.code === kMethodNotFound) {
InspectorTest.log(`Error: Called non-existent method. ${
messageObject.error.message} code: ${messageObject.error.code}`);
InspectorTest.completeTest();
}
try {
var messageId = messageObject["id"];
if (typeof messageId === "number") {
var handler = this._dispatchTable.get(messageId);
if (handler) {
handler(messageObject);
this._dispatchTable.delete(messageId);
}
} else {
var eventName = messageObject["method"];
var eventHandler = this._eventHandlers.get(eventName);
if (this._scriptMap && eventName === "Debugger.scriptParsed")
this._scriptMap.set(messageObject.params.scriptId, JSON.parse(JSON.stringify(messageObject.params)));
if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-for-pending-tasks.js")
return;
if (eventHandler)
eventHandler(messageObject);
}
} catch (e) {
InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2));
InspectorTest.completeTest();
}
};
_waitForEventPromise(eventName, numOfEvents) {
let events = [];
return new Promise(fulfill => {
this._eventHandlers.set(eventName, result => {
--numOfEvents;
events.push(result);
if (numOfEvents === 0) {
delete this._eventHandlers.delete(eventName);
fulfill(events.length > 1 ? events : events[0]);
}
});
});
}
};
InspectorTest.runTestSuite = function(testSuite) {
function nextTest() {
if (!testSuite.length) {
InspectorTest.completeTest();
return;
}
var fun = testSuite.shift();
InspectorTest.log("\nRunning test: " + fun.name);
fun(nextTest);
}
nextTest();
}
InspectorTest.runAsyncTestSuite = async function(testSuite) {
const selected = testSuite.filter(test => test.name.startsWith('f_'));
if (selected.length)
testSuite = selected;
for (var test of testSuite) {
InspectorTest.log("\nRunning test: " + test.name);
try {
await test();
} catch (e) {
utils.print(e.stack);
}
}
InspectorTest.completeTest();
}
InspectorTest.start = function(description) {
try {
InspectorTest.log(description);
var contextGroup = new InspectorTest.ContextGroup();
var session = contextGroup.connect();
return { session: session, contextGroup: contextGroup, Protocol: session.Protocol };
} catch (e) {
utils.print(e.stack);
}
}
// Loaded from 'test/inspector/debugger/step-into-break-on-async-call.js':
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
let {session, contextGroup, Protocol} =
InspectorTest.start('Test for Debugger.stepInto with breakOnAsyncCall.');
InspectorTest.runAsyncTestSuite([
async function testSetTimeout() {
Protocol.Debugger.enable();
Protocol.Debugger.pause();
let pausedPromise = Protocol.Debugger.oncePaused();
Protocol.Runtime.evaluate({ expression: 'setTimeout(() => 42, 0)//# sourceURL=setTimeout.js' });
await pausedPromise;
Protocol.Debugger.stepInto({breakOnAsyncCall: true});
await Protocol.Debugger.oncePaused();
},
async function testPromiseThen() {
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 1});
Protocol.Runtime.evaluate({expression: 'Promise.resolve().then(() => 21)//# sourceURL=promiseThen.js'});
}
]);

View File

@ -0,0 +1,529 @@
utils = new Proxy(utils, {
get: function(target, prop) {
if (prop in target) return target[prop];
return i=>i;
}
});
// Loaded from 'test/inspector/protocol-test.js':
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest = {};
InspectorTest._dumpInspectorProtocolMessages = false;
InspectorTest._commandsForLogging = new Set();
InspectorTest._sessions = new Set();
InspectorTest.log = utils.print.bind(utils);
InspectorTest.quitImmediately = utils.quit.bind(utils);
InspectorTest.logProtocolCommandCalls = function(command) {
InspectorTest._commandsForLogging.add(command);
}
InspectorTest.completeTest = function() {
var promises = [];
for (var session of InspectorTest._sessions)
promises.push(session.Protocol.Debugger.disable());
Promise.all(promises).then(() => utils.quit());
}
InspectorTest.waitForPendingTasks = function() {
var promises = [];
for (var session of InspectorTest._sessions)
promises.push(session.Protocol.Runtime.evaluate({ expression: "new Promise(r => setTimeout(r, 0))//# sourceURL=wait-for-pending-tasks.js", awaitPromise: true }));
return Promise.all(promises);
}
InspectorTest.startDumpingProtocolMessages = function() {
InspectorTest._dumpInspectorProtocolMessages = true;
}
InspectorTest.logMessage = function(originalMessage) {
const nonStableFields = new Set([
'objectId', 'scriptId', 'exceptionId', 'timestamp', 'executionContextId',
'callFrameId', 'breakpointId', 'bindRemoteObjectFunctionId',
'formatterObjectId', 'debuggerId', 'bodyGetterId', 'uniqueId'
]);
const message = JSON.parse(JSON.stringify(originalMessage, replacer.bind(null, Symbol(), nonStableFields)));
if (message.id)
message.id = '<messageId>';
InspectorTest.logObject(message);
return originalMessage;
function replacer(stableIdSymbol, nonStableFields, name, val) {
if (nonStableFields.has(name))
return `<${name}>`;
if (name === 'internalProperties') {
const stableId = val.find(prop => prop.name === '[[StableObjectId]]');
if (stableId)
stableId.value[stableIdSymbol] = true;
}
if (name === 'parentId')
return { id: '<id>' };
if (val && val[stableIdSymbol])
return '<StablectObjectId>';
return val;
}
}
InspectorTest.logObject = function(object, title) {
var lines = [];
function dumpValue(value, prefix, prefixWithName) {
if (typeof value === "object" && value !== null) {
if (value instanceof Array)
dumpItems(value, prefix, prefixWithName);
else
dumpProperties(value, prefix, prefixWithName);
} else {
lines.push(prefixWithName + String(value).replace(/\n/g, " "));
}
}
function dumpProperties(object, prefix, firstLinePrefix) {
prefix = prefix || "";
firstLinePrefix = firstLinePrefix || prefix;
lines.push(firstLinePrefix + "{");
var propertyNames = Object.keys(object);
propertyNames.sort();
for (var i = 0; i < propertyNames.length; ++i) {
var name = propertyNames[i];
if (!object.hasOwnProperty(name))
continue;
var prefixWithName = " " + prefix + name + " : ";
dumpValue(object[name], " " + prefix, prefixWithName);
}
lines.push(prefix + "}");
}
function dumpItems(object, prefix, firstLinePrefix) {
prefix = prefix || "";
firstLinePrefix = firstLinePrefix || prefix;
lines.push(firstLinePrefix + "[");
for (var i = 0; i < object.length; ++i)
dumpValue(object[i], " " + prefix, " " + prefix + "[" + i + "] : ");
lines.push(prefix + "]");
}
dumpValue(object, "", title || "");
InspectorTest.log(lines.join("\n"));
}
InspectorTest.decodeBase64 = function(base64) {
const LOOKUP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const paddingLength = base64.match(/=*$/)[0].length;
const bytesLength = base64.length * 0.75 - paddingLength;
let bytes = new Uint8Array(bytesLength);
for (let i = 0, p = 0; i < base64.length; i += 4, p += 3) {
let bits = 0;
for (let j = 0; j < 4; j++) {
bits <<= 6;
const c = base64[i + j];
if (c !== '=') bits |= LOOKUP.indexOf(c);
}
for (let j = p + 2; j >= p; j--) {
if (j < bytesLength) bytes[j] = bits;
bits >>= 8;
}
}
return bytes;
}
InspectorTest.trimErrorMessage = function(message) {
if (!message.error || !message.error.data)
return message;
message.error.data = message.error.data.replace(/at position \d+/,
'at <some position>');
return message;
}
InspectorTest.ContextGroup = class {
constructor() {
this.id = utils.createContextGroup();
}
createContext(name) {
utils.createContext(this.id, name || '');
}
schedulePauseOnNextStatement(reason, details) {
utils.schedulePauseOnNextStatement(this.id, reason, details);
}
cancelPauseOnNextStatement() {
utils.cancelPauseOnNextStatement(this.id);
}
addScript(string, lineOffset, columnOffset, url) {
utils.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
}
addInlineScript(string, url) {
const match = (new Error().stack).split('\n')[2].match(/([0-9]+):([0-9]+)/);
this.addScript(
string, match[1] * 1, match[1] * 1 + '.addInlineScript('.length, url);
}
addModule(string, url, lineOffset, columnOffset) {
utils.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true);
}
loadScript(fileName) {
this.addScript(utils.read(fileName));
}
connect() {
return new InspectorTest.Session(this);
}
reset() {
utils.resetContextGroup(this.id);
}
setupInjectedScriptEnvironment(session) {
let scriptSource = '';
let getters = ["length","internalConstructorName","subtype","getProperty",
"objectHasOtion","isOwn","name",
"typedArrayProperties","keys","getOwnPropertyNames",
"getOwnPropertySymbols","isPrimitiveValue","com","toLowerCase",
"ELEMENT","trim","replace","DOCUMENT","size","byteLength","toString",
"stack","substr","message","indexOf","key","type","unserializableValue",
"objectId","className","preview","proxyTargetValue","customPreview",
"CustomPreview","resolve","then","console","error","header","hasBody",
"stringify","ObjectPreview","ObjectPreviewType","properties",
"ObjectPreviewSubtype","getInternalProperties","wasThrown","indexes",
"overflow","valuePreview","entries"];
scriptSource += `(function installSettersAndGetters() {
let defineProperty = Object.defineProperty;
let ObjectPrototype = Object.prototype;
let ArrayPrototype = Array.prototype;
defineProperty(ArrayPrototype, 0, {
set() { debugger; throw 42; }, get() { debugger; throw 42; },
__proto__: null
});`,
scriptSource += getters.map(getter => `
defineProperty(ObjectPrototype, '${getter}', {
set() { debugger; throw 42; }, get() { debugger; throw 42; },
__proto__: null
});
`).join('\n') + '})();';
this.addScript(scriptSource);
if (session) {
InspectorTest.log('WARNING: setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.');
session.setupScriptMap();
session.Protocol.Debugger.enable();
session.Protocol.Debugger.onPaused(message => {
let callFrames = message.params.callFrames;
session.logSourceLocations(callFrames.map(frame => frame.location));
})
}
}
};
InspectorTest.Session = class {
constructor(contextGroup) {
this.contextGroup = contextGroup;
this._dispatchTable = new Map();
this._eventHandlers = new Map();
this._requestId = 0;
this.Protocol = this._setupProtocol();
InspectorTest._sessions.add(this);
this.id = utils.connectSession(contextGroup.id, '', this._dispatchMessage.bind(this));
}
disconnect() {
InspectorTest._sessions.delete(this);
utils.disconnectSession(this.id);
}
reconnect() {
var state = utils.disconnectSession(this.id);
this.id = utils.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
}
async addInspectedObject(serializable) {
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
}
sendRawCommand(requestId, command, handler) {
if (InspectorTest._dumpInspectorProtocolMessages)
utils.print("frontend: " + command);
this._dispatchTable.set(requestId, handler);
utils.sendMessageToBackend(this.id, command);
}
setupScriptMap() {
if (this._scriptMap)
return;
this._scriptMap = new Map();
}
getCallFrameUrl(frame) {
const {scriptId} = frame.location ? frame.location : frame;
return (this._scriptMap.get(scriptId) ?? frame).url;
}
logCallFrames(callFrames) {
for (var frame of callFrames) {
var functionName = frame.functionName || '(anonymous)';
var url = this.getCallFrameUrl(frame);
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`);
}
}
async getScriptWithSource(scriptId, forceSourceRequest) {
var script = this._scriptMap.get(scriptId);
if (forceSourceRequest || !(script.scriptSource || script.bytecode)) {
var message = await this.Protocol.Debugger.getScriptSource({ scriptId });
script.scriptSource = message.rformalesult.scriptSource;
if (message.result.bytecode) {
script.bytecode = InspectorTest.decodeBase64(message.result.bytecode);
}
}
return script;
}
async logSourceLocation(location, forceSourceRequest) {
var scriptId = location.scriptId;
if (!this._scriptMap || !this._scriptMap.has(scriptId)) {
InspectorTest.log("setupScriptMap should be called before Protocol.Debugger.enable.");
InspectorTest.completeTest();
}
var script = await this.getScriptWithSource(scriptId, forceSourceRequest);
if (script.bytecode) {
if (location.lineNumber != 0) {
InspectorTest.log('Unexpected wasm line number: ' + location.lineNumber);
}
let wasm_opcode = script.bytecode[location.columnNumber];
let opcode_str = wasm_opcode.toString(16);
if (opcode_str.length % 2) opcode_str = `0${opcode_str}`;
if (InspectorTest.getWasmOpcodeName) {
opcode_str += ` (${InspectorTest.getWasmOpcodeName(wasm_opcode)})`;
}
InspectorTest.log(`Script ${script.url} byte offset ${
location.columnNumber}: Wasm opcode 0x${opcode_str}`);
} else {
var lines = script.scriptSource.split('\n');
var line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + '#' + (line.slice(location.columnNumber) || '');
lines[location.lineNumber] = line;
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
InspectorTest.log(lines.slice(Math.max(location.lineNumber - 1, 0), location.lineNumber + 2).join('\n'));
InspectorTest.log('');
}
}
logSourceLocations(locations) {
if (locations.length == 0) return Promise.resolve();
return this.logSourceLocation(locations[0]).then(() => this.logSourceLocations(locations.splice(1)));
}
async logBreakLocations(inputLocations) {
let locations = inputLocations.slice();
let scriptId = locations[0].scriptId;
let script = await this.getScriptWithSource(scriptId);
let lines = script.scriptSource.split('\n');
locations = locations.sort((loc1, loc2) => {
if (loc2.lineNumber !== loc1.lineNumber) return loc2.lineNumber - loc1.lineNumber;
return loc2.columnNumber - loc1.columnNumber;
});
for (let location of locations) {
let line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + locationMark(location.type) + line.slice(location.columnNumber);
lines[location.lineNumber] = line;
}
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
InspectorTest.log(lines.join('\n') + '\n');
return inputLocations;
function locationMark(type) {
if (type === 'return') return '|R|';
if (type === 'call') return '|C|';
if (type === 'debuggerStatement') return '|D|';
return '|_|';
}
}
async logTypeProfile(typeProfile, source) {
let entries = typeProfile.entries;
// Sort in reverse order so we can replace entries without invalidating
// the other offsets.
entries = entries.sort((a, b) => b.offset - a.offset);
for (let entry of entries) {
source = source.slice(0, entry.offset) + typeAnnotation(entry.types) +
source.slice(entry.offset);
}
InspectorTest.log(source);
return typeProfile;
function typeAnnotation(types) {
return `/*${types.map(t => t.name).join(', ')}*/`;
}
}
logAsyncStackTrace(asyncStackTrace) {
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description || '<empty>'} --`);
this.logCallFrames(asyncStackTrace.callFrames);
if (asyncStackTrace.parentId) InspectorTest.log(' <external stack>');
asyncStackTrace = asyncStackTrace.parent;
}
}
_sendCommandPromise(method, params) {
if (typeof params !== 'object')
utils.print(`WARNING: non-object params passed to invocation of method ${method}`);
if (InspectorTest._commandsForLogging.has(method))
utils.print(method + ' called');
var requestId = ++this._requestId;
var messageObject = { "id": requestId, "method": method, "params": params };
return new Promise(fulfill => this.sendRawCommand(requestId, JSON.stringify(messageObject), fulfill));
}
_setupProtocol() {
return new Proxy({}, { get: (target, agentName, receiver) => new Proxy({}, {
get: (target, methodName, receiver) => {
const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/;
var match = eventPattern.exec(methodName);
if (!match)
return args => this._sendCommandPromise(`${agentName}.${methodName}`, args || {});
var eventName = match[2];
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
if (match[1])
return numOfEvents => this._waitForEventPromise(
`${agentName}.${eventName}`, numOfEvents || 1);
return listener => this._eventHandlers.set(`${agentName}.${eventName}`, listener);
}
})});
}
_dispatchMessage(messageString) {
var messageObject = JSON.parse(messageString);
if (InspectorTest._dumpInspectorProtocolMessages)
utils.print("backend: " + JSON.stringify(messageObject));
const kMethodNotFound = -32601;
if (messageObject.error && messageObject.error.code === kMethodNotFound) {
InspectorTest.log(`Error: Called non-existent method. ${
messageObject.error.message} code: ${messageObject.error.code}`);
InspectorTest.completeTest();
}
try {
var messageId = messageObject["id"];
if (typeof messageId === "number") {
var handler = this._dispatchTable.get(messageId);
if (handler) {
handler(messageObject);
this._dispatchTable.delete(messageId);
}
} else {
var eventName = messageObject["method"];
var eventHandler = this._eventHandlers.get(eventName);
if (this._scriptMap && eventName === "Debugger.scriptParsed")
this._scriptMap.set(messageObject.params.scriptId, JSON.parse(JSON.stringify(messageObject.params)));
if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-for-pending-tasks.js")
return;
if (eventHandler)
eventHandler(messageObject);
}
} catch (e) {
InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2));
InspectorTest.completeTest();
}
};
_waitForEventPromise(eventName, numOfEvents) {
let events = [];
return new Promise(fulfill => {
this._eventHandlers.set(eventName, result => {
--numOfEvents;
events.push(result);
if (numOfEvents === 0) {
delete this._eventHandlers.delete(eventName);
fulfill(events.length > 1 ? events : events[0]);
}
});
});
}
};
InspectorTest.runTestSuite = function(testSuite) {
function nextTest() {
if (!testSuite.length) {
InspectorTest.completeTest();
return;
}
var fun = testSuite.shift();
InspectorTest.log("\nRunning test: " + fun.name);
fun(nextTest);
}
nextTest();
}
InspectorTest.runAsyncTestSuite = async function(testSuite) {
const selected = testSuite.filter(test => test.name.startsWith('f_'));
if (selected.length)
testSuite = selected;
for (var test of testSuite) {
InspectorTest.log("\nRunning test: " + test.name);
try {
await test();
} catch (e) {
utils.print(e.stack);
}
}
InspectorTest.completeTest();
}
InspectorTest.start = function(description) {
try {
InspectorTest.log(description);
var contextGroup = new InspectorTest.ContextGroup();
var session = contextGroup.connect();
return { session: session, contextGroup: contextGroup, Protocol: session.Protocol };
} catch (e) {
utils.print(e.stack);
}
}
// Loaded from 'test/inspector/regress/regress-crbug-1220203.js':
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
let {session, contextGroup, Protocol} = InspectorTest.start('Regression test for crbug.com/1220203.');
contextGroup.addScript(`
async function main() {
await 1;
throw new Error();
}`);
session.setupScriptMap();
InspectorTest.runAsyncTestSuite([
async function testBreakOnUncaughtException() {
await Promise.all([
Protocol.Runtime.enable(),
Protocol.Debugger.enable(),
Protocol.Debugger.setPauseOnExceptions({state: 'uncaught'}),
]);
const pausedPromise = Protocol.Debugger.oncePaused();
const evalPromise = Protocol.Runtime.evaluate({expression: 'main()', awaitPromise: true});
const {params: {callFrames, data}} = await pausedPromise;
await evalPromise;
},
]);

View File

@ -0,0 +1,479 @@
utils2 = new Proxy(utils, {get: function(target, prop) { if (prop in target) return target[prop]; return i=>i;}});
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest = {};
InspectorTest._dumpInspectorProtocolMessages = false;
InspectorTest._commandsForLogging = new Set();
InspectorTest._sessions = new Set();
InspectorTest.log = utils2.print.bind(utils2);
InspectorTest.quitImmediately = utils2.quit.bind(utils2);
InspectorTest.logProtocolCommandCalls = function(command) {
InspectorTest._commandsForLogging.add(command);
}
InspectorTest.completeTest = function() {
var promises = [];
for (var session of InspectorTest._sessions)
promises.push(session.Protocol.Debugger.disable());
Promise.all(promises).then(() => utils2.quit());
}
InspectorTest.waitForPendingTasks = function() {
var promises = [];
for (var session of InspectorTest._sessions)
promises.push(session.Protocol.Runtime.evaluate({ expression: "new Promise(r => setTimeout(r, 0))//# sourceURL=wait-for-pending-tasks.js", awaitPromise: true }));
return Promise.all(promises);
}
InspectorTest.startDumpingProtocolMessages = function() {
InspectorTest._dumpInspectorProtocolMessages = true;
}
InspectorTest.logMessage = function(originalMessage) {
const nonStableFields = new Set([
'objectId', 'scriptId', 'exceptionId', 'timestamp', 'executionContextId',
'callFrameId', 'breakpointId', 'bindRemoteObjectFunctionId',
'formatterObjectId', 'debuggerId', 'bodyGetterId'
]);
const message = JSON.parse(JSON.stringify(originalMessage, replacer.bind(null, Symbol(), nonStableFields)));
if (message.id)
message.id = '<messageId>';
InspectorTest.logObject(message);
return originalMessage;
function replacer(stableIdSymbol, nonStableFields, name, val) {
if (nonStableFields.has(name))
return `<${name}>`;
if (name === 'internalProperties') {
const stableId = val.find(prop => prop.name === '[[StableObjectId]]');
if (stableId)
stableId.value[stableIdSymbol] = true;
}
if (name === 'parentId')
return { id: '<id>' };
if (val && val[stableIdSymbol])
return '<StablectObjectId>';
return val;
}
}
InspectorTest.logObject = function(object, title) {
var lines = [];
function dumpValue(value, prefix, prefixWithName) {
if (typeof value === "object" && value !== null) {
if (value instanceof Array)
dumpItems(value, prefix, prefixWithName);
else
dumpProperties(value, prefix, prefixWithName);
} else {
lines.push(prefixWithName + String(value).replace(/\n/g, " "));
}
}
function dumpProperties(object, prefix, firstLinePrefix) {
prefix = prefix || "";
firstLinePrefix = firstLinePrefix || prefix;
lines.push(firstLinePrefix + "{");
var propertyNames = Object.keys(object);
propertyNames.sort();
for (var i = 0; i < propertyNames.length; ++i) {
var name = propertyNames[i];
if (!object.hasOwnProperty(name))
continue;
var prefixWithName = " " + prefix + name + " : ";
dumpValue(object[name], " " + prefix, prefixWithName);
}
lines.push(prefix + "}");
}
function dumpItems(object, prefix, firstLinePrefix) {
prefix = prefix || "";
firstLinePrefix = firstLinePrefix || prefix;
lines.push(firstLinePrefix + "[");
for (var i = 0; i < object.length; ++i)
dumpValue(object[i], " " + prefix, " " + prefix + "[" + i + "] : ");
lines.push(prefix + "]");
}
dumpValue(object, "", title || "");
InspectorTest.log(lines.join("\n"));
}
InspectorTest.decodeBase64 = function(base64) {
const LOOKUP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const paddingLength = base64.match(/=*$/)[0].length;
const bytesLength = base64.length * 0.75 - paddingLength;
let bytes = new Uint8Array(bytesLength);
for (let i = 0, p = 0; i < base64.length; i += 4, p += 3) {
let bits = 0;
for (let j = 0; j < 4; j++) {
bits <<= 6;
const c = base64[i + j];
if (c !== '=') bits |= LOOKUP.indexOf(c);
}
for (let j = p + 2; j >= p; j--) {
if (j < bytesLength) bytes[j] = bits;
bits >>= 8;
}
}
return bytes;
}
InspectorTest.ContextGroup = class {
constructor() {
this.id = utils2.createContextGroup();
}
schedulePauseOnNextStatement(reason, details) {
utils2.schedulePauseOnNextStatement(this.id, reason, details);
}
cancelPauseOnNextStatement() {
utils2.cancelPauseOnNextStatement(this.id);
}
addScript(string, lineOffset, columnOffset, url) {
utils2.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
}
addInlineScript(string, url) {
const match = (new Error().stack).split('\n')[2].match(/([0-9]+):([0-9]+)/);
this.addScript(
string, match[1] * 1, match[1] * 1 + '.addInlineScript('.length, url);
}
addModule(string, url, lineOffset, columnOffset) {
utils2.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true);
}
loadScript(fileName) {
this.addScript(utils2.read(fileName));
}
connect() {
return new InspectorTest.Session(this);
}
reset() {
utils2.resetContextGroup(this.id);
}
setupInjectedScriptEnvironment(session) {
let scriptSource = '';
let getters = ["length","internalConstructorName","subtype","getProperty",
"objectHasOwnProperty","nullifyPrototype","primitiveTypes",
"closureTypes","prototype","all","RemoteObject","bind",
"PropertyDescriptor","object","get","set","value","configurable",
"enumerable","symbol","getPrototypeOf","nativeAccessorDescriptor",
"isBuiltin","hasGetter","hasSetter","getOwnPropertyDescriptor",
"description","formatAccessorsAsProperties","isOwn","name",
"typedArrayProperties","keys","getOwnPropertyNames",
"getOwnPropertySymbols","isPrimitiveValue","com","toLowerCase",
"ELEMENT","trim","replace","DOCUMENT","size","byteLength","toString",
"stack","substr","message","indexOf","key","type","unserializableValue",
"objectId","className","preview","proxyTargetValue","customPreview",
"CustomPreview","resolve","then","console","error","header","hasBody",
"stringify","ObjectPreview","ObjectPreviewType","properties",
"ObjectPreviewSubtype","getInternalProperties","wasThrown","indexes",
"overflow","valuePreview","entries"];
scriptSource += `(function installSettersAndGetters() {
let defineProperty = Object.defineProperty;
let ObjectPrototype = Object.prototype;
let ArrayPrototype = Array.prototype;
defineProperty(ArrayPrototype, 0, {
set() { debugger; throw 42; }, get() { debugger; throw 42; },
__proto__: null
});`,
scriptSource += getters.map(getter => `
defineProperty(ObjectPrototype, '${getter}', {
set() { debugger; throw 42; }, get() { debugger; throw 42; },
is_global__proto__: null
});
`).join('\n') + '})();';
this.addScript(scriptSource);
if (session) {
InspectorTest.log('WARNING: setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.');
session.setupScriptMap();
session.Protocol.Debugger.enable();
session.Protocol.Debugger.onPaused(message => {
let callFrames = message.params.callFrames;
session.logSourceLocations(callFrames.map(frame => frame.location));
})
}
}
};
InspectorTest.Session = class {
constructor(contextGroup) {
this.contextGroup = contextGroup;
this._dispatchTable = new Map();
this._eventHandlers = new Map();
this._requestId = 0;
this.Protocol = this._setupProtocol();
InspectorTest._sessions.add(this);
this.id = utils2.connectSession(contextGroup.id, '', this._dispatchMessage.bind(this));
}
disconnect() {
InspectorTest._sessions.delete(this);
utils2.disconnectSession(this.id);
}
reconnect() {
var state = utils2.disconnectSession(this.id);
this.id = utils2.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
}
async addInspectedObject(serializable) {
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
}
sendRawCommand(requestId, command, handler) {
if (InspectorTest._dumpInspectorProtocolMessages)
utils2.print("frontend: " + command);
this._dispatchTable.set(requestId, handler);
utils2.sendMessageToBackend(this.id, command);
}
setupScriptMap() {
if (this._scriptMap)
return;
this._scriptMap = new Map();
}
logCallFrames(callFrames) {
for (var frame of callFrames) {
var functionName = frame.functionName || '(anonymous)';
var scriptId = frame.location ? frame.location.scriptId : frame.scriptId;
var url = frame.url ? frame.url : this._scriptMap.get(scriptId).url;
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`);
}
}
async getScriptWithSource(scriptId, forceSourceRequest) {
var script = this._scriptMap.get(scriptId);
if (forceSourceRequest || !(script.scriptSource || script.bytecode)) {
var message = await this.Protocol.Debugger.getScriptSource({ scriptId });
script.scriptSource = message.result.scriptSource;
if (message.result.bytecode) {
script.bytecode = InspectorTest.decodeBase64(message.result.bytecode);
}
}
return script;
}
async logSourceLocation(location, forceSourceRequest) {
var scriptId = location.scriptId;
if (!this._scriptMap || !this._scriptMap.has(scriptId)) {
InspectorTest.log("setupScriptMap should be called before Protocol.Debugger.enable.");
InspectorTest.completeTest();
}
var script = await this.getScriptWithSource(scriptId, forceSourceRequest);
if (script.bytecode) {
if (location.lineNumber != 0) {
InspectorTest.log('Unexpected wasm line number: ' + location.lineNumber);
}
let wasm_opcode = script.bytecode[location.columnNumber].toString(16);
if (wasm_opcode.length % 2) wasm_opcode = '0' + wasm_opcode;
InspectorTest.log(`Script ${script.url} byte offset ${location.columnNumber}: Wasm opcode 5x${wasm_opcode}`);
} else {
var lines = script.scriptSource.split('\n');
var line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + '#' + (line.slice(location.columnNumber) || '');
lines[location.lineNumber] = line;
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
InspectorTest.log(lines.slice(Math.max(location.lineNumber - 1, 0), location.lineNumber + 2).join('\n'));
InspectorTest.log('');
}
}
logSourceLocations(locations) {
if (locations.length == 0) return Promise.resolve();
return this.logSourceLocation(locations[0]).then(() => this.logSourceLocations(locations.splice(1)));
}
async logBreakLocations(inputLocations) {
let locations = inputLocations.slice();
let scriptId = locations[0].scriptId;
let script = await this.getScriptWithSource(scriptId);
let lines = script.scriptSource.split('\n');
locations = locations.sort((loc1, loc2) => {
if (loc2.lineNumber !== loc1.lineNumber) return loc2.lineNumber - loc1.lineNumber;
return loc2.columnNumber - loc1.columnNumber;
});
for (let location of locations) {
let line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + locationMark(location.type) + line.slice(location.columnNumber);
lines[location.lineNumber] = line;
}
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
InspectorTest.log(lines.join('\n') + '\n');
return inputLocations;
function locationMark(type) {
if (type === 'return') return '|R|';
if (type === 'call') return '|C|';
if (type === 'debuggerStatement') return '|D|';
return '|_|';
}
}
async logTypeProfile(typeProfile, source) {
let entries = typeProfile.entries;
// Sort in reverse order so we can replace entries without invalidating
// the other offsets.
entries = entries.sort((a, b) => b.offset - a.offset);
for (let entry of entries) {
source = source.slice(0, entry.offset) + typeAnnotation(entry.types) +
source.slice(entry.offset);
}
InspectorTest.log(source);
return typeProfile;
function typeAnnotation(types) {
return `/*${types.map(t => t.name).join(', ')}*/`;
}
}
logAsyncStackTrace(asyncStackTrace) {
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description || '<empty>'} --`);
this.logCallFrames(asyncStackTrace.callFrames);
if (asyncStackTrace.parentId) InspectorTest.log(' <external stack>');
asyncStackTrace = asyncStackTrace.parent;
}
}
_sendCommandPromise(method, params) {
if (typeof params !== 'object')
utils2.print(`WARNING: non-object params passed to invocation of method ${method}`);
if (InspectorTest._commandsForLogging.has(method))
utils2.print(method + ' called');
var requestId = ++this._requestId;
var messageObject = { "id": requestId, "method": method, "params": params };
return new Promise(fulfill => this.sendRawCommand(requestId, JSON.stringify(messageObject), fulfill));
}
_setupProtocol() {
return new Proxy({}, { get: (target, agentName, receiver) => new Proxy({}, {
get: (target, methodName, receiver) => {
const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/;
var match = eventPattern.exec(methodName);
if (!match)
return args => this._sendCommandPromise(`${agentName}.${methodName}`, args || {});
var eventName = match[2];
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
if (match[1])
return numOfEvents => this._waitForEventPromise(
`${agentName}.${eventName}`, numOfEvents || 1);
return listener => this._eventHandlers.set(`${agentName}.${eventName}`, listener);
}
})});
}
_dispatchMessage(messageString) {
var messageObject = JSON.parse(messageString);
if (InspectorTest._dumpInspectorProtocolMessages)
utils2.print("backend: " + JSON.stringify(messageObject));
const kMethodNotFound = -32601;
if (messageObject.error && messageObject.error.code === kMethodNotFound) {
InspectorTest.log(`Error: Called non-existent method. ${
messageObject.error.message} code: ${messageObject.error.code}`);
InspectorTest.completeTest();
}
try {
var messageId = messageObject["id"];
if (typeof messageId === "number") {
var handler = this._dispatchTable.get(messageId);
if (handler) {
handler(messageObject);
this._dispatchTable.delete(messageId);
}
} else {
var eventName = messageObject["method"];
var eventHandler = this._eventHandlers.get(eventName);
if (this._scriptMap && eventName === "Debugger.scriptParsed")
this._scriptMap.set(messageObject.params.scriptId, JSON.parse(JSON.stringify(messageObject.params)));
if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-for-pending-tasks.js")
return;
if (eventHandler)
eventHandler(messageObject);
}
} catch (e) {
InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2));
InspectorTest.completeTest();
} var session = contextGroup.connect();
return { session: session, contextGroup: contextGroup, Protocol: session.Protocol };
} catch (e) {
utils2.print(e.stack);
}
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest.log('Checks createContext().');
var executionContextIds = new Set();
var contextGroup1 = new InspectorTest.ContextGroup();
var session1 = contextGroup1.connect();
setup(session1);
var contextGroup2 = new InspectorTest.ContextGroup();
var session2 = contextGroup2.connect();
setup(session2);
session1.Protocol.Runtime.enable()
.then(() => session2.Protocol.Runtime.enable({}))
.then(() => session1.Protocol.Debugger.enable())
.then(() => session2.Protocol.Debugger.enable({}))
.then(InspectorTest.logMessage)
.then(() => {
session1.Protocol.Runtime.evaluate({ expression: 'debugger;' });
session2.Protocol.Runtime.evaluate({expression: 'setTimeout(x => x * 2, 0)'});
session1.Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 3, 0)' });
})
.then(() => InspectorTest.waitForPendingTasks())
.then(() => {
InspectorTest.log(`Reported script's execution id: ${executionContextIds.size}`);
executionContextIds.clear();
})
.then(() => session1.reconnect())
.then(() => session2.reconnect())
.then(() => {
session1.Protocol.Runtime.evaluate({ expression: 'debugger;' })
session2.Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 2, 0)' });
session1.Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 3, 0)' });
})
.then(() => InspectorTest.waitForPendingTasks())
.then(() => session2.Protocol.Debugger.disable({}))
.then(() => session1.Protocol.Debugger.disable({}))
.then(() => InspectorTest.log(`Reported script's execution id: ${executionContextIds.size}`))
.then(InspectorTest.completeTest);
function setup(session) {
session.Protocol.Runtime.onExecutionContextCreated(InspectorTest.logMessage);
session.setupScriptMap();
session.Protocol.Debugger.onPaused((message) => {
session.logSourceLocation(message.params.callFrames[5].location);
session.Protocol.Debugger.stepOut();
});
session.Protocol.Debugger.onScriptParsed(message => executionContextIds.add(message.params.executionContextId));
}

43
deps/v8/test/fuzzer/json.cc vendored Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-json.h"
#include "include/v8-local-handle.h"
#include "include/v8-primitive.h"
#include "test/fuzzer/fuzzer-support.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Ignore too long inputs as they tend to find OOM or timeouts, not real bugs.
if (size > 16 * 1024) {
return false;
}
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
if (size > INT_MAX) return 0;
v8::Local<v8::String> source;
if (!v8::String::NewFromOneByte(isolate, data, v8::NewStringType::kNormal,
static_cast<int>(size))
.ToLocal(&source)) {
return 0;
}
v8::JSON::Parse(support->GetContext(), source).IsEmpty();
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
return 0;
}

1
deps/v8/test/fuzzer/json/json vendored Normal file
View File

@ -0,0 +1 @@
{"json": 1}

1
deps/v8/test/fuzzer/json/not-json vendored Normal file
View File

@ -0,0 +1 @@
not json

313
deps/v8/test/fuzzer/multi-return.cc vendored Normal file
View File

@ -0,0 +1,313 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstddef>
#include <cstdint>
#include "src/codegen/machine-type.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/backend/instruction-selector.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
#include "src/compiler/pipeline.h"
#include "src/compiler/raw-machine-assembler.h"
#include "src/compiler/turbofan-graph.h"
#include "src/compiler/wasm-compiler.h"
#include "src/execution/simulator.h"
#include "src/wasm/wasm-code-pointer-table-inl.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/fuzzer/fuzzer-support.h"
namespace v8 {
namespace internal {
namespace compiler {
namespace fuzzer {
constexpr MachineType kTypes[] = {
// The first entry is just a placeholder, because '0' is a separator.
MachineType(),
#if !V8_TARGET_ARCH_32_BIT
MachineType::Int64(),
#endif
MachineType::Int32(), MachineType::Float32(), MachineType::Float64()};
static constexpr int kNumTypes = arraysize(kTypes);
class InputProvider {
public:
InputProvider(const uint8_t* data, size_t size)
: current_(data), end_(data + size) {}
size_t NumNonZeroBytes(size_t offset, int limit) {
DCHECK_LE(limit, std::numeric_limits<uint8_t>::max());
DCHECK_GE(current_ + offset, current_);
const uint8_t* p;
for (p = current_ + offset; p < end_; ++p) {
if (*p % limit == 0) break;
}
return p - current_ - offset;
}
int NextInt8(int limit) {
DCHECK_LE(limit, std::numeric_limits<uint8_t>::max());
if (current_ == end_) return 0;
uint8_t result = *current_;
current_++;
return static_cast<int>(result) % limit;
}
int NextInt32(int limit) {
if (current_ + sizeof(uint32_t) > end_) return 0;
int result =
base::ReadLittleEndianValue<int>(reinterpret_cast<Address>(current_));
current_ += sizeof(uint32_t);
return result % limit;
}
private:
const uint8_t* current_;
const uint8_t* end_;
};
MachineType RandomType(InputProvider* input) {
return kTypes[input->NextInt8(kNumTypes)];
}
int index(MachineType type) { return static_cast<int>(type.representation()); }
Node* Constant(RawMachineAssembler* m, MachineType type, int value) {
switch (type.representation()) {
case MachineRepresentation::kWord32:
return m->Int32Constant(static_cast<int32_t>(value));
case MachineRepresentation::kWord64:
return m->Int64Constant(static_cast<int64_t>(value));
case MachineRepresentation::kFloat32:
return m->Float32Constant(static_cast<float>(value));
case MachineRepresentation::kFloat64:
return m->Float64Constant(static_cast<double>(value));
default:
UNREACHABLE();
}
}
Node* ToInt32(RawMachineAssembler* m, MachineType type, Node* a) {
switch (type.representation()) {
case MachineRepresentation::kWord32:
return a;
case MachineRepresentation::kWord64:
return m->TruncateInt64ToInt32(a);
case MachineRepresentation::kFloat32:
return m->TruncateFloat32ToInt32(a, TruncateKind::kArchitectureDefault);
case MachineRepresentation::kFloat64:
return m->RoundFloat64ToInt32(a);
default:
UNREACHABLE();
}
}
CallDescriptor* CreateRandomCallDescriptor(Zone* zone, size_t return_count,
size_t param_count,
InputProvider* input) {
wasm::FunctionSig::Builder builder(zone, return_count, param_count);
for (size_t i = 0; i < param_count; i++) {
MachineType type = RandomType(input);
builder.AddParam(wasm::ValueType::For(type));
}
// Read the end byte of the parameters.
input->NextInt8(1);
for (size_t i = 0; i < return_count; i++) {
MachineType type = RandomType(input);
builder.AddReturn(wasm::ValueType::For(type));
}
return compiler::GetWasmCallDescriptor(
zone, builder.Get(), compiler::WasmCallKind::kWasmIndirectFunction);
}
std::shared_ptr<wasm::NativeModule> AllocateNativeModule(i::Isolate* isolate,
size_t code_size) {
auto module = std::make_shared<wasm::WasmModule>(wasm::kWasmOrigin);
module->num_declared_functions = 1;
// We have to add the code object to a NativeModule, because the
// WasmCallDescriptor assumes that code is on the native heap and not
// within a code object.
auto native_module = wasm::GetWasmEngine()->NewNativeModule(
isolate, wasm::WasmEnabledFeatures::All(), wasm::WasmDetectedFeatures{},
wasm::CompileTimeImports{}, std::move(module), code_size);
native_module->SetWireBytes({});
return native_module;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
InputProvider input(data, size);
// Create randomized descriptor.
size_t param_count = input.NumNonZeroBytes(0, kNumTypes);
if (param_count > wasm::kV8MaxWasmFunctionParams) return 0;
size_t return_count = input.NumNonZeroBytes(param_count + 1, kNumTypes);
if (return_count > wasm::kV8MaxWasmFunctionReturns) return 0;
CallDescriptor* desc =
CreateRandomCallDescriptor(&zone, return_count, param_count, &input);
if (v8_flags.wasm_fuzzer_gen_test) {
// Print some debugging output which describes the produced signature.
printf("[");
for (size_t j = 0; j < param_count; ++j) {
// Parameter 0 is the WasmContext.
printf(" %s", MachineReprToString(
desc->GetParameterType(j + 1).representation()));
}
printf(" ] -> [");
for (size_t j = 0; j < desc->ReturnCount(); ++j) {
printf(" %s",
MachineReprToString(desc->GetReturnType(j).representation()));
}
printf(" ]\n\n");
}
// Count parameters of each type.
constexpr size_t kNumMachineRepresentations =
static_cast<size_t>(MachineRepresentation::kLastRepresentation) + 1;
// Trivial hash table for the number of occurrences of parameter types. The
// MachineRepresentation of the parameter types is used as hash code.
int counts[kNumMachineRepresentations] = {0};
for (size_t i = 0; i < param_count; ++i) {
// Parameter 0 is the WasmContext.
++counts[index(desc->GetParameterType(i + 1))];
}
// Generate random inputs.
std::unique_ptr<int[]> inputs(new int[param_count]);
std::unique_ptr<int[]> outputs(new int[desc->ReturnCount()]);
for (size_t i = 0; i < param_count; ++i) {
inputs[i] = input.NextInt32(10000);
}
RawMachineAssembler callee(
i_isolate, zone.New<TFGraph>(&zone), desc,
MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags());
// Generate callee, returning random picks of its parameters.
std::unique_ptr<Node* []> params(new Node*[desc->ParameterCount() + 2]);
// The first input of a return is the number of stack slots that should be
// popped before returning.
std::unique_ptr<Node* []> returns(new Node*[desc->ReturnCount() + 1]);
for (size_t i = 0; i < param_count; ++i) {
// Parameter(0) is the WasmContext.
params[i] = callee.Parameter(i + 1);
}
for (size_t i = 0; i < desc->ReturnCount(); ++i) {
MachineType type = desc->GetReturnType(i);
// Find a random same-type parameter to return. Use a constant if none.
if (counts[index(type)] == 0) {
returns[i] = Constant(&callee, type, 42);
outputs[i] = 42;
} else {
int n = input.NextInt32(counts[index(type)]);
int k = 0;
while (desc->GetParameterType(k + 1) != desc->GetReturnType(i) ||
--n > 0) {
++k;
}
returns[i] = params[k];
outputs[i] = inputs[k];
}
}
callee.Return(static_cast<int>(desc->ReturnCount()), returns.get());
OptimizedCompilationInfo info(base::ArrayVector("testing"), &zone,
CodeKind::FOR_TESTING);
DirectHandle<Code> code =
Pipeline::GenerateCodeForTesting(&info, i_isolate, desc, callee.graph(),
AssemblerOptions::Default(i_isolate),
callee.ExportForTest())
.ToHandleChecked();
std::shared_ptr<wasm::NativeModule> module =
AllocateNativeModule(i_isolate, code->instruction_size());
wasm::WasmCodeRefScope wasm_code_ref_scope;
wasm::WasmCode* wasm_code =
module->AddCodeForTesting(code, desc->signature_hash());
WasmCodePointer code_pointer =
wasm::GetProcessWideWasmCodePointerTable()->AllocateAndInitializeEntry(
wasm_code->instruction_start(), wasm_code->signature_hash());
// Generate wrapper.
int expect = 0;
MachineSignature::Builder sig_builder(&zone, 1, 0);
sig_builder.AddReturn(MachineType::Int32());
CallDescriptor* wrapper_desc =
Linkage::GetSimplifiedCDescriptor(&zone, sig_builder.Get());
RawMachineAssembler caller(
i_isolate, zone.New<TFGraph>(&zone), wrapper_desc,
MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags());
params[0] = caller.IntPtrConstant(code_pointer.value());
// WasmContext dummy.
params[1] = caller.PointerConstant(nullptr);
for (size_t i = 0; i < param_count; ++i) {
params[i + 2] = Constant(&caller, desc->GetParameterType(i + 1), inputs[i]);
}
Node* call = caller.AddNode(caller.common()->Call(desc),
static_cast<int>(param_count + 2), params.get());
Node* ret = Constant(&caller, MachineType::Int32(), 0);
for (size_t i = 0; i < desc->ReturnCount(); ++i) {
// Skip roughly one third of the outputs.
if (input.NextInt8(3) == 0) continue;
Node* ret_i = (desc->ReturnCount() == 1)
? call
: caller.AddNode(caller.common()->Projection(i), call);
ret = caller.Int32Add(ret, ToInt32(&caller, desc->GetReturnType(i), ret_i));
expect += outputs[i];
}
caller.Return(ret);
// Call the wrapper.
OptimizedCompilationInfo wrapper_info(base::ArrayVector("wrapper"), &zone,
CodeKind::FOR_TESTING);
DirectHandle<Code> wrapper_code =
Pipeline::GenerateCodeForTesting(
&wrapper_info, i_isolate, wrapper_desc, caller.graph(),
AssemblerOptions::Default(i_isolate), caller.ExportForTest())
.ToHandleChecked();
auto fn = GeneratedCode<int32_t>::FromCode(i_isolate, *wrapper_code);
int result = fn.Call();
wasm::GetProcessWideWasmCodePointerTable()->FreeEntry(code_pointer);
CHECK_EQ(expect, result);
return 0;
}
} // namespace fuzzer
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,4 @@
All files in this directory are used by the trybots to check that the fuzzer
executes correctly, see
https://github.com/v8/v8/blob/master/test/fuzzer/README.md. There should be at
least one file in this directory, e.g. this README file.

100
deps/v8/test/fuzzer/parser.cc vendored Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <cctype>
#include <list>
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/objects/string.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parsing.h"
#include "test/fuzzer/fuzzer-support.h"
bool IsValidInput(const uint8_t* data, size_t size) {
// Ignore too long inputs as they tend to find OOM or timeouts, not real bugs.
if (size > 2048) return false;
std::vector<char> parentheses;
const char* ptr = reinterpret_cast<const char*>(data);
for (size_t i = 0; i != size; ++i) {
// Check that all characters in the data are valid.
if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) return false;
// Check balance of parentheses in the data.
switch (ptr[i]) {
case '(':
case '[':
case '{':
parentheses.push_back(ptr[i]);
break;
case ')':
if (parentheses.empty() || parentheses.back() != '(') return false;
parentheses.pop_back();
break;
case ']':
if (parentheses.empty() || parentheses.back() != '[') return false;
parentheses.pop_back();
break;
case '}':
if (parentheses.empty() || parentheses.back() != '{') return false;
parentheses.pop_back();
break;
default:
break;
}
}
return parentheses.empty();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (!IsValidInput(data, size)) {
return 0;
}
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
v8::internal::Isolate* i_isolate =
reinterpret_cast<v8::internal::Isolate*>(isolate);
v8::internal::Factory* factory = i_isolate->factory();
if (size > INT_MAX) return 0;
v8::internal::MaybeDirectHandle<v8::internal::String> source =
factory->NewStringFromOneByte(v8::base::VectorOf(data, size));
if (source.is_null()) return 0;
v8::internal::Handle<v8::internal::Script> script =
factory->NewScript(source.ToHandleChecked());
v8::internal::UnoptimizedCompileState state;
v8::internal::ReusableUnoptimizedCompileState reusable_state(i_isolate);
v8::internal::UnoptimizedCompileFlags flags =
v8::internal::UnoptimizedCompileFlags::ForScriptCompile(i_isolate,
*script);
v8::internal::ParseInfo info(i_isolate, flags, &state, &reusable_state);
if (!v8::internal::parsing::ParseProgram(
&info, script, i_isolate, i::parsing::ReportStatisticsMode::kYes)) {
info.pending_error_handler()->PrepareErrors(i_isolate,
info.ast_value_factory());
info.pending_error_handler()->ReportErrors(i_isolate, script);
}
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
return 0;
}

View File

@ -0,0 +1 @@
console.log('hello world');

98
deps/v8/test/fuzzer/regexp.cc vendored Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/regexp/regexp.h"
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/base/strings.h"
#include "src/execution/isolate-inl.h"
#include "src/heap/factory.h"
#include "test/fuzzer/fuzzer-support.h"
namespace i = v8::internal;
void Test(v8::Isolate* isolate, i::DirectHandle<i::JSRegExp> regexp,
i::DirectHandle<i::String> subject,
i::DirectHandle<i::RegExpMatchInfo> results_array) {
v8::TryCatch try_catch(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
// Exceptions will be swallowed by the try/catch above.
USE(i::RegExp::Exec_Single(i_isolate, regexp, subject, 0, results_array));
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Factory* factory = i_isolate->factory();
CHECK(!i_isolate->has_exception());
if (size > INT_MAX) return 0;
i::MaybeDirectHandle<i::String> maybe_source =
factory->NewStringFromOneByte(v8::base::VectorOf(data, size));
i::DirectHandle<i::String> source;
if (!maybe_source.ToHandle(&source)) {
i_isolate->clear_exception();
return 0;
}
static const int kAllFlags = i::JSRegExp::kGlobal | i::JSRegExp::kIgnoreCase |
i::JSRegExp::kMultiline | i::JSRegExp::kSticky |
i::JSRegExp::kUnicode | i::JSRegExp::kDotAll;
const uint8_t one_byte_array[6] = {'f', 'o', 'o', 'b', 'a', 'r'};
const v8::base::uc16 two_byte_array[6] = {'f', 0xD83D, 0xDCA9,
'b', 'a', 0x2603};
CHECK(!i_isolate->has_exception());
i::DirectHandle<i::RegExpMatchInfo> results_array =
i::RegExpMatchInfo::New(i_isolate, 2);
i::DirectHandle<i::String> one_byte =
factory
->NewStringFromOneByte(
v8::base::Vector<const uint8_t>(one_byte_array, 6))
.ToHandleChecked();
i::DirectHandle<i::String> two_byte =
factory
->NewStringFromTwoByte(
v8::base::Vector<const v8::base::uc16>(two_byte_array, 6))
.ToHandleChecked();
i::DirectHandle<i::JSRegExp> regexp;
{
CHECK(!i_isolate->has_exception());
v8::TryCatch try_catch_inner(isolate);
// Create a string so that we can calculate a hash from the input data.
std::string str = std::string(reinterpret_cast<const char*>(data), size);
i::JSRegExp::Flags flag = static_cast<i::JSRegExp::Flags>(
std::hash<std::string>()(str) % (kAllFlags + 1));
i::MaybeDirectHandle<i::JSRegExp> maybe_regexp =
i::JSRegExp::New(i_isolate, source, flag);
if (!maybe_regexp.ToHandle(&regexp)) {
i_isolate->clear_exception();
return 0;
}
}
Test(isolate, regexp, one_byte, results_array);
Test(isolate, regexp, two_byte, results_array);
Test(isolate, regexp, factory->empty_string(), results_array);
Test(isolate, regexp, source, results_array);
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
CHECK(!i_isolate->has_exception());
return 0;
}

1
deps/v8/test/fuzzer/regexp/test00 vendored Normal file
View File

@ -0,0 +1 @@
a*

1
deps/v8/test/fuzzer/regexp/test01 vendored Normal file
View File

@ -0,0 +1 @@
xyz{93}?

1
deps/v8/test/fuzzer/regexp/test02 vendored Normal file
View File

@ -0,0 +1 @@
(foo|bar|baz)

1
deps/v8/test/fuzzer/regexp/test03 vendored Normal file
View File

@ -0,0 +1 @@
[^]

1
deps/v8/test/fuzzer/regexp/test04 vendored Normal file
View File

@ -0,0 +1 @@
[\d]

1
deps/v8/test/fuzzer/regexp/test05 vendored Normal file
View File

@ -0,0 +1 @@
\c1

1
deps/v8/test/fuzzer/regexp/test06 vendored Normal file
View File

@ -0,0 +1 @@
[a\]c]

1
deps/v8/test/fuzzer/regexp/test07 vendored Normal file
View File

@ -0,0 +1 @@
\00011

1
deps/v8/test/fuzzer/regexp/test08 vendored Normal file
View File

@ -0,0 +1 @@
(x)(x)(x)\2*

1
deps/v8/test/fuzzer/regexp/test09 vendored Normal file
View File

@ -0,0 +1 @@
(?=a)?a

1
deps/v8/test/fuzzer/regexp/test10 vendored Normal file
View File

@ -0,0 +1 @@
\1\2(a(?<=\1(b\1\2))\2)\1

1
deps/v8/test/fuzzer/regexp/test11 vendored Normal file
View File

@ -0,0 +1 @@
\x34

1
deps/v8/test/fuzzer/regexp/test12 vendored Normal file
View File

@ -0,0 +1 @@
\u{12345}|\u{23456}

1
deps/v8/test/fuzzer/regexp/test13 vendored Normal file
View File

@ -0,0 +1 @@
^a

1
deps/v8/test/fuzzer/regexp/test14 vendored Normal file
View File

@ -0,0 +1 @@
a{1,1}?

1
deps/v8/test/fuzzer/regexp/test15 vendored Normal file
View File

@ -0,0 +1 @@
a\d

1
deps/v8/test/fuzzer/regexp/test16 vendored Normal file
View File

@ -0,0 +1 @@
a[\q]

1
deps/v8/test/fuzzer/regexp/test17 vendored Normal file
View File

@ -0,0 +1 @@
\0

1
deps/v8/test/fuzzer/regexp/test18 vendored Normal file
View File

@ -0,0 +1 @@
a{1z}

1
deps/v8/test/fuzzer/regexp/test19 vendored Normal file
View File

@ -0,0 +1 @@
{12z}

1
deps/v8/test/fuzzer/regexp/test20 vendored Normal file
View File

@ -0,0 +1 @@
|

1
deps/v8/test/fuzzer/regexp/test21 vendored Normal file
View File

@ -0,0 +1 @@
(?:ab)*

1
deps/v8/test/fuzzer/regexp/test22 vendored Normal file
View File

@ -0,0 +1 @@
(?:a*)?

1
deps/v8/test/fuzzer/regexp/test23 vendored Normal file
View File

@ -0,0 +1 @@
(?:a+){0}

1
deps/v8/test/fuzzer/regexp/test24 vendored Normal file
View File

@ -0,0 +1 @@
a\Bc

73
deps/v8/test/fuzzer/testcfg.py vendored Normal file
View File

@ -0,0 +1,73 @@
# Copyright 2016 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from testrunner.local import testsuite
from testrunner.objects import testcase
from testrunner.outproc import fuzzer
SUB_TESTS = [
'inspector',
'json',
'parser',
'regexp',
'regexp_builtins',
'multi_return',
'wasm/module',
'wasm/async',
'wasm/code',
'wasm/compile',
'wasm/compile_all',
'wasm/compile_simd',
'wasm/compile_wasmgc',
'wasm/init_expr',
'wasm/streaming',
]
class VariantsGenerator(testsuite.VariantsGenerator):
def _get_variants(self, test):
return self._standard_variant
class TestLoader(testsuite.GenericTestLoader):
@property
def test_dirs(self):
return SUB_TESTS
def _to_relpath(self, abspath, _):
return abspath.relative_to(self.suite.root)
def _should_filter_by_name(self, _):
return False
class TestSuite(testsuite.TestSuite):
def _test_loader_class(self):
return TestLoader
def _test_class(self):
return TestCase
def _variants_gen_class(self):
return VariantsGenerator
class TestCase(testcase.TestCase):
def _get_files_params(self):
return [self.suite.root / self.path]
def _get_variant_flags(self):
return []
def _get_statusfile_flags(self):
return []
def _get_mode_flags(self):
return []
def get_shell(self):
fuzzer_name = '_'.join(self.path.parts[:-1])
return f'v8_simple_{fuzzer_name}_fuzzer'
@property
def output_proc(self):
return fuzzer.OutProc(self.expected_outcomes)

14
deps/v8/test/fuzzer/wasm/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>WebAssembly"
}
buganizer_public: {
component_id: 1456332
}

100
deps/v8/test/fuzzer/wasm/async.cc vendored Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/execution/isolate-inl.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-module.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal {
class WasmModuleObject;
}
namespace v8::internal::wasm::fuzzing {
class AsyncFuzzerResolver : public CompilationResultResolver {
public:
AsyncFuzzerResolver(Isolate* isolate, bool* done)
: isolate_(isolate), done_(done) {}
void OnCompilationSucceeded(DirectHandle<WasmModuleObject> module) override {
*done_ = true;
fuzzing_return_value_ = ExecuteAgainstReference(
isolate_, module, kDefaultMaxFuzzerExecutedInstructions);
}
void OnCompilationFailed(DirectHandle<JSAny> error_reason) override {
*done_ = true;
// Keep {fuzzing_return_value_} at -1; while failed compilation is also
// somewhat interesting, we get this often enough via mutation, so no need
// to add this to the corpus.
}
int fuzzing_return_value() const { return fuzzing_return_value_; }
private:
Isolate* isolate_;
bool* done_;
int fuzzing_return_value_ = -1;
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// Set some more flags.
v8_flags.wasm_async_compilation = true;
v8_flags.wasm_max_mem_pages = 32;
v8_flags.wasm_max_table_size = 100;
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
// Clear any exceptions from a prior run.
if (i_isolate->has_exception()) {
i_isolate->clear_exception();
}
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// We explicitly enable staged/experimental WebAssembly features here to
// increase fuzzer coverage. For libfuzzer fuzzers it is not possible that the
// fuzzer enables the flag by itself.
EnableExperimentalWasmFeatures(isolate);
TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate);
bool done = false;
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
constexpr const char* kAPIMethodName = "WasmAsyncFuzzer.compile";
base::OwnedVector<const uint8_t> bytes = base::OwnedCopyOf(data, size);
std::shared_ptr<AsyncFuzzerResolver> resolver =
std::make_shared<AsyncFuzzerResolver>(i_isolate, &done);
GetWasmEngine()->AsyncCompile(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(), resolver,
std::move(bytes), kAPIMethodName);
// Wait for the promise to resolve.
while (!done) {
support->PumpMessageLoop(platform::MessageLoopBehavior::kWaitForWork);
isolate->PerformMicrotaskCheckpoint();
}
return resolver->fuzzing_return_value();
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,4 @@
All files in this directory are used by the trybots to check that the fuzzer
executes correctly, see
https://github.com/v8/v8/blob/master/test/fuzzer/README.md. There should be at
least one file in this directory, e.g. this README file.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

36
deps/v8/test/fuzzer/wasm/code.cc vendored Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include "src/execution/isolate.h"
#include "src/wasm/wasm-module-builder.h"
#include "test/common/wasm/test-signatures.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
class WasmCodeFuzzer : public WasmExecutionFuzzer {
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
ZoneBuffer* buffer) override {
TestSignatures sigs;
WasmModuleBuilder builder(zone);
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
f->EmitCode(data.begin(), static_cast<uint32_t>(data.size()));
f->Emit(kExprEnd);
builder.AddExport(base::CStrVector("main"), f);
builder.AddMemory(0, 32);
builder.WriteTo(buffer);
return true;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return WasmCodeFuzzer().FuzzWasmModule({data, size});
}
} // namespace v8::internal::wasm::fuzzing

0
deps/v8/test/fuzzer/wasm/code/foo vendored Normal file
View File

31
deps/v8/test/fuzzer/wasm/compile-all.cc vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/vector.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Fuzzer that may generate WasmGC and SIMD expressions.
class WasmCompileAllFuzzer : public WasmExecutionFuzzer {
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
ZoneBuffer* buffer) override {
base::Vector<const uint8_t> wire_bytes = GenerateRandomWasmModule(
zone, WasmModuleGenerationOptions::All(), data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
constexpr bool kRequireValid = true;
return WasmCompileAllFuzzer().FuzzWasmModule({data, size}, kRequireValid);
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,31 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/vector.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Fuzzer that may generate SIMD expressions.
class WasmCompileSIMDFuzzer : public WasmExecutionFuzzer {
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
ZoneBuffer* buffer) override {
base::Vector<const uint8_t> wire_bytes = GenerateRandomWasmModule(
zone, {{WasmModuleGenerationOption::kGenerateSIMD}}, data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
constexpr bool kRequireValid = true;
return WasmCompileSIMDFuzzer().FuzzWasmModule({data, size}, kRequireValid);
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,31 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/vector.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Fuzzer that may generate WasmGC expressions.
class WasmCompileWasmGCFuzzer : public WasmExecutionFuzzer {
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
ZoneBuffer* buffer) override {
base::Vector<const uint8_t> wire_bytes = GenerateRandomWasmModule(
zone, {{WasmModuleGenerationOption::kGenerateWasmGC}}, data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
constexpr bool kRequireValid = true;
return WasmCompileWasmGCFuzzer().FuzzWasmModule({data, size}, kRequireValid);
}
} // namespace v8::internal::wasm::fuzzing

30
deps/v8/test/fuzzer/wasm/compile.cc vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/vector.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
class WasmCompileMVPFuzzer : public WasmExecutionFuzzer {
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
ZoneBuffer* buffer) override {
base::Vector<const uint8_t> wire_bytes = GenerateRandomWasmModule(
zone, WasmModuleGenerationOptions::MVP(), data);
buffer->write(wire_bytes.data(), wire_bytes.size());
// Without SIMD expressions we are always able to produce a valid module.
return true;
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
constexpr bool kRequireValid = true;
return WasmCompileMVPFuzzer().FuzzWasmModule({data, size}, kRequireValid);
}
} // namespace v8::internal::wasm::fuzzing

0
deps/v8/test/fuzzer/wasm/compile/foo vendored Normal file
View File

405
deps/v8/test/fuzzer/wasm/deopt.cc vendored Normal file
View File

@ -0,0 +1,405 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/base/vector.h"
#include "src/execution/isolate.h"
#include "src/objects/property-descriptor.h"
#include "src/wasm/compilation-environment-inl.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-subtyping.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/flag-utils.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
// This fuzzer fuzzes deopts.
// It generates a main function accepting a call target. The call target is then
// used in a call_ref or call_indirect. The fuzzer runs the program in a
// reference run to collect expected results.
// Then it performs the same run on a new module optimizing the module after
// every target, causing emission of deopt nodes and potentially triggering
// deopts. Note that if the code containing the speculative call is unreachable
// or not inlined, the fuzzer won't generate a deopt node and won't perform a
// deopt.
// Pseudo code of a minimal wasm module that the fuzzer could generate:
//
// int global0 = 0;
// Table table = [callee0, callee1];
//
// int callee0(int a, int b) {
// return a + b;
// }
//
// int callee1(int a, int b) {
// return a * b;
// }
//
// int inlinee(int a, int b) {
// auto callee = table.get(global0);
// return call_ref(auto_callee)(a, b);
// }
//
// int main(int callee_index) {
// global0 = callee_index;
// return inlinee(1, 2);
// }
// The fuzzer then performs the following test:
// assertEquals(expected_val0, main(0)); // Collects feedback.
// %WasmTierUpFunction(main);
// assertEquals(expected_val1, main(1)); // Potentially triggers deopt.
namespace v8::internal::wasm::fuzzing {
namespace {
using ExecutionResult = std::variant<int, std::string /*exception*/>;
std::ostream& operator<<(std::ostream& out, const ExecutionResult& result) {
std::visit([&out](auto&& val) { out << val; }, result);
return out;
}
class NearHeapLimitCallbackScope {
public:
explicit NearHeapLimitCallbackScope(Isolate* isolate) : isolate_(isolate) {
isolate_->heap()->AddNearHeapLimitCallback(Callback, this);
}
~NearHeapLimitCallbackScope() {
isolate_->heap()->RemoveNearHeapLimitCallback(Callback, initial_limit_);
}
bool heap_limit_reached() const { return heap_limit_reached_; }
private:
static size_t Callback(void* raw_data, size_t current_limit,
size_t initial_limit) {
NearHeapLimitCallbackScope* data =
reinterpret_cast<NearHeapLimitCallbackScope*>(raw_data);
data->heap_limit_reached_ = true;
data->isolate_->TerminateExecution();
data->initial_limit_ = initial_limit;
// Return a slightly raised limit, just to make it to the next
// interrupt check point, where execution will terminate.
return initial_limit * 1.25;
}
Isolate* isolate_;
bool heap_limit_reached_ = false;
size_t initial_limit_ = 0;
};
class EnterDebuggingScope {
public:
explicit EnterDebuggingScope(Isolate* isolate) : isolate_(isolate) {
GetWasmEngine()->EnterDebuggingForIsolate(isolate_);
}
~EnterDebuggingScope() {
GetWasmEngine()->LeaveDebuggingForIsolate(isolate_);
}
private:
Isolate* isolate_;
};
std::vector<ExecutionResult> PerformReferenceRun(
const std::vector<std::string>& callees, ModuleWireBytes wire_bytes,
WasmEnabledFeatures enabled_features, bool valid, Isolate* isolate) {
std::vector<ExecutionResult> results;
FlagScope<bool> eager_compile(&v8_flags.wasm_lazy_compilation, false);
// Reference runs use extra compile settings (like non-determinism detection),
// which would be removed and replaced with a new liftoff function without
// these options.
FlagScope<bool> no_liftoff_code_flushing(&v8_flags.flush_liftoff_code, false);
ErrorThrower thrower(isolate, "WasmFuzzerSyncCompileReference");
int32_t max_steps = kDefaultMaxFuzzerExecutedInstructions;
// We aren't really debugging but this will prevent tier-up and other
// "dynamic" behavior that we do not want to trigger during reference
// execution. This also aligns well with the reference compilation compiling
// with the kForDebugging liftoff option.
EnterDebuggingScope debugging_scope(isolate);
DirectHandle<WasmModuleObject> module_object =
CompileReferenceModule(isolate, wire_bytes.module_bytes(), &max_steps);
thrower.Reset();
CHECK(!isolate->has_exception());
DirectHandle<WasmInstanceObject> instance;
if (!GetWasmEngine()
->SyncInstantiate(isolate, &thrower, module_object, {}, {})
.ToHandle(&instance)) {
CHECK(thrower.error());
// The only reason to fail the second instantiation should be OOM. This can
// happen e.g. for memories with a very big initial size especially on 32
// bit platforms.
CHECK(strstr(thrower.error_msg(), "Out of memory"));
return {};
}
NearHeapLimitCallbackScope near_heap_limit(isolate);
for (uint32_t i = 0; i < callees.size(); ++i) {
// Before execution, there should be no dangling nondeterminism registered
// on the engine.
DCHECK(!WasmEngine::had_nondeterminism());
DirectHandle<Object> arguments[] = {
direct_handle(Smi::FromInt(i), isolate)};
std::unique_ptr<const char[]> exception;
int32_t result = testing::CallWasmFunctionForTesting(
isolate, instance, "main", base::VectorOf(arguments), &exception);
// If there is nondeterminism, we cannot guarantee the behavior of the test
// module, and in particular it may not terminate.
if (WasmEngine::clear_nondeterminism()) break;
// Reached max steps, do not try to execute the test module as it might
// never terminate.
if (max_steps < 0) break;
// Similar to max steps reached, also discard modules that need too much
// memory.
if (near_heap_limit.heap_limit_reached()) {
isolate->CancelTerminateExecution();
break;
}
if (exception) {
isolate->CancelTerminateExecution();
if (strcmp(exception.get(),
"RangeError: Maximum call stack size exceeded") == 0) {
// There was a stack overflow, which may happen nondeterministically. We
// cannot guarantee the behavior of the test module, and in particular
// it may not terminate.
break;
}
results.emplace_back(exception.get());
} else {
results.emplace_back(result);
}
}
thrower.Reset();
isolate->clear_exception();
return results;
}
void ConfigureFlags(v8::Isolate* isolate) {
struct FlagConfiguration {
explicit FlagConfiguration(v8::Isolate* isolate) {
// Disable the NativeModule cache. Different fuzzer iterations should not
// interact with each other. Rerunning a fuzzer input (e.g. with
// libfuzzer's "-runs=x" argument) should repeatedly test deoptimizations.
// When caching the optimized code, only the first run will execute any
// deopts.
v8_flags.wasm_native_module_cache = false;
// We switch it to synchronous mode to avoid the nondeterminism of
// background jobs finishing at random times.
v8_flags.wasm_sync_tier_up = true;
// Enable the experimental features we want to fuzz. (Note that
// EnableExperimentalWasmFeatures only enables staged features.)
v8_flags.wasm_deopt = true;
v8_flags.wasm_inlining_call_indirect = true;
// Make inlining more aggressive.
v8_flags.wasm_inlining_ignore_call_counts = true;
v8_flags.wasm_inlining_budget = v8_flags.wasm_inlining_budget * 5;
v8_flags.wasm_inlining_max_size = v8_flags.wasm_inlining_max_size * 5;
v8_flags.wasm_inlining_factor = v8_flags.wasm_inlining_factor * 5;
// Enable other staged or experimental features and enforce flag
// implications.
EnableExperimentalWasmFeatures(isolate);
}
};
static FlagConfiguration config(isolate);
}
int FuzzIt(base::Vector<const uint8_t> data) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// Strictly enforce the input size limit as in fuzzer-common.h.
if (data.size() > kMaxFuzzerInputSize) return 0;
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
ConfigureFlags(isolate);
v8::TryCatch try_catch(isolate);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
// Clear recursive groups: The fuzzer creates random types in every run. These
// are saved as recursive groups as part of the type canonicalizer, but types
// from previous runs just waste memory.
ResetTypeCanonicalizer(isolate, &zone);
std::vector<std::string> callees;
std::vector<std::string> inlinees;
base::Vector<const uint8_t> buffer =
GenerateWasmModuleForDeopt(&zone, data, callees, inlinees);
ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
testing::SetupIsolateForWasmModule(i_isolate);
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool valid = GetWasmEngine()->SyncValidate(
i_isolate, enabled_features, CompileTimeImportsForFuzzing(), buffer);
// Choose whether to optimize the main function or the outermost callee.
// As the main function has a fixed signature, it doesn't provide great
// coverage to always optimize and deopt the main function. Instead by
// optimizing an inner wasm function, there can be a large amount of
// parameters and returns with all kinds of types.
const bool optimize_main_function =
inlinees.empty() || data.empty() || !(data.last() & 1);
if (v8_flags.wasm_fuzzer_gen_test) {
StdoutStream os;
std::stringstream flags;
flags << " --allow-natives-syntax --wasm-deopt "
"--wasm-inlining-ignore-call-counts --wasm-sync-tier-up "
"--wasm-inlining-budget="
<< v8_flags.wasm_inlining_budget
<< " --wasm-inlining-max-size=" << v8_flags.wasm_inlining_max_size
<< " --wasm-inlining-factor=" << v8_flags.wasm_inlining_factor;
// The deopt fuzzer generates a different call sequence.
bool emit_call_main = false;
GenerateTestCase(os, i_isolate, wire_bytes, valid, emit_call_main,
flags.str());
// Emit helper.
os << "let maybeThrows = (fct, ...args) => {\n"
" try {\n"
" print(fct(...args));\n"
" } catch (e) {\n"
" print(`caught ${e}`);\n"
" }\n"
"};\n\n";
// Emit calls for the different call target indices each followed by an
// explicit tier-up.
std::string fct("instance.exports.");
fct.append(optimize_main_function ? "main" : inlinees.back());
for (size_t i = 0; i < callees.size(); ++i) {
os << "maybeThrows(instance.exports.main, " << i
<< ");\n"
"%WasmTierUpFunction("
<< fct << ");\n";
}
os.flush();
}
ErrorThrower thrower(i_isolate, "WasmFuzzerSyncCompile");
MaybeDirectHandle<WasmModuleObject> compiled = GetWasmEngine()->SyncCompile(
i_isolate, enabled_features, CompileTimeImportsForFuzzing(), &thrower,
base::OwnedCopyOf(buffer));
if (!valid) {
FATAL("Generated module should validate, but got: %s\n",
thrower.error_msg());
}
std::vector<ExecutionResult> reference_results = PerformReferenceRun(
callees, wire_bytes, enabled_features, valid, i_isolate);
if (reference_results.empty()) {
// If the first run already included non-determinism, there isn't any value
// in even compiling it (as this fuzzer focuses on executing deopts).
// Return -1 to not add this case to the corpus.
return -1;
}
DirectHandle<WasmModuleObject> module_object = compiled.ToHandleChecked();
DirectHandle<WasmInstanceObject> instance;
if (!GetWasmEngine()
->SyncInstantiate(i_isolate, &thrower, module_object, {}, {})
.ToHandle(&instance)) {
DCHECK(thrower.error());
// The only reason to fail the second instantiation should be OOM. This can
// happen e.g. for memories with a very big initial size especially on 32
// bit platforms.
if (strstr(thrower.error_msg(), "Out of memory")) {
return -1; // Return -1 to not add this case to the corpus.
}
FATAL("Second instantiation failed unexpectedly: %s", thrower.error_msg());
}
DCHECK(!thrower.error());
DirectHandle<WasmExportedFunction> main_function =
testing::GetExportedFunction(i_isolate, instance, "main")
.ToHandleChecked();
int function_to_optimize =
main_function->shared()->wasm_exported_function_data()->function_index();
if (!optimize_main_function) {
function_to_optimize--;
}
int deopt_count_begin = GetWasmEngine()->GetDeoptsExecutedCount();
int deopt_count_previous_iteration = deopt_count_begin;
size_t num_callees = reference_results.size();
for (uint32_t i = 0; i < num_callees; ++i) {
DirectHandle<Object> arguments[] = {
direct_handle(Smi::FromInt(i), i_isolate)};
std::unique_ptr<const char[]> exception;
DCHECK(!WasmEngine::had_nondeterminism()); // No dangling nondeterminism.
int32_t result_value = testing::CallWasmFunctionForTesting(
i_isolate, instance, "main", base::VectorOf(arguments), &exception);
// Also the second run can hit nondeterminism which was not hit before (when
// growing memory). In that case, do not compare results.
// TODO(384781857): Due to nondeterminism, the second run could even not
// terminate. If this happens often enough we should do something about
// this.
if (WasmEngine::clear_nondeterminism()) return -1;
ExecutionResult actual_result;
if (exception) {
actual_result = exception.get();
} else {
actual_result = result_value;
}
if (actual_result != reference_results[i]) {
std::cerr << "Different results vs. reference run for callee "
<< callees[i] << ": \nReference: " << reference_results[i]
<< "\nActual: " << actual_result << std::endl;
CHECK_EQ(actual_result, reference_results[i]);
UNREACHABLE();
}
int deopt_count = GetWasmEngine()->GetDeoptsExecutedCount();
if (i != 0 && deopt_count == deopt_count_previous_iteration) {
// No deopt triggered. Skip the rest of the run as it won't provide
// meaningful coverage for the deoptimizer.
// Return -1 to prevent adding this case to the corpus if not a single
// deopt was executed.
return deopt_count == deopt_count_begin ? -1 : 0;
}
deopt_count_previous_iteration = deopt_count;
TierUpNowForTesting(i_isolate, instance->trusted_data(i_isolate),
function_to_optimize);
}
return 0;
}
} // anonymous namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return FuzzIt({data, size});
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,560 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/fuzzer/wasm/fuzzer-common.h"
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "include/v8-metrics.h"
#include "src/execution/isolate.h"
#include "src/utils/ostreams.h"
#include "src/wasm/baseline/liftoff-compiler.h"
#include "src/wasm/compilation-environment-inl.h"
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder-impl.h"
#include "src/wasm/module-instantiate.h"
#include "src/wasm/string-builder-multiline.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-opcodes-inl.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/flag-utils.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "tools/wasm/mjsunit-module-disassembler-impl.h"
#if V8_ENABLE_DRUMBRAKE
#include "src/wasm/interpreter/wasm-interpreter.h"
#endif // V8_ENABLE_DRUMBRAKE
namespace v8::internal::wasm::fuzzing {
namespace {
void CompileAllFunctionsForReferenceExecution(NativeModule* native_module,
int32_t* max_steps) {
const WasmModule* module = native_module->module();
WasmCodeRefScope code_ref_scope;
CompilationEnv env = CompilationEnv::ForModule(native_module);
ModuleWireBytes wire_bytes_accessor{native_module->wire_bytes()};
for (size_t i = module->num_imported_functions; i < module->functions.size();
++i) {
auto& func = module->functions[i];
base::Vector<const uint8_t> func_code =
wire_bytes_accessor.GetFunctionBytes(&func);
constexpr bool kIsShared = false;
FunctionBody func_body(func.sig, func.code.offset(), func_code.begin(),
func_code.end(), kIsShared);
auto result =
ExecuteLiftoffCompilation(&env, func_body,
LiftoffOptions{}
.set_func_index(func.func_index)
.set_for_debugging(kForDebugging)
.set_max_steps(max_steps)
.set_detect_nondeterminism(true));
if (!result.succeeded()) {
FATAL(
"Liftoff compilation failed on a valid module. Run with "
"--trace-wasm-decoder (in a debug build) to see why.");
}
native_module->PublishCode(native_module->AddCompiledCode(result));
}
}
} // namespace
CompileTimeImports CompileTimeImportsForFuzzing() {
CompileTimeImports result;
result.Add(CompileTimeImport::kJsString);
result.Add(CompileTimeImport::kTextDecoder);
result.Add(CompileTimeImport::kTextEncoder);
return result;
}
// Compile a baseline module. We pass a pointer to a max step counter and a
// nondeterminsm flag that are updated during execution by Liftoff.
DirectHandle<WasmModuleObject> CompileReferenceModule(
Isolate* isolate, base::Vector<const uint8_t> wire_bytes,
int32_t* max_steps) {
// Create the native module.
std::shared_ptr<NativeModule> native_module;
constexpr bool kNoVerifyFunctions = false;
auto enabled_features = WasmEnabledFeatures::FromIsolate(isolate);
WasmDetectedFeatures detected_features;
ModuleResult module_res =
DecodeWasmModule(enabled_features, wire_bytes, kNoVerifyFunctions,
ModuleOrigin::kWasmOrigin, &detected_features);
CHECK(module_res.ok());
std::shared_ptr<WasmModule> module = std::move(module_res).value();
CHECK_NOT_NULL(module);
CompileTimeImports compile_imports = CompileTimeImportsForFuzzing();
WasmError imports_error = ValidateAndSetBuiltinImports(
module.get(), wire_bytes, compile_imports, &detected_features);
CHECK(!imports_error.has_error()); // The module was compiled before.
const size_t code_size_estimate =
WasmCodeManager::EstimateNativeModuleCodeSize(module.get());
native_module = GetWasmEngine()->NewNativeModule(
isolate, enabled_features, detected_features,
CompileTimeImportsForFuzzing(), module, code_size_estimate);
native_module->SetWireBytes(base::OwnedCopyOf(wire_bytes));
// The module is known to be valid as this point (it was compiled by the
// caller before).
module->set_all_functions_validated();
// The value is -3 so that it is different than the compilation ID of actual
// compilations, different than the sentinel value of the CompilationState
// (-1) and the value used by native module deserialization (-2).
const int dummy_fuzzing_compilation_id = -3;
native_module->compilation_state()->set_compilation_id(
dummy_fuzzing_compilation_id);
InitializeCompilationForTesting(native_module.get());
// Compile all functions with Liftoff.
CompileAllFunctionsForReferenceExecution(native_module.get(), max_steps);
// Create the module object.
constexpr base::Vector<const char> kNoSourceUrl;
DirectHandle<Script> script =
GetWasmEngine()->GetOrCreateScript(isolate, native_module, kNoSourceUrl);
TypeCanonicalizer::PrepareForCanonicalTypeId(isolate,
module->MaxCanonicalTypeIndex());
return WasmModuleObject::New(isolate, std::move(native_module), script);
}
#if V8_ENABLE_DRUMBRAKE
void ClearJsToWasmWrappersForTesting(Isolate* isolate) {
for (int i = 0; i < isolate->heap()->js_to_wasm_wrappers()->length(); i++) {
isolate->heap()->js_to_wasm_wrappers()->set(i, ClearedValue(isolate));
}
}
int ExecuteAgainstReference(Isolate* isolate,
DirectHandle<WasmModuleObject> module_object,
int32_t max_executed_instructions,
bool is_wasm_jitless) {
#else // V8_ENABLE_DRUMBRAKE
int ExecuteAgainstReference(Isolate* isolate,
DirectHandle<WasmModuleObject> module_object,
int32_t max_executed_instructions) {
#endif // V8_ENABLE_DRUMBRAKE
// We do not instantiate the module if there is a start function, because a
// start function can contain an infinite loop which we cannot handle.
if (module_object->module()->start_function_index >= 0) return -1;
int32_t max_steps = max_executed_instructions;
HandleScope handle_scope(isolate); // Avoid leaking handles.
Zone reference_module_zone(isolate->allocator(), "wasm reference module");
DirectHandle<WasmModuleObject> module_ref = CompileReferenceModule(
isolate, module_object->native_module()->wire_bytes(), &max_steps);
DirectHandle<WasmInstanceObject> instance_ref;
// Before execution, there should be no dangling nondeterminism registered on
// the engine.
DCHECK(!WasmEngine::had_nondeterminism());
// Try to instantiate the reference instance, return if it fails.
{
ErrorThrower thrower(isolate, "ExecuteAgainstReference");
if (!GetWasmEngine()
->SyncInstantiate(isolate, &thrower, module_ref, {},
{}) // no imports & memory
.ToHandle(&instance_ref)) {
isolate->clear_exception();
thrower.Reset(); // Ignore errors.
return -1;
}
}
// Get the "main" exported function. Do nothing if it does not exist.
DirectHandle<WasmExportedFunction> main_function;
if (!testing::GetExportedFunction(isolate, instance_ref, "main")
.ToHandle(&main_function)) {
return -1;
}
struct OomCallbackData {
Isolate* isolate;
bool heap_limit_reached{false};
size_t initial_limit{0};
};
OomCallbackData oom_callback_data{isolate};
auto heap_limit_callback = [](void* raw_data, size_t current_limit,
size_t initial_limit) -> size_t {
OomCallbackData* data = reinterpret_cast<OomCallbackData*>(raw_data);
data->heap_limit_reached = true;
data->isolate->TerminateExecution();
data->initial_limit = initial_limit;
// Return a slightly raised limit, just to make it to the next
// interrupt check point, where execution will terminate.
return initial_limit * 1.25;
};
isolate->heap()->AddNearHeapLimitCallback(heap_limit_callback,
&oom_callback_data);
Tagged<WasmExportedFunctionData> func_data =
main_function->shared()->wasm_exported_function_data();
const FunctionSig* sig = func_data->instance_data()
->module()
->functions[func_data->function_index()]
.sig;
auto compiled_args = testing::MakeDefaultArguments(isolate, sig);
std::unique_ptr<const char[]> exception_ref;
int32_t result_ref = testing::CallWasmFunctionForTesting(
isolate, instance_ref, "main", base::VectorOf(compiled_args),
&exception_ref);
bool execute = true;
// Reached max steps, do not try to execute the test module as it might
// never terminate.
if (max_steps < 0) execute = false;
// If there is nondeterminism, we cannot guarantee the behavior of the test
// module, and in particular it may not terminate.
if (WasmEngine::clear_nondeterminism()) execute = false;
// Similar to max steps reached, also discard modules that need too much
// memory.
isolate->heap()->RemoveNearHeapLimitCallback(heap_limit_callback,
oom_callback_data.initial_limit);
if (oom_callback_data.heap_limit_reached) {
execute = false;
isolate->CancelTerminateExecution();
}
#if V8_ENABLE_DRUMBRAKE
if (is_wasm_jitless) {
v8::internal::v8_flags.jitless = true;
v8::internal::v8_flags.wasm_jitless = true;
FlagList::EnforceFlagImplications();
v8::internal::wasm::WasmInterpreterThread::Initialize();
ClearJsToWasmWrappersForTesting(isolate);
// Compiled WasmCode objects should be cleared before running drumbrake.
module_ref = Handle<WasmModuleObject>::null();
isolate->heap()->CollectAllGarbage(GCFlag::kNoFlags,
i::GarbageCollectionReason::kTesting);
// The module should be validated when compiled for jitless mode.
// But, we already compiled the module without jitless for the reference
// instance. So, we run the validation here before running drumbrake.
auto enabled_features = WasmEnabledFeatures::FromIsolate(isolate);
WasmDetectedFeatures unused_detected_features;
ModuleDecoderImpl decoder(
enabled_features, module_object->native_module()->wire_bytes(),
ModuleOrigin::kWasmOrigin, &unused_detected_features);
if (decoder.DecodeModule(/*validate_functions=*/true).failed()) return -1;
}
#endif // V8_ENABLE_DRUMBRAKE
if (exception_ref) {
if (strcmp(exception_ref.get(),
"RangeError: Maximum call stack size exceeded") == 0) {
// There was a stack overflow, which may happen nondeterministically. We
// cannot guarantee the behavior of the test module, and in particular it
// may not terminate.
execute = false;
}
}
if (!execute) {
// Before discarding the module, see if Turbofan runs into any DCHECKs.
TierUpAllForTesting(isolate, instance_ref->trusted_data(isolate));
return -1;
}
// Instantiate a fresh instance for the actual (non-ref) execution.
DirectHandle<WasmInstanceObject> instance;
{
ErrorThrower thrower(isolate, "ExecuteAgainstReference (second)");
// We instantiated before, so the second instantiation must also succeed.
if (!GetWasmEngine()
->SyncInstantiate(isolate, &thrower, module_object, {},
{}) // no imports & memory
.ToHandle(&instance)) {
DCHECK(thrower.error());
// The only reason to fail the second instantiation should be OOM.
if (strstr(thrower.error_msg(), "Out of memory")) {
// The initial memory size might be too large for instantiation
// (especially on 32 bit systems), therefore do not treat it as a fuzzer
// failure.
return -1;
}
FATAL("Second instantiation failed unexpectedly: %s",
thrower.error_msg());
}
DCHECK(!thrower.error());
}
std::unique_ptr<const char[]> exception;
int32_t result = testing::CallWasmFunctionForTesting(
isolate, instance, "main", base::VectorOf(compiled_args), &exception);
// Also the second run can hit nondeterminism which was not hit before (when
// growing memory). In that case, do not compare results.
// TODO(384781857): Due to nondeterminism, the second run could even not
// terminate. If this happens often enough we should do something about this.
if (WasmEngine::clear_nondeterminism()) return -1;
if ((exception_ref != nullptr) != (exception != nullptr)) {
FATAL("Exception mismatch! Expected: <%s>; got: <%s>",
exception_ref ? exception_ref.get() : "<no exception>",
exception ? exception.get() : "<no exception>");
}
if (!exception) {
CHECK_EQ(result_ref, result);
}
return 0;
}
void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
bool compiles) {
StdoutStream os;
GenerateTestCase(os, isolate, wire_bytes, compiles, false, "");
os.flush();
}
void GenerateTestCase(StdoutStream& os, Isolate* isolate,
ModuleWireBytes wire_bytes, bool compiles,
bool emit_call_main, std::string_view extra_flags) {
// Libfuzzer sometimes runs a test twice (for detecting memory leaks), and in
// this case we do not want multiple outputs by this function.
// Similarly if we explicitly execute the same test multiple times (via
// `-runs=N`).
static std::atomic<bool> did_output_before{false};
if (did_output_before.exchange(true)) return;
constexpr bool kVerifyFunctions = false;
auto enabled_features = WasmEnabledFeatures::FromIsolate(isolate);
WasmDetectedFeatures unused_detected_features;
ModuleResult module_res = DecodeWasmModule(
enabled_features, wire_bytes.module_bytes(), kVerifyFunctions,
ModuleOrigin::kWasmOrigin, &unused_detected_features);
CHECK_WITH_MSG(module_res.ok(), module_res.error().message().c_str());
WasmModule* module = module_res.value().get();
CHECK_NOT_NULL(module);
AccountingAllocator allocator;
Zone zone(&allocator, "constant expression zone");
MultiLineStringBuilder out;
NamesProvider names(module, wire_bytes.module_bytes());
MjsunitModuleDis disassembler(out, module, &names, wire_bytes, &allocator,
!compiles);
disassembler.PrintModule(extra_flags, emit_call_main);
const bool offsets = false; // Not supported by MjsunitModuleDis.
out.WriteTo(os, offsets);
}
namespace {
std::vector<uint8_t> CreateDummyModuleWireBytes(Zone* zone) {
// Build a simple module with a few types to pre-populate the type
// canonicalizer.
WasmModuleBuilder builder(zone);
const bool is_final = true;
builder.AddRecursiveTypeGroup(0, 2);
builder.AddArrayType(zone->New<ArrayType>(kWasmF32, true), is_final);
StructType::Builder struct_builder(zone, 2, false);
struct_builder.AddField(kWasmI64, false);
struct_builder.AddField(kWasmExternRef, false);
builder.AddStructType(struct_builder.Build(), !is_final);
FunctionSig::Builder sig_builder(zone, 1, 0);
sig_builder.AddReturn(kWasmI32);
builder.AddSignature(sig_builder.Get(), is_final);
ZoneBuffer buffer{zone};
builder.WriteTo(&buffer);
return std::vector<uint8_t>(buffer.begin(), buffer.end());
}
} // namespace
void AddDummyTypesToTypeCanonicalizer(Isolate* isolate, Zone* zone) {
const size_t type_count = GetTypeCanonicalizer()->GetCurrentNumberOfTypes();
testing::SetupIsolateForWasmModule(isolate);
// Cache (and leak) the wire bytes, so they don't need to be rebuilt on each
// run.
static const std::vector<uint8_t> wire_bytes =
CreateDummyModuleWireBytes(zone);
const bool is_valid = GetWasmEngine()->SyncValidate(
isolate, WasmEnabledFeatures(), CompileTimeImportsForFuzzing(),
base::VectorOf(wire_bytes));
CHECK(is_valid);
// As the types are reset on each run by the fuzzer, the validation should
// have added new types to the TypeCanonicalizer.
CHECK_GT(GetTypeCanonicalizer()->GetCurrentNumberOfTypes(), type_count);
}
void EnableExperimentalWasmFeatures(v8::Isolate* isolate) {
struct EnableExperimentalWasmFeatures {
explicit EnableExperimentalWasmFeatures(v8::Isolate* isolate) {
// Enable all staged features.
#define ENABLE_STAGED_FEATURES(feat, ...) \
v8_flags.experimental_wasm_##feat = true;
FOREACH_WASM_STAGING_FEATURE_FLAG(ENABLE_STAGED_FEATURES)
#undef ENABLE_STAGED_FEATURES
// Enable non-staged experimental features or other experimental flags
// that we also want to fuzz, e.g., new optimizations.
// Note: If you add a Wasm feature here, you will also have to add the
// respective flag(s) to the mjsunit/wasm/generate-random-module.js test,
// otherwise that fails on an unsupported feature.
// You may also want to add the flag(s) to the JS file header in
// `PrintModule()` of `mjsunit-module-disassembler-impl.h`, to make bugs
// easier to reproduce with generated mjsunit test cases.
// See https://crbug.com/335082212.
v8_flags.wasm_inlining_call_indirect = true;
// Enforce implications from enabling features.
FlagList::EnforceFlagImplications();
// Last, install any conditional features. Implications are handled
// implicitly.
isolate->InstallConditionalFeatures(isolate->GetCurrentContext());
}
};
// The compiler will properly synchronize the constructor call.
static EnableExperimentalWasmFeatures one_time_enable_experimental_features(
isolate);
}
void ResetTypeCanonicalizer(v8::Isolate* isolate, Zone* zone) {
v8::internal::Isolate* i_isolate =
reinterpret_cast<v8::internal::Isolate*>(isolate);
// Make sure that there are no NativeModules left referencing the canonical
// types. Collecting NativeModules can require two rounds of GC.
for (int i = 0; i < 2 && GetWasmEngine()->NativeModuleCount() != 0; i++) {
// We need to invoke GC without stack, otherwise the native module may
// survive.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate->heap());
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
}
GetTypeCanonicalizer()->EmptyStorageForTesting();
TypeCanonicalizer::ClearWasmCanonicalTypesForTesting(i_isolate);
AddDummyTypesToTypeCanonicalizer(i_isolate, zone);
}
int WasmExecutionFuzzer::FuzzWasmModule(base::Vector<const uint8_t> data,
bool require_valid) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// Strictly enforce the input size limit. Note that setting "max_len" on the
// fuzzer target is not enough, since different fuzzers are used and not all
// respect that limit.
if (data.size() > max_input_size()) return -1;
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// We explicitly enable staged WebAssembly features here to increase fuzzer
// coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables
// the flag by itself.
EnableExperimentalWasmFeatures(isolate);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
// Clear recursive groups: The fuzzer creates random types in every run. These
// are saved as recursive groups as part of the type canonicalizer, but types
// from previous runs just waste memory.
ResetTypeCanonicalizer(isolate, &zone);
// Clear any exceptions from a prior run.
if (i_isolate->has_exception()) {
i_isolate->clear_exception();
}
v8::TryCatch try_catch(isolate);
HandleScope scope(i_isolate);
ZoneBuffer buffer(&zone);
// The first byte specifies some internal configuration, like which function
// is compiled with which compiler, and other flags.
uint8_t configuration_byte = data.empty() ? 0 : data[0];
if (!data.empty()) data += 1;
// Derive the compiler configuration for the first four functions from the
// configuration byte, to choose for each function between:
// 0: TurboFan
// 1: Liftoff
// 2: Liftoff for debugging
uint8_t tier_mask = 0;
uint8_t debug_mask = 0;
for (int i = 0; i < 4; ++i, configuration_byte /= 3) {
int compiler_config = configuration_byte % 3;
tier_mask |= (compiler_config == 0) << i;
debug_mask |= (compiler_config == 2) << i;
}
if (!GenerateModule(i_isolate, &zone, data, &buffer)) {
return -1;
}
testing::SetupIsolateForWasmModule(i_isolate);
ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool valid = GetWasmEngine()->SyncValidate(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(),
wire_bytes.module_bytes());
if (v8_flags.wasm_fuzzer_gen_test) {
GenerateTestCase(i_isolate, wire_bytes, valid);
}
FlagScope<bool> eager_compile(&v8_flags.wasm_lazy_compilation, false);
// We want to keep dynamic tiering enabled because that changes the code
// Liftoff generates as well as optimizing compilers' behavior (especially
// around inlining). We switch it to synchronous mode to avoid the
// nondeterminism of background jobs finishing at random times.
FlagScope<bool> sync_tier_up(&v8_flags.wasm_sync_tier_up, true);
// The purpose of setting the tier mask (which affects the initial
// compilation of each function) is to deterministically test a combination
// of Liftoff and Turbofan.
FlagScope<int> tier_mask_scope(&v8_flags.wasm_tier_mask_for_testing,
tier_mask);
FlagScope<int> debug_mask_scope(&v8_flags.wasm_debug_mask_for_testing,
debug_mask);
// Reference runs use extra compile settings (like non-determinism detection),
// which would be removed and replaced with a new liftoff function without
// these options.
FlagScope<bool> no_liftoff_code_flushing(&v8_flags.flush_liftoff_code, false);
ErrorThrower thrower(i_isolate, "WasmFuzzerSyncCompile");
MaybeDirectHandle<WasmModuleObject> compiled_module =
GetWasmEngine()->SyncCompile(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(), &thrower,
base::OwnedCopyOf(buffer));
CHECK_EQ(valid, !compiled_module.is_null());
CHECK_EQ(!valid, thrower.error());
if (require_valid && !valid) {
FATAL("Generated module should validate, but got: %s", thrower.error_msg());
}
thrower.Reset();
// Do not execute invalid modules, and return `-1` to avoid adding them to the
// corpus. Even though invalid modules are also somewhat interesting to fuzz,
// we will get them often enough via mutations, so we do not add them to the
// corpus.
if (!valid) return -1;
return ExecuteAgainstReference(i_isolate, compiled_module.ToHandleChecked(),
kDefaultMaxFuzzerExecutedInstructions);
}
} // namespace v8::internal::wasm::fuzzing

107
deps/v8/test/fuzzer/wasm/fuzzer-common.h vendored Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WASM_FUZZER_COMMON_H_
#define WASM_FUZZER_COMMON_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-module.h"
namespace v8::internal {
class StdoutStream;
class WasmInstanceObject;
namespace wasm {
class ZoneBuffer;
}
} // namespace v8::internal
namespace v8::internal::wasm::fuzzing {
// A default value for {max_executed_instructions} in {ExecuteAgainstReference}.
#ifdef USE_SIMULATOR
constexpr int kDefaultMaxFuzzerExecutedInstructions = 16'000;
#else
constexpr int kDefaultMaxFuzzerExecutedInstructions = 1'000'000;
#endif
CompileTimeImports CompileTimeImportsForFuzzing();
// First creates a reference module fully compiled with Liftoff, with
// instrumentation to stop after a given number of steps and to record any
// nondeterminism while executing. If execution finishes within {max_steps},
// {module_object} is instantiated, its "main" function is executed, and the
// result is compared against the reference execution. If non-determinism was
// detected during the reference execution, the result is allowed to differ.
#ifndef V8_ENABLE_DRUMBRAKE
// Returns 0 or -1, see {WasmExecutionFuzzer::FuzzWasmModule}.
V8_WARN_UNUSED_RESULT int ExecuteAgainstReference(
Isolate* isolate, DirectHandle<WasmModuleObject> module_object,
int32_t max_executed_instructions);
#else // V8_ENABLE_DRUMBRAKE
// It turns `--wasm-jitless` flag on for the second execution when
// `is_wasm_jitless` is set.
int ExecuteAgainstReference(Isolate* isolate,
DirectHandle<WasmModuleObject> module_object,
int32_t max_executed_instructions,
bool is_wasm_jitless = false);
void ClearJsToWasmWrappersForTesting(Isolate* isolate);
#endif // V8_ENABLE_DRUMBRAKE
DirectHandle<WasmModuleObject> CompileReferenceModule(
Isolate* isolate, base::Vector<const uint8_t> wire_bytes,
int32_t* max_steps);
void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
bool compiles);
void GenerateTestCase(StdoutStream& os, Isolate* isolate,
ModuleWireBytes wire_bytes, bool compiles,
bool emit_call_main, std::string_view extra_args);
// Validate a module containing a few wasm-gc types. This can be done to
// prepulate the TypeCanonicalizer with a few canonical types, so that a
// module-specific type index is more likely to be different from its canonical
// type index.
void AddDummyTypesToTypeCanonicalizer(Isolate* isolate, Zone* zone);
// On the first call, enables all staged wasm features and experimental features
// that are ready for fuzzing. All subsequent calls are no-ops. This avoids race
// conditions with threads reading the flags. Fuzzers are executed in their own
// process anyway, so this should not interfere with anything.
void EnableExperimentalWasmFeatures(v8::Isolate* isolate);
// Clear the type canonicalizer storage and re-add the dummy types.
void ResetTypeCanonicalizer(v8::Isolate* isolate, Zone* zone);
constexpr int kMaxFuzzerInputSize = 512;
class WasmExecutionFuzzer {
public:
virtual ~WasmExecutionFuzzer() = default;
// The main entry point: Generate a module from the data, using
// `GenerateModule` defined below. Returns `-1` if the test case is not
// interesting for fuzzing; otherwise returns `0`.
// This should be returned to libfuzzer to indicate that the test case should
// not be added to the corpus (see
// https://llvm.org/docs/LibFuzzer.html#rejecting-unwanted-inputs).
V8_WARN_UNUSED_RESULT int FuzzWasmModule(base::Vector<const uint8_t> data,
bool require_valid = false);
virtual size_t max_input_size() const { return kMaxFuzzerInputSize; }
protected:
virtual bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
ZoneBuffer* buffer) = 0;
};
} // namespace v8::internal::wasm::fuzzing
#endif // WASM_FUZZER_COMMON_H_

379
deps/v8/test/fuzzer/wasm/init-expr.cc vendored Normal file
View File

@ -0,0 +1,379 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/base/vector.h"
#include "src/execution/isolate.h"
#include "src/objects/property-descriptor.h"
#include "src/wasm/compilation-environment-inl.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-subtyping.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/flag-utils.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
// This fuzzer fuzzes initializer expressions used e.g. in globals.
// The fuzzer creates a set of globals with initializer expressions and a set of
// functions containing the same body as these initializer expressions.
// The global value should be equal to the result of running the corresponding
// function.
namespace v8::internal::wasm::fuzzing {
#define CHECK_FLOAT_EQ(expected, actual) \
if (std::isnan(expected)) { \
CHECK(std::isnan(actual)); \
} else { \
CHECK_EQ(expected, actual); \
CHECK_EQ(std::signbit(expected), std::signbit(actual)); \
}
namespace {
bool IsNullOrWasmNull(Tagged<Object> obj) {
return IsNull(obj) || IsWasmNull(obj);
}
DirectHandle<Object> GetExport(Isolate* isolate,
DirectHandle<WasmInstanceObject> instance,
const char* name) {
DirectHandle<JSObject> exports_object;
DirectHandle<Name> exports =
isolate->factory()->InternalizeUtf8String("exports");
exports_object = Cast<JSObject>(
JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
DirectHandle<Name> main_name =
isolate->factory()->NewStringFromAsciiChecked(name);
PropertyDescriptor desc;
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
isolate, exports_object, main_name, &desc);
CHECK(property_found.FromMaybe(false));
return desc.value();
}
void CheckEquivalent(const WasmValue& init_lhs, const WasmValue& init_rhs,
const WasmModule& module) {
DisallowGarbageCollection no_gc;
// Stack of elements to be checked.
std::vector<std::pair<WasmValue, WasmValue>> cmp = {{init_lhs, init_rhs}};
using TaggedT = decltype(Tagged<Object>().ptr());
// Map of lhs objects we have already seen to their rhs object on the first
// visit. This is needed to ensure a reasonable runtime for the check.
// Example:
// (array.new $myArray 10 (array.new_default $myArray 10))
// This creates a nested array where each outer array element is the same
// inner array. Without memorizing the inner array, we'd end up performing
// 100+ comparisons.
std::unordered_map<TaggedT, TaggedT> lhs_map;
auto SeenAlready = [&lhs_map](Tagged<Object> lhs, Tagged<Object> rhs) {
auto [iter, inserted] = lhs_map.insert({lhs.ptr(), rhs.ptr()});
if (inserted) return false;
CHECK_EQ(iter->second, rhs.ptr());
return true;
};
auto CheckArray = [&cmp, &SeenAlready](Tagged<Object> lhs,
Tagged<Object> rhs) {
if (SeenAlready(lhs, rhs)) return;
CHECK(IsWasmArray(lhs));
CHECK(IsWasmArray(rhs));
Tagged<WasmArray> lhs_array = Cast<WasmArray>(lhs);
Tagged<WasmArray> rhs_array = Cast<WasmArray>(rhs);
CHECK_EQ(lhs_array->map(), rhs_array->map());
CHECK_EQ(lhs_array->length(), rhs_array->length());
cmp.reserve(cmp.size() + lhs_array->length());
for (uint32_t i = 0; i < lhs_array->length(); ++i) {
cmp.emplace_back(lhs_array->GetElement(i), rhs_array->GetElement(i));
}
};
auto CheckStruct = [&cmp, &SeenAlready](Tagged<Object> lhs,
Tagged<Object> rhs) {
if (SeenAlready(lhs, rhs)) return;
CHECK(IsWasmStruct(lhs));
CHECK(IsWasmStruct(rhs));
Tagged<WasmStruct> lhs_struct = Cast<WasmStruct>(lhs);
Tagged<WasmStruct> rhs_struct = Cast<WasmStruct>(rhs);
CHECK_EQ(lhs_struct->map(), rhs_struct->map());
const CanonicalStructType* type = GetTypeCanonicalizer()->LookupStruct(
lhs_struct->map()->wasm_type_info()->type_index());
uint32_t field_count = type->field_count();
for (uint32_t i = 0; i < field_count; ++i) {
cmp.emplace_back(lhs_struct->GetFieldValue(i),
rhs_struct->GetFieldValue(i));
}
};
// Compare the function result with the global value.
while (!cmp.empty()) {
const auto [lhs, rhs] = cmp.back();
cmp.pop_back();
CHECK_EQ(lhs.type(), rhs.type());
switch (lhs.type().kind()) {
case ValueKind::kF32:
CHECK_FLOAT_EQ(lhs.to_f32(), rhs.to_f32());
break;
case ValueKind::kF64:
CHECK_FLOAT_EQ(lhs.to_f64(), rhs.to_f64());
break;
case ValueKind::kI8:
CHECK_EQ(lhs.to_i8(), rhs.to_i8());
break;
case ValueKind::kI16:
CHECK_EQ(lhs.to_i16(), rhs.to_i16());
break;
case ValueKind::kI32:
CHECK_EQ(lhs.to_i32(), rhs.to_i32());
break;
case ValueKind::kI64:
CHECK_EQ(lhs.to_i64(), rhs.to_i64());
break;
case ValueKind::kS128:
CHECK_EQ(lhs.to_s128(), lhs.to_s128());
break;
case ValueKind::kRef:
case ValueKind::kRefNull: {
Tagged<Object> lhs_ref = *lhs.to_ref();
Tagged<Object> rhs_ref = *rhs.to_ref();
CHECK_EQ(IsNull(lhs_ref), IsNull(rhs_ref));
CHECK_EQ(IsWasmNull(lhs_ref), IsWasmNull(rhs_ref));
switch (lhs.type().heap_representation_non_shared()) {
case HeapType::kFunc:
case HeapType::kI31:
CHECK_EQ(lhs_ref, rhs_ref);
break;
case HeapType::kNoFunc:
case HeapType::kNone:
case HeapType::kNoExn:
CHECK(IsWasmNull(lhs_ref));
CHECK(IsWasmNull(rhs_ref));
break;
case HeapType::kNoExtern:
CHECK(IsNull(lhs_ref));
CHECK(IsNull(rhs_ref));
break;
case HeapType::kExtern:
case HeapType::kAny:
case HeapType::kEq:
case HeapType::kArray:
case HeapType::kStruct:
if (IsNullOrWasmNull(lhs_ref)) break;
if (IsWasmStruct(lhs_ref)) {
CheckStruct(lhs_ref, rhs_ref);
} else if (IsWasmArray(lhs_ref)) {
CheckArray(lhs_ref, rhs_ref);
} else if (IsSmi(lhs_ref)) {
CHECK_EQ(lhs_ref, rhs_ref);
}
break;
default:
CHECK(lhs.type().has_index());
if (IsWasmNull(lhs_ref)) break;
CanonicalTypeIndex type_index = lhs.type().ref_index();
TypeCanonicalizer* types = GetTypeCanonicalizer();
if (types->IsFunctionSignature(type_index)) {
CHECK_EQ(lhs_ref, rhs_ref);
} else if (types->IsStruct(type_index)) {
CheckStruct(lhs_ref, rhs_ref);
} else if (types->IsArray(type_index)) {
CheckArray(lhs_ref, rhs_ref);
} else {
UNIMPLEMENTED();
}
}
break;
}
default:
UNIMPLEMENTED();
}
}
}
void FuzzIt(base::Vector<const uint8_t> data) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// Disable the optimizing compiler. The init expressions can be huge and might
// produce long compilation times. The function is only used as a reference
// and only run once, so use liftoff only as it allows much faster fuzzing.
v8_flags.liftoff_only = true;
// We explicitly enable staged WebAssembly features here to increase fuzzer
// coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables
// the flag by itself.
EnableExperimentalWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
HandleScope scope(i_isolate);
// Clear recursive groups: The fuzzer creates random types in every run. These
// are saved as recursive groups as part of the type canonicalizer, but types
// from previous runs just waste memory.
ResetTypeCanonicalizer(isolate, &zone);
size_t expression_count = 0;
base::Vector<const uint8_t> bytes =
GenerateWasmModuleForInitExpressions(&zone, data, &expression_count);
testing::SetupIsolateForWasmModule(i_isolate);
ModuleWireBytes wire_bytes(bytes.begin(), bytes.end());
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool valid = GetWasmEngine()->SyncValidate(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(),
wire_bytes.module_bytes());
if (v8_flags.wasm_fuzzer_gen_test) {
GenerateTestCase(i_isolate, wire_bytes, valid);
}
CHECK(valid);
FlagScope<bool> eager_compile(&v8_flags.wasm_lazy_compilation, false);
ErrorThrower thrower(i_isolate, "WasmFuzzerSyncCompile");
MaybeDirectHandle<WasmModuleObject> compiled_module =
GetWasmEngine()->SyncCompile(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(), &thrower,
base::OwnedCopyOf(bytes));
CHECK(!compiled_module.is_null());
CHECK(!thrower.error());
thrower.Reset();
CHECK(!i_isolate->has_exception());
DirectHandle<WasmModuleObject> module_object =
compiled_module.ToHandleChecked();
DirectHandle<WasmInstanceObject> instance =
GetWasmEngine()
->SyncInstantiate(i_isolate, &thrower, module_object, {}, {})
.ToHandleChecked();
CHECK_EQ(expression_count,
module_object->native_module()->module()->num_declared_functions);
for (size_t i = 0; i < expression_count; ++i) {
char buffer[22];
snprintf(buffer, sizeof buffer, "f%zu", i);
// Execute corresponding function.
auto function =
Cast<WasmExportedFunction>(GetExport(i_isolate, instance, buffer));
DirectHandle<Object> undefined = i_isolate->factory()->undefined_value();
DirectHandle<Object> function_result =
Execution::Call(i_isolate, function, undefined, {}).ToHandleChecked();
// Get global value.
snprintf(buffer, sizeof buffer, "g%zu", i);
auto global =
Cast<WasmGlobalObject>(GetExport(i_isolate, instance, buffer));
switch (global->type().kind()) {
case ValueKind::kF32: {
float global_val = global->GetF32();
float func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsHeapNumber(*function_result));
func_val = Cast<HeapNumber>(*function_result)->value();
}
CHECK_FLOAT_EQ(func_val, global_val);
break;
}
case ValueKind::kF64: {
double global_val = global->GetF64();
double func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsHeapNumber(*function_result));
func_val = Cast<HeapNumber>(*function_result)->value();
}
CHECK_FLOAT_EQ(func_val, global_val);
break;
}
case ValueKind::kI32: {
int32_t global_val = global->GetI32();
int32_t func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsHeapNumber(*function_result));
func_val = Cast<HeapNumber>(*function_result)->value();
}
CHECK_EQ(func_val, global_val);
break;
}
case ValueKind::kI64: {
int64_t global_val = global->GetI64();
int64_t func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsBigInt(*function_result));
bool lossless;
func_val = Cast<BigInt>(*function_result)->AsInt64(&lossless);
CHECK(lossless);
}
CHECK_EQ(func_val, global_val);
break;
}
case ValueKind::kRef:
case ValueKind::kRefNull: {
// For reference types the expectations are more limited.
// Any struct.new would create a new object, so reference equality
// comparisons will not work.
DirectHandle<Object> global_val = global->GetRef();
CHECK_EQ(IsUndefined(*global_val), IsUndefined(*function_result));
CHECK_EQ(IsNullOrWasmNull(*global_val),
IsNullOrWasmNull(*function_result));
if (!IsNullOrWasmNull(*global_val)) {
if (IsSubtypeOf(global->type(), kWasmFuncRef,
module_object->module())) {
// For any function the global should be an internal function
// whose external function equals the call result. (The call goes
// through JS conversions while the global is accessed directly.)
CHECK(IsWasmFuncRef(*global_val));
CHECK(
WasmExportedFunction::IsWasmExportedFunction(*function_result));
CHECK(*WasmInternalFunction::GetOrCreateExternal(direct_handle(
Cast<WasmFuncRef>(*global_val)->internal(i_isolate),
i_isolate)) == *function_result);
} else {
// On arrays and structs, perform a deep comparison.
DisallowGarbageCollection no_gc;
WasmValue global_value =
instance->trusted_data(i_isolate)->GetGlobalValue(
i_isolate, instance->module()->globals[i]);
WasmValue func_value(function_result, global_value.type());
CheckEquivalent(global_value, func_value, *module_object->module());
}
}
break;
}
default:
UNIMPLEMENTED();
}
}
}
} // anonymous namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzIt({data, size});
return 0;
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,3 @@
choongwoo.han@microsoft.com
paolosev@microsoft.com
sethb@microsoft.com

View File

@ -0,0 +1,71 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/v8-context.h"
#include "include/v8-isolate.h"
#include "src/execution/isolate-inl.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
auto rnd_gen = MersenneTwister(reinterpret_cast<const char*>(data), size);
InitializeDrumbrakeForFuzzing();
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
v8::internal::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
// Clear any pending exceptions from a prior run.
if (i_isolate->has_exception()) {
i_isolate->clear_exception();
}
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// Drumbrake may not support some experimental features yet.
// EnableExperimentalWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate);
ModuleWireBytes wire_bytes(data, data + size);
HandleScope scope(i_isolate);
ErrorThrower thrower(i_isolate, "wasm fuzzer");
DirectHandle<i::WasmModuleObject> module_object;
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool compiles =
GetWasmEngine()
->SyncCompile(i_isolate, enabled_features, CompileTimeImports{},
&thrower,
v8::base::OwnedCopyOf(wire_bytes.module_bytes()))
.ToHandle(&module_object);
// TODO(338326645): Add similar GenerateTestCase code for wasm fast
// interpreter.
// if (v8_flags.wasm_fuzzer_gen_test) {
// GenerateTestCase(i_isolate, wire_bytes, compiles);
// }
if (compiles) {
FastInterpretAndExecuteModule(i_isolate, module_object, rnd_gen);
}
thrower.Reset();
// Pump the message loop and run micro tasks, e.g. GC finalization tasks.
support->PumpMessageLoop(v8::platform::MessageLoopBehavior::kDoNotWait);
isolate->PerformMicrotaskCheckpoint();
return 0;
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,28 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Fuzzer that may generate WasmGC+SIMD expressions.
bool GenerateModule(i::Isolate* isolate, i::Zone* zone,
v8::base::Vector<const uint8_t> data,
i::wasm::ZoneBuffer* buffer) {
v8::base::Vector<const uint8_t> wire_bytes =
fuzzing::GenerateRandomWasmModule(
zone, fuzzing::WasmModuleGenerationOptions::All(), data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return LLVMFuzzerTestOneInputCommon(data, size, GenerateModule);
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,27 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
bool GenerateModule(i::Isolate* isolate, i::Zone* zone,
v8::base::Vector<const uint8_t> data,
i::wasm::ZoneBuffer* buffer) {
v8::base::Vector<const uint8_t> wire_bytes =
fuzzing::GenerateRandomWasmModule(
zone, fuzzing::WasmModuleGenerationOptions::MVP(), data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return LLVMFuzzerTestOneInputCommon(data, size, GenerateModule);
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,31 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data, ZoneBuffer* buffer) {
ValueType kIntTypes4[4];
FunctionSig sig_i_iii{1, 3, kIntTypes4};
WasmModuleBuilder builder(zone);
WasmFunctionBuilder* f = builder.AddFunction(&sig_i_iii);
f->EmitCode(data.begin(), static_cast<uint32_t>(data.size()));
f->Emit(kExprEnd);
builder.AddExport(base::CStrVector("main"), f);
builder.AddMemory(0, 32);
builder.WriteTo(buffer);
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return LLVMFuzzerTestOneInputCommon(data, size, GenerateModule);
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,89 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include "include/libplatform/libplatform.h"
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/execution/isolate-inl.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// We reduce the maximum memory size and table size of WebAssembly instances
// to avoid OOMs in the fuzzer.
v8_flags.wasm_max_mem_pages = 32;
v8_flags.wasm_max_table_size = 100;
// We need to reset jitless flags set by a previous run.
v8::internal::v8_flags.jitless = false;
v8::internal::v8_flags.wasm_jitless = false;
FlagList::EnforceFlagImplications();
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
// We also need to clear JsToWasm wrappers from a previous run.
ClearJsToWasmWrappersForTesting(i_isolate);
// Clear any pending exceptions from a prior run.
if (i_isolate->has_exception()) {
i_isolate->clear_exception();
}
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// Drumbrake may not support some experimental features yet.
// EnableExperimentalWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate);
ModuleWireBytes wire_bytes(data, data + size);
HandleScope scope(i_isolate);
ErrorThrower thrower(i_isolate, "wasm interpreter diff fuzzer");
DirectHandle<WasmModuleObject> module_object;
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool compiles =
GetWasmEngine()
->SyncCompile(i_isolate, enabled_features,
fuzzing::CompileTimeImportsForFuzzing(), &thrower,
v8::base::OwnedCopyOf(wire_bytes.module_bytes()))
.ToHandle(&module_object);
if (v8_flags.wasm_fuzzer_gen_test) {
fuzzing::GenerateTestCase(i_isolate, wire_bytes, compiles);
}
int module_result = -1;
if (compiles) {
module_result = fuzzing::ExecuteAgainstReference(
i_isolate, module_object,
fuzzing::kDefaultMaxFuzzerExecutedInstructions,
/* edge_is_wasm_jitless */ true);
}
// Pump the message loop and run micro tasks, e.g. GC finalization tasks.
support->PumpMessageLoop(v8::platform::MessageLoopBehavior::kDoNotWait);
isolate->PerformMicrotaskCheckpoint();
return module_result;
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,519 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
#include <ctime>
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-function.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "include/v8-metrics.h"
#include "src/api/api-inl.h"
#include "src/execution/isolate.h"
#include "src/objects/objects-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/utils/ostreams.h"
#include "src/wasm/baseline/liftoff-compiler.h"
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/interpreter/wasm-interpreter-runtime.h"
#include "src/wasm/interpreter/wasm-interpreter.h"
#include "src/wasm/module-instantiate.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-opcodes-inl.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/wasm/flag-utils.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
std::mt19937_64 MersenneTwister(const char* data, size_t size) {
std::mt19937_64 rng;
std::string str = std::string(data, size);
std::size_t hash = std::hash<std::string>()(str);
rng.seed(hash);
return rng;
}
class WasmInterpretationResult {
public:
static WasmInterpretationResult Failed() { return {kFailed, 0, false}; }
static WasmInterpretationResult Trapped(bool possible_nondeterminism) {
return {kTrapped, 0, possible_nondeterminism};
}
static WasmInterpretationResult Finished(int32_t result,
bool possible_nondeterminism) {
return {kFinished, result, possible_nondeterminism};
}
// {failed()} captures different reasons: The module was invalid, no function
// to call was found in the module, the function did not terminate within a
// limited number of steps, or a stack overflow happened.
bool failed() const { return status_ == kFailed; }
bool trapped() const { return status_ == kTrapped; }
bool finished() const { return status_ == kFinished; }
int32_t result() const {
DCHECK_EQ(status_, kFinished);
return result_;
}
bool possible_nondeterminism() const { return possible_nondeterminism_; }
private:
enum Status { kFinished, kTrapped, kFailed };
const Status status_;
const int32_t result_;
const bool possible_nondeterminism_;
WasmInterpretationResult(Status status, int32_t result,
bool possible_nondeterminism)
: status_(status),
result_(result),
possible_nondeterminism_(possible_nondeterminism) {}
};
// Interprets the given module, starting at the function specified by
// {function_index}. The return type of the function has to be int32. The module
// should not have any imports or exports.
WasmInterpretationResult FastInterpretWasmModule(
Isolate* isolate, DirectHandle<WasmInstanceObject> instance,
int32_t function_index, const std::vector<WasmValue>& args,
std::vector<WasmValue>& rets) {
Zone zone(isolate->allocator(), ZONE_NAME);
v8::internal::HandleScope scope(isolate);
const WasmFunction* func = &instance->module()->functions[function_index];
CHECK(func->exported);
// This would normally be handled by export wrappers.
if (!IsJSCompatibleSignature(
reinterpret_cast<const wasm::CanonicalSig*>(func->sig))) {
return WasmInterpretationResult::Trapped(false);
}
CHECK(v8_flags.wasm_jitless);
isolate->clear_exception();
wasm::WasmInterpreterThread* thread =
wasm::WasmInterpreterThread::GetCurrentInterpreterThread(isolate);
DirectHandle<Tuple2> interpreter_object =
v8::internal::WasmTrustedInstanceData::GetOrCreateInterpreterObject(
instance);
int* thread_in_wasm_code = trap_handler::GetThreadInWasmThreadLocalAddress();
*thread_in_wasm_code = true;
// Assume an instance can run in only one thread.
wasm::InterpreterHandle* handle =
wasm::GetOrCreateInterpreterHandle(isolate, interpreter_object);
for (const WasmValue& arg : args) {
if (arg.type().is_reference()) {
// We should pass WasmNull as null argument, not a JS null value.
CHECK(!IsNull(*arg.to_ref(), isolate));
}
}
bool success = handle->wasm::InterpreterHandle::Execute(
thread, 0, static_cast<uint32_t>(function_index), args, rets);
*thread_in_wasm_code = false;
// Returned values should not be the hole value.
if (success) {
for (auto& wasm_value : rets) {
if (wasm_value.type().is_reference())
CHECK(!IsTheHole(*wasm_value.to_ref()));
}
}
return success ? WasmInterpretationResult::Finished(0, false)
: WasmInterpretationResult::Failed();
}
Handle<JSObject> GetOrCreateModuleNamespaceObject(Isolate* isolate,
Handle<JSObject> ffi_object,
Handle<String> module_name) {
PropertyDescriptor desc;
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
isolate, ffi_object, module_name, &desc);
if (property_found.FromMaybe(false)) {
return Cast<JSObject>(desc.value());
}
Handle<JSObject> module_namespace =
isolate->factory()->NewJSObject(isolate->object_function());
JSObject::DefinePropertyOrElementIgnoreAttributes(ffi_object, module_name,
module_namespace)
.Assert();
return module_namespace;
}
Handle<JSFunction> GenerateJSFunction(Isolate* isolate) {
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8::Local<v8::String> fn_body =
v8::String::NewFromUtf8(v8_isolate, "return arguments[1] + 0x414141;")
.ToLocalChecked();
v8::ScriptCompiler::Source script_source(fn_body);
v8::Local<v8::Function> function =
v8::ScriptCompiler::CompileFunction(v8_isolate->GetCurrentContext(),
&script_source)
.ToLocalChecked();
return Cast<JSFunction>(
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(function)));
}
MaybeDirectHandle<WasmTableObject> GenerateWasmTable(
Isolate* isolate, DirectHandle<WasmModuleObject> module_object,
uint32_t table_index) {
const WasmTable& table = module_object->module()->tables[table_index];
uint32_t table_initial = 10;
uint32_t table_maximum = 30;
bool has_maximum = true;
if (table.type == wasm::kWasmAnyRef) {
DirectHandle<WasmTableObject> table_obj = WasmTableObject::New(
isolate, Handle<WasmTrustedInstanceData>(), wasm::kWasmAnyRef,
wasm::CanonicalValueType{wasm::kWasmAnyRef}, table_initial, has_maximum,
table_maximum, isolate->factory()->undefined_value(),
wasm::AddressType::kI32);
// Anyref (externref) Table
Handle<FixedArray> externref_backing_store{table_obj->entries(), isolate};
externref_backing_store->set(0, Smi::FromInt(0x414141));
externref_backing_store->set(
1, *isolate->factory()->NewJSObject(isolate->object_function()));
return table_obj;
} else if (table.type == wasm::kWasmFuncRef) {
return WasmTableObject::New(
isolate, Handle<WasmTrustedInstanceData>(), wasm::kWasmFuncRef,
wasm::CanonicalValueType{wasm::kWasmFuncRef}, table_initial,
has_maximum, table_maximum, isolate->factory()->null_value(),
wasm::AddressType::kI32);
}
// Not supported type
return {};
}
Handle<String> ExtractUtf8StringFromModuleBytes(
Isolate* isolate, base::Vector<const uint8_t> wire_bytes,
wasm::WireBytesRef ref) {
base::Vector<const uint8_t> name_vec =
wire_bytes.SubVector(ref.offset(), ref.end_offset());
return isolate->factory()->InternalizeUtf8String(
base::Vector<const char>::cast(name_vec));
}
DirectHandle<JSObject> CreateImportObject(
Isolate* isolate, DirectHandle<WasmModuleObject> module_object) {
Handle<JSObject> ffi_object =
isolate->factory()->NewJSObject(isolate->object_function());
base::Vector<const uint8_t> wire_bytes =
module_object->native_module()->wire_bytes();
for (size_t index = 0; index < module_object->module()->import_table.size();
++index) {
const WasmImport& import = module_object->module()->import_table[index];
Handle<String> module_name = ExtractUtf8StringFromModuleBytes(
isolate, wire_bytes, import.module_name);
Handle<String> field_name = ExtractUtf8StringFromModuleBytes(
isolate, wire_bytes, import.field_name);
Handle<JSObject> module_namespace =
GetOrCreateModuleNamespaceObject(isolate, ffi_object, module_name);
switch (import.kind) {
case kExternalFunction: {
// TODO: Support other types of external functions such as wasm?
// It currently supports only external JS function.
Handle<JSFunction> fn_obj = GenerateJSFunction(isolate);
JSObject::DefinePropertyOrElementIgnoreAttributes(module_namespace,
field_name, fn_obj)
.Assert();
break;
}
case kExternalTable: {
uint32_t table_index = import.index;
DirectHandle<WasmTableObject> table_obj;
if (GenerateWasmTable(isolate, module_object, table_index)
.ToHandle(&table_obj)) {
JSObject::DefinePropertyOrElementIgnoreAttributes(
module_namespace, field_name, table_obj)
.Assert();
}
break;
}
case kExternalMemory: {
// Memory
SharedFlag shared = SharedFlag::kNotShared;
int memory_initial = 1;
int memory_maximum = 32;
DirectHandle<WasmMemoryObject> memory_obj;
if (WasmMemoryObject::New(isolate, memory_initial, memory_maximum,
shared, wasm::AddressType::kI32)
.ToHandle(&memory_obj)) {
JSObject::DefinePropertyOrElementIgnoreAttributes(
module_namespace, field_name, memory_obj)
.Assert();
}
break;
}
case kExternalGlobal: {
// Global
const uint32_t offset = 0;
const WasmGlobal& global =
module_object->module()->globals[import.index];
DirectHandle<WasmTrustedInstanceData> trusted_data =
WasmTrustedInstanceData::New(isolate, module_object, false);
MaybeDirectHandle<WasmGlobalObject> maybe_global_obj =
WasmGlobalObject::New(isolate, trusted_data,
MaybeHandle<JSArrayBuffer>(),
MaybeHandle<FixedArray>(), global.type,
offset, global.mutability);
DirectHandle<WasmGlobalObject> global_obj;
if (maybe_global_obj.ToHandle(&global_obj)) {
JSObject::DefinePropertyOrElementIgnoreAttributes(
module_namespace, field_name, global_obj)
.Assert();
}
break;
}
case kExternalTag: {
// Not supported yet.
break;
}
default:
UNREACHABLE();
}
}
return ffi_object;
}
bool InstantiateModule(Isolate* isolate,
DirectHandle<WasmModuleObject> module_object,
DirectHandle<internal::JSObject> imports_obj,
DirectHandle<WasmInstanceObject>* instance) {
ErrorThrower thrower(isolate, "WebAssembly Instantiation");
if (!GetWasmEngine()
->SyncInstantiate(isolate, &thrower, module_object, imports_obj, {})
.ToHandle(instance)) {
isolate->clear_exception();
thrower.Reset(); // Ignore errors.
return false;
}
return true;
}
// Generate an array of default arguments for the given signature, to be used in
// the interpreter.
std::vector<WasmValue> FastMakeDefaultInterpreterArguments(
Isolate* isolate, const WasmModule* module, const FunctionSig* sig,
std::mt19937_64 mt_generator_64) {
size_t param_count = sig->parameter_count();
std::vector<WasmValue> arguments(param_count);
int64_t rand_num = 0;
for (size_t i = 0; i < param_count; ++i) {
rand_num = mt_generator_64();
switch (sig->GetParam(i).kind()) {
case kI32:
arguments[i] = WasmValue(static_cast<int32_t>(rand_num));
break;
case kI64:
arguments[i] = WasmValue(rand_num);
break;
case kF32:
arguments[i] = WasmValue(static_cast<float>(rand_num));
break;
case kF64:
arguments[i] = WasmValue(static_cast<double>(rand_num));
break;
case kRefNull: {
ValueType type = sig->GetParam(i);
wasm::CanonicalValueType canonical_type = wasm::kWasmBottom;
if (type.has_index()) {
canonical_type =
type.Canonicalize(module->canonical_type_id(type.ref_index()));
} else {
canonical_type = CanonicalValueType{type};
}
if (type.heap_representation() == HeapType::kExtern) {
arguments[i] = WasmValue(
Cast<Object>(isolate->factory()->NewHeapNumber(rand_num + 0.125)),
canonical_type);
} else {
arguments[i] =
WasmValue(isolate->factory()->wasm_null(), canonical_type);
}
break;
}
case kS128: {
int64_t rand_num2 = mt_generator_64();
const int64_t simd_value[2] = {rand_num, rand_num2};
arguments[i] = WasmValue(reinterpret_cast<const uint8_t*>(simd_value),
(CanonicalValueType)sig->GetParam(i));
break;
}
case kRef:
case kI8:
case kI16:
case kF16:
case kVoid:
case kTop:
case kBottom:
UNREACHABLE();
}
}
return arguments;
}
void RunInstance(Isolate* isolate, std::mt19937_64 rand_generator,
DirectHandle<WasmModuleObject> module,
DirectHandle<WasmInstanceObject> instance) {
size_t num_exports = instance->module()->export_table.size();
for (size_t i = 0; i < num_exports; ++i) {
WasmExport exp = instance->module()->export_table[i];
if (exp.kind != kExternalFunction) continue;
WasmFunction wfn = instance->module()->functions[exp.index];
std::vector<WasmValue> arguments = FastMakeDefaultInterpreterArguments(
isolate, instance->module(), wfn.sig, rand_generator);
std::vector<WasmValue> retvals(wfn.sig->return_count());
FastInterpretWasmModule(isolate, instance, wfn.func_index, arguments,
retvals);
isolate->clear_exception();
} // end handle loop
}
int FastInterpretAndExecuteModule(Isolate* isolate,
DirectHandle<WasmModuleObject> module_object,
std::mt19937_64 rand_generator) {
// We do not instantiate the module if there is a start function, because a
// start function can contain an infinite loop which we cannot handle.
if (module_object->module()->start_function_index >= 0) return -1;
HandleScope handle_scope(isolate); // Avoid leaking handles.
DirectHandle<JSObject> imports_obj =
CreateImportObject(isolate, module_object);
DirectHandle<WasmInstanceObject> other_instance;
if (!InstantiateModule(isolate, module_object, imports_obj, &other_instance))
return -1;
DirectHandle<WasmInstanceObject> instance;
if (!InstantiateModule(isolate, module_object, imports_obj, &instance))
return -1;
RunInstance(isolate, rand_generator, module_object, other_instance);
RunInstance(isolate, rand_generator, module_object, instance);
return 0;
}
void InitializeDrumbrakeForFuzzing() {
// We should always pass jitless for wasm-jitless.
v8_flags.jitless = true;
v8_flags.wasm_jitless = true;
// This configuration will make fuzzing slower, but helps to reproduce GC bugs
// more reliably.
v8_flags.stress_compaction = true;
v8_flags.gc_interval = 500;
// Avoid restarting the fuzzer process due to timeout when there is an
// infinite loop.
v8_flags.drumbrake_fuzzer_timeout = true;
// We reduce the maximum memory size and table size of WebAssembly instances
// to avoid OOMs in the fuzzer.
v8_flags.wasm_max_mem_pages = 32;
v8_flags.wasm_max_table_size = 100;
FlagList::EnforceFlagImplications();
WasmInterpreterThread::Initialize();
}
int LLVMFuzzerTestOneInputCommon(const uint8_t* data, size_t size,
GenerateModuleFunc generate_module) {
auto rnd_gen = MersenneTwister(reinterpret_cast<const char*>(data), size);
wasm::fuzzing::InitializeDrumbrakeForFuzzing();
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
// Clear any pending exceptions from a prior run.
if (i_isolate->has_exception()) {
i_isolate->clear_exception();
}
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// Drumbrake may not support some experimental features yet.
// wasm::fuzzing::EnableExperimentalWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
wasm::ZoneBuffer buffer(&zone);
if (!generate_module(i_isolate, &zone, {data, size}, &buffer)) {
return 0;
}
wasm::ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
HandleScope scope(i_isolate);
wasm::ErrorThrower thrower(i_isolate, "wasm fuzzer");
DirectHandle<WasmModuleObject> module_object;
auto enabled_features = wasm::WasmEnabledFeatures::FromIsolate(i_isolate);
bool compiles = wasm::GetWasmEngine()
->SyncCompile(i_isolate, enabled_features,
fuzzing::CompileTimeImportsForFuzzing(),
&thrower, v8::base::OwnedCopyOf(buffer))
.ToHandle(&module_object);
// TODO(338326645): add similar GenerateTestCase code for wasm fast
// interpreter.
// if (v8_flags.wasm_fuzzer_gen_test) {
// wasm::fuzzing::GenerateTestCase(i_isolate, wire_bytes, compiles);
// }
int module_result = -1;
if (compiles) {
module_result =
FastInterpretAndExecuteModule(i_isolate, module_object, rnd_gen);
}
thrower.Reset();
// Pump the message loop and run micro tasks, e.g. GC finalization tasks.
support->PumpMessageLoop(v8::platform::MessageLoopBehavior::kDoNotWait);
isolate->PerformMicrotaskCheckpoint();
return module_result;
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,38 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WASM_FAST_INTERPRETER_FUZZER_COMMON_H_
#define WASM_FAST_INTERPRETER_FUZZER_COMMON_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <random>
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module-builder.h"
namespace v8::internal::wasm::fuzzing {
std::mt19937_64 MersenneTwister(const char* data, size_t size);
// Instantiates and interprets exported functions within module_object if
// possible
int FastInterpretAndExecuteModule(Isolate* isolate,
DirectHandle<WasmModuleObject> module_object,
std::mt19937_64 g);
void InitializeDrumbrakeForFuzzing();
typedef std::function<bool(Isolate*, Zone*, v8::base::Vector<const uint8_t>,
ZoneBuffer*)>
GenerateModuleFunc;
int LLVMFuzzerTestOneInputCommon(const uint8_t* data, size_t size,
GenerateModuleFunc generate_module);
} // namespace v8::internal::wasm::fuzzing
#endif // WASM_FAST_INTERPRETER_FUZZER_COMMON_H_

View File

@ -0,0 +1,382 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/base/vector.h"
#include "src/execution/isolate.h"
#include "src/objects/property-descriptor.h"
#include "src/wasm/compilation-environment-inl.h"
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-subtyping.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/flag-utils.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace fuzzing = v8::internal::wasm::fuzzing;
// This fuzzer fuzzes initializer expressions used e.g. in globals.
// The fuzzer creates a set of globals with initializer expressions and a set of
// functions containing the same body as these initializer expressions.
// The global value should be equal to the result of running the corresponding
// function.
namespace v8::internal::wasm::fuzzing {
#define CHECK_FLOAT_EQ(expected, actual) \
if (std::isnan(expected)) { \
CHECK(std::isnan(actual)); \
} else { \
CHECK_EQ(expected, actual); \
CHECK_EQ(std::signbit(expected), std::signbit(actual)); \
}
namespace {
bool IsNullOrWasmNull(Tagged<Object> obj) {
return IsNull(obj) || IsWasmNull(obj);
}
DirectHandle<Object> GetExport(Isolate* isolate,
DirectHandle<WasmInstanceObject> instance,
const char* name) {
DirectHandle<JSObject> exports_object;
DirectHandle<Name> exports =
isolate->factory()->InternalizeUtf8String("exports");
exports_object = Cast<JSObject>(
JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
DirectHandle<Name> main_name =
isolate->factory()->NewStringFromAsciiChecked(name);
PropertyDescriptor desc;
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
isolate, exports_object, main_name, &desc);
CHECK(property_found.FromMaybe(false));
return desc.value();
}
void CheckEquivalent(const WasmValue& init_lhs, const WasmValue& init_rhs,
const WasmModule& module) {
DisallowGarbageCollection no_gc;
// Stack of elements to be checked.
std::vector<std::pair<WasmValue, WasmValue>> cmp = {{init_lhs, init_rhs}};
using TaggedT = decltype(Tagged<Object>().ptr());
// Map of lhs objects we have already seen to their rhs object on the first
// visit. This is needed to ensure a reasonable runtime for the check.
// Example:
// (array.new $myArray 10 (array.new_default $myArray 10))
// This creates a nested array where each outer array element is the same
// inner array. Without memorizing the inner array, we'd end up performing
// 100+ comparisons.
std::unordered_map<TaggedT, TaggedT> lhs_map;
auto SeenAlready = [&lhs_map](Tagged<Object> lhs, Tagged<Object> rhs) {
auto [iter, inserted] = lhs_map.insert({lhs.ptr(), rhs.ptr()});
if (inserted) return false;
CHECK_EQ(iter->second, rhs.ptr());
return true;
};
auto CheckArray = [&cmp, &SeenAlready](Tagged<Object> lhs,
Tagged<Object> rhs) {
if (SeenAlready(lhs, rhs)) return;
CHECK(IsWasmArray(lhs));
CHECK(IsWasmArray(rhs));
Tagged<WasmArray> lhs_array = Cast<WasmArray>(lhs);
Tagged<WasmArray> rhs_array = Cast<WasmArray>(rhs);
CHECK_EQ(lhs_array->map(), rhs_array->map());
CHECK_EQ(lhs_array->length(), rhs_array->length());
cmp.reserve(cmp.size() + lhs_array->length());
for (uint32_t i = 0; i < lhs_array->length(); ++i) {
cmp.emplace_back(lhs_array->GetElement(i), rhs_array->GetElement(i));
}
};
auto CheckStruct = [&cmp, &SeenAlready](Tagged<Object> lhs,
Tagged<Object> rhs) {
if (SeenAlready(lhs, rhs)) return;
CHECK(IsWasmStruct(lhs));
CHECK(IsWasmStruct(rhs));
Tagged<WasmStruct> lhs_struct = Cast<WasmStruct>(lhs);
Tagged<WasmStruct> rhs_struct = Cast<WasmStruct>(rhs);
CHECK_EQ(lhs_struct->map(), rhs_struct->map());
const CanonicalStructType* type = GetTypeCanonicalizer()->LookupStruct(
lhs_struct->map()->wasm_type_info()->type_index());
uint32_t field_count = type->field_count();
for (uint32_t i = 0; i < field_count; ++i) {
cmp.emplace_back(lhs_struct->GetFieldValue(i),
rhs_struct->GetFieldValue(i));
}
};
// Compare the function result with the global value.
while (!cmp.empty()) {
const auto [lhs, rhs] = cmp.back();
cmp.pop_back();
CHECK_EQ(lhs.type(), rhs.type());
switch (lhs.type().kind()) {
case ValueKind::kF32:
CHECK_FLOAT_EQ(lhs.to_f32(), rhs.to_f32());
break;
case ValueKind::kF64:
CHECK_FLOAT_EQ(lhs.to_f64(), rhs.to_f64());
break;
case ValueKind::kI8:
CHECK_EQ(lhs.to_i8(), rhs.to_i8());
break;
case ValueKind::kI16:
CHECK_EQ(lhs.to_i16(), rhs.to_i16());
break;
case ValueKind::kI32:
CHECK_EQ(lhs.to_i32(), rhs.to_i32());
break;
case ValueKind::kI64:
CHECK_EQ(lhs.to_i64(), rhs.to_i64());
break;
case ValueKind::kS128:
CHECK_EQ(lhs.to_s128(), lhs.to_s128());
break;
case ValueKind::kRef:
case ValueKind::kRefNull: {
Tagged<Object> lhs_ref = *lhs.to_ref();
Tagged<Object> rhs_ref = *rhs.to_ref();
CHECK_EQ(IsNull(lhs_ref), IsNull(rhs_ref));
CHECK_EQ(IsWasmNull(lhs_ref), IsWasmNull(rhs_ref));
switch (lhs.type().heap_representation_non_shared()) {
case HeapType::kFunc:
case HeapType::kI31:
CHECK_EQ(lhs_ref, rhs_ref);
break;
case HeapType::kNoFunc:
case HeapType::kNone:
case HeapType::kNoExn:
CHECK(IsWasmNull(lhs_ref));
CHECK(IsWasmNull(rhs_ref));
break;
case HeapType::kNoExtern:
CHECK(IsNull(lhs_ref));
CHECK(IsNull(rhs_ref));
break;
case HeapType::kExtern:
case HeapType::kAny:
case HeapType::kEq:
case HeapType::kArray:
case HeapType::kStruct:
if (IsNullOrWasmNull(lhs_ref)) break;
if (IsWasmStruct(lhs_ref)) {
CheckStruct(lhs_ref, rhs_ref);
} else if (IsWasmArray(lhs_ref)) {
CheckArray(lhs_ref, rhs_ref);
} else if (IsSmi(lhs_ref)) {
CHECK_EQ(lhs_ref, rhs_ref);
}
break;
default:
CHECK(lhs.type().has_index());
if (IsWasmNull(lhs_ref)) break;
CanonicalTypeIndex type_index = lhs.type().ref_index();
TypeCanonicalizer* types = GetTypeCanonicalizer();
if (types->IsFunctionSignature(type_index)) {
CHECK_EQ(lhs_ref, rhs_ref);
} else if (types->IsStruct(type_index)) {
CheckStruct(lhs_ref, rhs_ref);
} else if (types->IsArray(type_index)) {
CheckArray(lhs_ref, rhs_ref);
} else {
UNIMPLEMENTED();
}
}
break;
}
default:
UNIMPLEMENTED();
}
}
}
// Forked from test\fuzzer\wasm\init-expr.cc to work with the Wasm interpreter.
// TODO(choongwoo.han@microsoft.com): Deduplicate code.
void FuzzIt(base::Vector<const uint8_t> data) {
i::wasm::fuzzing::InitializeDrumbrakeForFuzzing();
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// We explicitly enable staged WebAssembly features here to increase fuzzer
// coverage. For libfuzzer fuzzers it is not possible that the fuzzer enables
// the flag by itself.
EnableExperimentalWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
HandleScope scope(i_isolate);
// Clear recursive groups: The fuzzer creates random types in every run. These
// are saved as recursive groups as part of the type canonicalizer, but types
// from previous runs just waste memory.
ResetTypeCanonicalizer(isolate, &zone);
size_t expression_count = 0;
base::Vector<const uint8_t> bytes =
GenerateWasmModuleForInitExpressions(&zone, data, &expression_count);
testing::SetupIsolateForWasmModule(i_isolate);
ModuleWireBytes wire_bytes(bytes.begin(), bytes.end());
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool valid = GetWasmEngine()->SyncValidate(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(),
wire_bytes.module_bytes());
if (v8_flags.wasm_fuzzer_gen_test) {
GenerateTestCase(i_isolate, wire_bytes, valid);
}
CHECK(valid);
FlagScope<bool> eager_compile(&v8_flags.wasm_lazy_compilation, false);
ErrorThrower thrower(i_isolate, "WasmFuzzerSyncCompile");
MaybeDirectHandle<WasmModuleObject> compiled_module =
GetWasmEngine()->SyncCompile(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(), &thrower,
base::OwnedCopyOf(bytes));
CHECK(!compiled_module.is_null());
CHECK(!thrower.error());
thrower.Reset();
CHECK(!i_isolate->has_exception());
DirectHandle<WasmModuleObject> module_object =
compiled_module.ToHandleChecked();
DirectHandle<WasmInstanceObject> instance =
GetWasmEngine()
->SyncInstantiate(i_isolate, &thrower, module_object, {}, {})
.ToHandleChecked();
CHECK_EQ(expression_count,
module_object->native_module()->module()->num_declared_functions);
for (size_t i = 0; i < expression_count; ++i) {
char buffer[22];
snprintf(buffer, sizeof buffer, "f%zu", i);
// Execute corresponding function.
auto function =
Cast<WasmExportedFunction>(GetExport(i_isolate, instance, buffer));
DirectHandle<Object> undefined = i_isolate->factory()->undefined_value();
DirectHandle<Object> function_result =
Execution::Call(i_isolate, function, undefined, {}).ToHandleChecked();
// Get global value.
snprintf(buffer, sizeof buffer, "g%zu", i);
auto global =
Cast<WasmGlobalObject>(GetExport(i_isolate, instance, buffer));
switch (global->type().kind()) {
case ValueKind::kF32: {
float global_val = global->GetF32();
float func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsHeapNumber(*function_result));
func_val = Cast<HeapNumber>(*function_result)->value();
}
CHECK_FLOAT_EQ(func_val, global_val);
break;
}
case ValueKind::kF64: {
double global_val = global->GetF64();
double func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsHeapNumber(*function_result));
func_val = Cast<HeapNumber>(*function_result)->value();
}
CHECK_FLOAT_EQ(func_val, global_val);
break;
}
case ValueKind::kI32: {
int32_t global_val = global->GetI32();
int32_t func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsHeapNumber(*function_result));
func_val = Cast<HeapNumber>(*function_result)->value();
}
CHECK_EQ(func_val, global_val);
break;
}
case ValueKind::kI64: {
int64_t global_val = global->GetI64();
int64_t func_val;
if (IsSmi(*function_result)) {
func_val = Smi::ToInt(*function_result);
} else {
CHECK(IsBigInt(*function_result));
bool lossless;
func_val = Cast<BigInt>(*function_result)->AsInt64(&lossless);
CHECK(lossless);
}
CHECK_EQ(func_val, global_val);
break;
}
case ValueKind::kRef:
case ValueKind::kRefNull: {
// For reference types the expectations are more limited.
// Any struct.new would create a new object, so reference equality
// comparisons will not work.
DirectHandle<Object> global_val = global->GetRef();
CHECK_EQ(IsUndefined(*global_val), IsUndefined(*function_result));
CHECK_EQ(IsNullOrWasmNull(*global_val),
IsNullOrWasmNull(*function_result));
if (!IsNullOrWasmNull(*global_val)) {
if (IsSubtypeOf(global->type(), kWasmFuncRef,
module_object->module())) {
// For any function the global should be an internal function
// whose external function equals the call result. (The call goes
// through JS conversions while the global is accessed directly.)
CHECK(IsWasmFuncRef(*global_val));
CHECK(
WasmExportedFunction::IsWasmExportedFunction(*function_result));
CHECK(*WasmInternalFunction::GetOrCreateExternal(direct_handle(
Cast<WasmFuncRef>(*global_val)->internal(i_isolate),
i_isolate)) == *function_result);
} else {
// On arrays and structs, perform a deep comparison.
DisallowGarbageCollection no_gc;
WasmValue global_value =
instance->trusted_data(i_isolate)->GetGlobalValue(
i_isolate, instance->module()->globals[i]);
WasmValue func_value(function_result, global_value.type());
CheckEquivalent(global_value, func_value, *module_object->module());
}
}
break;
}
default:
UNIMPLEMENTED();
}
}
}
} // anonymous namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzIt({data, size});
return 0;
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,27 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Fuzzer that may generate SIMD expressions.
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data,
wasm::ZoneBuffer* buffer) {
v8::base::Vector<const uint8_t> wire_bytes = GenerateRandomWasmModule(
zone, {{WasmModuleGenerationOption::kGenerateSIMD}}, data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return LLVMFuzzerTestOneInputCommon(data, size, GenerateModule);
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,26 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/fuzzing/random-module-generation.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone.h"
#include "test/fuzzer/wasm/interpreter/interpreter-fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Fuzzer that may generate WasmGC expressions.
bool GenerateModule(Isolate* isolate, Zone* zone,
base::Vector<const uint8_t> data, ZoneBuffer* buffer) {
base::Vector<const uint8_t> wire_bytes = GenerateRandomWasmModule(
zone, {{WasmModuleGenerationOption::kGenerateWasmGC}}, data);
if (wire_bytes.empty()) return false;
buffer->write(wire_bytes.data(), wire_bytes.size());
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return LLVMFuzzerTestOneInputCommon(data, size, GenerateModule);
}
} // namespace v8::internal::wasm::fuzzing

91
deps/v8/test/fuzzer/wasm/module.cc vendored Normal file
View File

@ -0,0 +1,91 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include "include/libplatform/libplatform.h"
#include "include/v8-context.h"
#include "include/v8-exception.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "src/execution/isolate-inl.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-feature-flags.h"
#include "src/wasm/wasm-module.h"
#include "test/common/wasm/wasm-module-runner.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
// We reduce the maximum memory size and table size of WebAssembly instances
// to avoid OOMs in the fuzzer.
v8_flags.wasm_max_mem_pages = 32;
v8_flags.wasm_max_table_size = 100;
// Disable lazy compilation to find compiler bugs easier.
v8_flags.wasm_lazy_compilation = false;
// Choose one of Liftoff or TurboFan, depending on the size of the input (we
// can't use a dedicated byte from the input, because we want to be able to
// pass Wasm modules unmodified to this fuzzer).
v8_flags.liftoff = size & 1;
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
// Clear any exceptions from a prior run.
if (i_isolate->has_exception()) {
i_isolate->clear_exception();
}
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// We explicitly enable staged/experimental WebAssembly features here to
// increase fuzzer coverage. For libfuzzer fuzzers it is not possible that the
// fuzzer enables the flag by itself.
EnableExperimentalWasmFeatures(isolate);
v8::TryCatch try_catch(isolate);
testing::SetupIsolateForWasmModule(i_isolate);
ModuleWireBytes wire_bytes(data, data + size);
HandleScope scope(i_isolate);
ErrorThrower thrower(i_isolate, "wasm fuzzer");
DirectHandle<WasmModuleObject> module_object;
auto enabled_features = WasmEnabledFeatures::FromIsolate(i_isolate);
bool compiles =
GetWasmEngine()
->SyncCompile(i_isolate, enabled_features,
CompileTimeImportsForFuzzing(), &thrower,
base::OwnedCopyOf(wire_bytes.module_bytes()))
.ToHandle(&module_object);
if (v8_flags.wasm_fuzzer_gen_test) {
GenerateTestCase(i_isolate, wire_bytes, compiles);
}
// Return `-1` for invalid modules. We generate enough of them via mutation,
// no need to add them to the corpus.
int fuzzer_return_value =
compiles ? ExecuteAgainstReference(i_isolate, module_object,
kDefaultMaxFuzzerExecutedInstructions)
: -1;
// Pump the message loop and run micro tasks, e.g. GC finalization tasks.
support->PumpMessageLoop(v8::platform::MessageLoopBehavior::kDoNotWait);
isolate->PerformMicrotaskCheckpoint();
return fuzzer_return_value;
}
} // namespace v8::internal::wasm::fuzzing

0
deps/v8/test/fuzzer/wasm/module/foo vendored Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

201
deps/v8/test/fuzzer/wasm/streaming.cc vendored Normal file
View File

@ -0,0 +1,201 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "include/v8-isolate.h"
#include "src/api/api-inl.h"
#include "src/flags/flags.h"
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-objects-inl.h"
#include "test/fuzzer/fuzzer-support.h"
#include "test/fuzzer/wasm/fuzzer-common.h"
namespace v8::internal::wasm::fuzzing {
// Some properties of the compilation result to check. Extend if needed.
struct CompilationResult {
bool failed = false;
std::string error_message;
// If successful:
uint32_t imported_functions = 0;
uint32_t declared_functions = 0;
static CompilationResult ForFailure(std::string error_message) {
DCHECK(!error_message.empty());
return {true, std::move(error_message)};
}
static CompilationResult ForSuccess(const WasmModule* module) {
return {false,
{},
module->num_imported_functions,
module->num_declared_functions};
}
};
class TestResolver : public CompilationResultResolver {
public:
explicit TestResolver(Isolate* isolate) : isolate_(isolate) {}
void OnCompilationSucceeded(DirectHandle<WasmModuleObject> module) override {
done_ = true;
native_module_ = module->shared_native_module();
}
void OnCompilationFailed(DirectHandle<JSAny> error_reason) override {
done_ = true;
failed_ = true;
DirectHandle<String> str =
Object::ToString(isolate_, error_reason).ToHandleChecked();
error_message_.assign(str->ToCString().get());
}
bool done() const { return done_; }
bool failed() const { return failed_; }
const std::shared_ptr<NativeModule>& native_module() const {
return native_module_;
}
const std::string& error_message() const { return error_message_; }
private:
Isolate* isolate_;
bool done_ = false;
bool failed_ = false;
std::string error_message_;
std::shared_ptr<NativeModule> native_module_;
};
CompilationResult CompileStreaming(v8_fuzzer::FuzzerSupport* support,
WasmEnabledFeatures enabled_features,
base::Vector<const uint8_t> data,
uint8_t config) {
v8::Isolate* isolate = support->GetIsolate();
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
CompilationResult result;
std::weak_ptr<NativeModule> weak_native_module;
{
HandleScope handle_scope{i_isolate};
auto resolver = std::make_shared<TestResolver>(i_isolate);
DirectHandle<Context> context =
v8::Utils::OpenDirectHandle(*support->GetContext());
std::shared_ptr<StreamingDecoder> stream =
GetWasmEngine()->StartStreamingCompilation(
i_isolate, enabled_features, CompileTimeImports{}, context,
"wasm-streaming-fuzzer", resolver);
if (data.size() > 0) {
size_t split = config % data.size();
stream->OnBytesReceived(data.SubVector(0, split));
stream->OnBytesReceived(data.SubVectorFrom(split));
}
stream->Finish();
// Wait for the promise to resolve or reject.
while (!resolver->done()) {
support->PumpMessageLoop(platform::MessageLoopBehavior::kWaitForWork);
isolate->PerformMicrotaskCheckpoint();
}
if (resolver->failed()) {
return CompilationResult::ForFailure(resolver->error_message());
}
result = CompilationResult::ForSuccess(resolver->native_module()->module());
weak_native_module = resolver->native_module();
}
// Collect garbage until the native module is collected. This ensures that we
// recompile the module for sync compilation instead of taking it from the
// cache.
// If this turns out to be too slow, we could try to explicitly clear the
// cache, but we have to be careful not to break other internal assumptions
// then (because we have several identical modules / scripts).
while (weak_native_module.lock()) {
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
}
return result;
}
CompilationResult CompileSync(Isolate* isolate,
WasmEnabledFeatures enabled_features,
base::Vector<const uint8_t> data) {
ErrorThrower thrower{isolate, "wasm-streaming-fuzzer"};
DirectHandle<WasmModuleObject> module_object;
CompilationResult result;
if (!GetWasmEngine()
->SyncCompile(isolate, enabled_features, CompileTimeImports{},
&thrower, base::OwnedCopyOf(data))
.ToHandle(&module_object)) {
DirectHandle<Object> error = thrower.Reify();
DirectHandle<String> error_msg =
Object::ToString(isolate, error).ToHandleChecked();
return CompilationResult::ForFailure(error_msg->ToCString().get());
}
return CompilationResult::ForSuccess(module_object->module());
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 1) return 0;
v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
v8::Isolate* isolate = support->GetIsolate();
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
// We explicitly enable staged/experimental WebAssembly features here to
// increase fuzzer coverage. For libfuzzer fuzzers it is not possible that the
// fuzzer enables the flag by itself.
EnableExperimentalWasmFeatures(isolate);
// Limit the maximum module size to avoid OOM.
v8_flags.wasm_max_module_size = 256 * KB;
WasmEnabledFeatures enabled_features =
WasmEnabledFeatures::FromIsolate(i_isolate);
base::Vector<const uint8_t> data_vec{data, size - 1};
uint8_t config = data[size - 1];
CompilationResult streaming_result =
CompileStreaming(support, enabled_features, data_vec, config);
CompilationResult sync_result =
CompileSync(i_isolate, enabled_features, data_vec);
if (streaming_result.failed != sync_result.failed) {
const char* error_msg = streaming_result.failed
? streaming_result.error_message.c_str()
: sync_result.error_message.c_str();
FATAL(
"Streaming compilation did%s fail, sync compilation did%s. "
"Error message: %s\n",
streaming_result.failed ? "" : " not", sync_result.failed ? "" : " not",
error_msg);
}
if (streaming_result.error_message != sync_result.error_message) {
FATAL("Error messages differ:\nstreaming: %s\n sync: %s",
streaming_result.error_message.c_str(),
sync_result.error_message.c_str());
}
CHECK_EQ(streaming_result.imported_functions, sync_result.imported_functions);
CHECK_EQ(streaming_result.declared_functions, sync_result.declared_functions);
// We should not leave exceptions behind.
DCHECK(!i_isolate->has_exception());
return 0;
}
} // namespace v8::internal::wasm::fuzzing

View File

@ -0,0 +1,4 @@
All files in this directory are used by the trybots to check that the fuzzer
executes correctly, see
https://github.com/v8/v8/blob/main/test/fuzzer/README.md. There should be at
least one file in this directory, e.g. this README file.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.