2020-11-02 13:06:11 +00:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
2021-04-29 16:54:13 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#endif // !defined(_WIN32) && !defined(_WIN64)
|
2020-11-02 13:06:11 +00:00
|
|
|
|
|
|
|
#include <locale.h>
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "include/libplatform/libplatform.h"
|
|
|
|
#include "include/v8.h"
|
|
|
|
#include "src/base/platform/platform.h"
|
2020-11-05 17:00:11 +00:00
|
|
|
#include "src/base/platform/time.h"
|
|
|
|
#include "src/base/small-vector.h"
|
2021-06-17 15:43:55 +00:00
|
|
|
#include "src/base/vector.h"
|
2020-11-02 13:06:11 +00:00
|
|
|
#include "src/flags/flags.h"
|
|
|
|
#include "src/heap/read-only-heap.h"
|
|
|
|
#include "src/libplatform/default-platform.h"
|
|
|
|
#include "src/utils/utils.h"
|
|
|
|
#include "test/inspector/frontend-channel.h"
|
|
|
|
#include "test/inspector/isolate-data.h"
|
|
|
|
#include "test/inspector/task-runner.h"
|
|
|
|
#include "test/inspector/tasks.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace {
|
|
|
|
|
2020-11-05 17:00:11 +00:00
|
|
|
base::SmallVector<TaskRunner*, 2> task_runners;
|
2020-11-02 13:06:11 +00:00
|
|
|
|
|
|
|
class UtilsExtension : public IsolateData::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) {
|
2020-11-30 13:16:40 +00:00
|
|
|
// 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.
|
2020-11-02 13:06:11 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ClearAllSessions() { channels_.clear(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
static TaskRunner* backend_runner_;
|
|
|
|
|
|
|
|
static void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
[inspector][fuzzer] Fix termination
Joining the thread from the watchdog is problematic, since e.g.
{pthread_join} (the implementation of {Thread::Join} on POSIX systems)
has undefined behaviour if multiple threads try to join at the same
time. In practice, this leads to deadlocks.
Thus implement termination by just calling {TaskRunner::Terminate}, but
not {TaskRunner::Join}. This fixes the deadlocks in the inspector
fuzzer.
The inspector test binary is fixed simarly, even though there it seems
to not cause problems so far.
In both files, the {Terminate} function is inlined into callers because
it's only a single line now, with one to two users.
Also, replace the single fuzzer test (which is invalid javascript) by
two tests: One called "invalid" explicitly, still with invalid
javascript, and one empty file, which is valid input. That one
reproduced the deadlock.
R=szuend@chromium.org
Bug: chromium:1142437
Change-Id: I8fb98b0cdbf3ceff6af6849397e5da5a4e9acd3c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2526384
Reviewed-by: Simon Zünd <szuend@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71054}
2020-11-09 13:37:17 +00:00
|
|
|
// 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();
|
2020-11-02 13:06:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void CompileAndRunWithOrigin(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
2020-11-24 10:00:26 +00:00
|
|
|
if (args.Length() != 6 || !args[0]->IsInt32() || !args[1]->IsString() ||
|
|
|
|
!args[2]->IsString() || !args[3]->IsInt32() || !args[4]->IsInt32() ||
|
|
|
|
!args[5]->IsBoolean()) {
|
2020-11-02 13:06:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-05 16:15:32 +00:00
|
|
|
backend_runner_->Append(std::make_unique<ExecuteStringTask>(
|
2020-11-02 13:06:11 +00:00
|
|
|
args.GetIsolate(), args[0].As<v8::Int32>()->Value(),
|
|
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>()),
|
|
|
|
args[2].As<v8::String>(), args[3].As<v8::Int32>(),
|
2020-11-24 10:00:26 +00:00
|
|
|
args[4].As<v8::Int32>(), args[5].As<v8::Boolean>()));
|
2020-11-02 13:06:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void SchedulePauseOnNextStatement(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 3 || !args[0]->IsInt32() || !args[1]->IsString() ||
|
|
|
|
!args[2]->IsString()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::vector<uint16_t> reason =
|
|
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>());
|
|
|
|
std::vector<uint16_t> details =
|
|
|
|
ToVector(args.GetIsolate(), args[2].As<v8::String>());
|
|
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
|
|
RunSyncTask(backend_runner_,
|
|
|
|
[&context_group_id, &reason, &details](IsolateData* 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>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
|
|
RunSyncTask(backend_runner_, [&context_group_id](IsolateData* data) {
|
|
|
|
data->CancelPauseOnNextStatement(context_group_id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CreateContextGroup(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int context_group_id = 0;
|
|
|
|
RunSyncTask(backend_runner_, [&context_group_id](IsolateData* data) {
|
|
|
|
context_group_id = data->CreateContextGroup();
|
|
|
|
});
|
|
|
|
args.GetReturnValue().Set(
|
|
|
|
v8::Int32::New(args.GetIsolate(), context_group_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ResetContextGroup(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
|
|
RunSyncTask(backend_runner_, [&context_group_id](IsolateData* data) {
|
|
|
|
data->ResetContextGroup(context_group_id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ConnectSession(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 3 || !args[0]->IsInt32() || !args[1]->IsString() ||
|
|
|
|
!args[2]->IsFunction()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
FrontendChannelImpl* channel = new FrontendChannelImpl(
|
|
|
|
IsolateData::FromContext(context)->task_runner(),
|
|
|
|
IsolateData::FromContext(context)->GetContextGroupId(context),
|
|
|
|
args.GetIsolate(), args[2].As<v8::Function>());
|
|
|
|
|
|
|
|
std::vector<uint8_t> state =
|
|
|
|
ToBytes(args.GetIsolate(), args[1].As<v8::String>());
|
|
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
|
|
int session_id = 0;
|
|
|
|
RunSyncTask(backend_runner_, [&context_group_id, &session_id, &channel,
|
|
|
|
&state](IsolateData* data) {
|
|
|
|
session_id = data->ConnectSession(
|
|
|
|
context_group_id,
|
|
|
|
v8_inspector::StringView(state.data(), state.size()), channel);
|
|
|
|
channel->set_session_id(session_id);
|
|
|
|
});
|
|
|
|
|
|
|
|
channels_[session_id].reset(channel);
|
|
|
|
args.GetReturnValue().Set(v8::Int32::New(args.GetIsolate(), session_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DisconnectSession(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int session_id = args[0].As<v8::Int32>()->Value();
|
|
|
|
std::vector<uint8_t> state;
|
|
|
|
RunSyncTask(backend_runner_, [&session_id, &state](IsolateData* data) {
|
|
|
|
state = data->DisconnectSession(session_id);
|
|
|
|
});
|
|
|
|
channels_.erase(session_id);
|
|
|
|
args.GetReturnValue().Set(ToV8String(args.GetIsolate(), state));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SendMessageToBackend(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 2 || !args[0]->IsInt32() || !args[1]->IsString()) {
|
|
|
|
return;
|
|
|
|
}
|
2020-11-05 16:15:32 +00:00
|
|
|
backend_runner_->Append(std::make_unique<SendMessageToBackendTask>(
|
2020-11-02 13:06:11 +00:00
|
|
|
args[0].As<v8::Int32>()->Value(),
|
|
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>())));
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::map<int, std::unique_ptr<FrontendChannelImpl>> channels_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TaskRunner* UtilsExtension::backend_runner_ = nullptr;
|
|
|
|
std::map<int, std::unique_ptr<FrontendChannelImpl>> UtilsExtension::channels_;
|
|
|
|
|
|
|
|
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 IsolateData::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, "allowAccessorFormatting"),
|
|
|
|
v8::FunctionTemplate::New(
|
|
|
|
isolate, &InspectorExtension::AllowAccessorFormatting));
|
|
|
|
inspector->Set(
|
|
|
|
ToV8String(isolate, "markObjectAsNotInspectable"),
|
|
|
|
v8::FunctionTemplate::New(
|
|
|
|
isolate, &InspectorExtension::MarkObjectAsNotInspectable));
|
|
|
|
inspector->Set(ToV8String(isolate, "createObjectWithAccessor"),
|
|
|
|
v8::FunctionTemplate::New(
|
|
|
|
isolate, &InspectorExtension::CreateObjectWithAccessor));
|
|
|
|
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>& args) {
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
data->FireContextCreated(context, data->GetContextGroupId(context),
|
|
|
|
v8_inspector::StringView());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void FireContextDestroyed(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
data->FireContextDestroyed(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void FreeContext(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
data->FreeContext(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddInspectedObject(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 2 || !args[0]->IsInt32()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
data->AddInspectedObject(args[0].As<v8::Int32>()->Value(), args[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetMaxAsyncTaskStacks(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
IsolateData::FromContext(args.GetIsolate()->GetCurrentContext())
|
|
|
|
->SetMaxAsyncTaskStacksForTest(args[0].As<v8::Int32>()->Value());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DumpAsyncTaskStacksStateForTest(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
IsolateData::FromContext(args.GetIsolate()->GetCurrentContext())
|
|
|
|
->DumpAsyncTaskStacksStateForTest();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void BreakProgram(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
std::vector<uint16_t> reason =
|
|
|
|
ToVector(args.GetIsolate(), args[0].As<v8::String>());
|
|
|
|
v8_inspector::StringView reason_view(reason.data(), reason.size());
|
|
|
|
std::vector<uint16_t> details =
|
|
|
|
ToVector(args.GetIsolate(), args[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>& args) {
|
|
|
|
if (args.Length() != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::ObjectTemplate> templ =
|
|
|
|
v8::ObjectTemplate::New(args.GetIsolate());
|
|
|
|
templ->SetAccessCheckCallback(&StrictAccessCheck);
|
|
|
|
args.GetReturnValue().Set(
|
|
|
|
templ->NewInstance(args.GetIsolate()->GetCurrentContext())
|
|
|
|
.ToLocalChecked());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CallWithScheduledBreak(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsString() ||
|
|
|
|
!args[2]->IsString()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::vector<uint16_t> reason =
|
|
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>());
|
|
|
|
v8_inspector::StringView reason_view(reason.data(), reason.size());
|
|
|
|
std::vector<uint16_t> details =
|
|
|
|
ToVector(args.GetIsolate(), args[2].As<v8::String>());
|
|
|
|
v8_inspector::StringView details_view(details.data(), details.size());
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
int context_group_id = data->GetContextGroupId(context);
|
|
|
|
data->SchedulePauseOnNextStatement(context_group_id, reason_view,
|
|
|
|
details_view);
|
|
|
|
v8::MaybeLocal<v8::Value> result;
|
|
|
|
result = args[0].As<v8::Function>()->Call(context, context->Global(), 0,
|
|
|
|
nullptr);
|
|
|
|
data->CancelPauseOnNextStatement(context_group_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AllowAccessorFormatting(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsObject()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Object> object = args[0].As<v8::Object>();
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
|
|
v8::Local<v8::Private> shouldFormatAccessorsPrivate = v8::Private::ForApi(
|
|
|
|
isolate, ToV8String(isolate, "allowAccessorFormatting"));
|
|
|
|
object
|
|
|
|
->SetPrivate(isolate->GetCurrentContext(), shouldFormatAccessorsPrivate,
|
|
|
|
v8::Null(isolate))
|
|
|
|
.ToChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MarkObjectAsNotInspectable(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsObject()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Object> object = args[0].As<v8::Object>();
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
|
|
v8::Local<v8::Private> notInspectablePrivate =
|
|
|
|
v8::Private::ForApi(isolate, ToV8String(isolate, "notInspectable"));
|
|
|
|
object
|
|
|
|
->SetPrivate(isolate->GetCurrentContext(), notInspectablePrivate,
|
|
|
|
v8::True(isolate))
|
|
|
|
.ToChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CreateObjectWithAccessor(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsBoolean()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
|
|
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
|
|
|
|
if (args[1].As<v8::Boolean>()->Value()) {
|
|
|
|
templ->SetAccessor(v8::Local<v8::String>::Cast(args[0]), AccessorGetter,
|
|
|
|
AccessorSetter);
|
|
|
|
} else {
|
|
|
|
templ->SetAccessor(v8::Local<v8::String>::Cast(args[0]), AccessorGetter);
|
|
|
|
}
|
|
|
|
args.GetReturnValue().Set(
|
|
|
|
templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AccessorGetter(v8::Local<v8::String> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
2021-04-15 08:10:06 +00:00
|
|
|
isolate->ThrowError("Getter is called");
|
2020-11-02 13:06:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void AccessorSetter(v8::Local<v8::String> property,
|
|
|
|
v8::Local<v8::Value> value,
|
|
|
|
const v8::PropertyCallbackInfo<void>& info) {
|
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
2021-04-15 08:10:06 +00:00
|
|
|
isolate->ThrowError("Setter is called");
|
2020-11-02 13:06:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void StoreCurrentStackTrace(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
std::vector<uint16_t> description =
|
|
|
|
ToVector(isolate, args[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;
|
|
|
|
args.GetReturnValue().Set(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ExternalAsyncTaskStarted(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsArrayBuffer()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
v8_inspector::V8StackTraceId* id =
|
|
|
|
static_cast<v8_inspector::V8StackTraceId*>(
|
|
|
|
args[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
|
|
|
data->ExternalAsyncTaskStarted(*id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ExternalAsyncTaskFinished(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsArrayBuffer()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
v8_inspector::V8StackTraceId* id =
|
|
|
|
static_cast<v8_inspector::V8StackTraceId*>(
|
|
|
|
args[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
|
|
|
data->ExternalAsyncTaskFinished(*id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ScheduleWithAsyncStack(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsString() ||
|
|
|
|
!args[2]->IsBoolean()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
int context_group_id = data->GetContextGroupId(context);
|
|
|
|
bool with_empty_stack = args[2].As<v8::Boolean>()->Value();
|
|
|
|
if (with_empty_stack) context->Exit();
|
|
|
|
|
|
|
|
std::vector<uint16_t> task_name =
|
|
|
|
ToVector(isolate, args[1].As<v8::String>());
|
|
|
|
v8_inspector::StringView task_name_view(task_name.data(), task_name.size());
|
|
|
|
|
2020-11-05 16:15:32 +00:00
|
|
|
RunAsyncTask(
|
|
|
|
data->task_runner(), task_name_view,
|
|
|
|
std::make_unique<SetTimeoutTask>(
|
|
|
|
context_group_id, isolate, v8::Local<v8::Function>::Cast(args[0])));
|
2020-11-02 13:06:11 +00:00
|
|
|
if (with_empty_stack) context->Enter();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetAllowCodeGenerationFromStrings(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsBoolean()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
args.GetIsolate()->GetCurrentContext()->AllowCodeGenerationFromStrings(
|
|
|
|
args[0].As<v8::Boolean>()->Value());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetResourceNamePrefix(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
|
|
data->SetResourceNamePrefix(v8::Local<v8::String>::Cast(args[0]));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
using CharVector = v8::base::Vector<const char>;
|
2020-11-02 13:06:11 +00:00
|
|
|
|
2020-11-05 17:00:11 +00:00
|
|
|
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;
|
[inspector][fuzzer] Fix termination
Joining the thread from the watchdog is problematic, since e.g.
{pthread_join} (the implementation of {Thread::Join} on POSIX systems)
has undefined behaviour if multiple threads try to join at the same
time. In practice, this leads to deadlocks.
Thus implement termination by just calling {TaskRunner::Terminate}, but
not {TaskRunner::Join}. This fixes the deadlocks in the inspector
fuzzer.
The inspector test binary is fixed simarly, even though there it seems
to not cause problems so far.
In both files, the {Terminate} function is inlined into callers because
it's only a single line now, with one to two users.
Also, replace the single fuzzer test (which is invalid javascript) by
two tests: One called "invalid" explicitly, still with invalid
javascript, and one empty file, which is valid input. That one
reproduced the deadlock.
R=szuend@chromium.org
Bug: chromium:1142437
Change-Id: I8fb98b0cdbf3ceff6af6849397e5da5a4e9acd3c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2526384
Reviewed-by: Simon Zünd <szuend@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71054}
2020-11-09 13:37:17 +00:00
|
|
|
for (TaskRunner* task_runner : task_runners) task_runner->Terminate();
|
2020-11-05 17:00:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
base::Semaphore* const semaphore_;
|
|
|
|
};
|
|
|
|
|
2020-11-02 13:06:11 +00:00
|
|
|
void FuzzInspector(const uint8_t* data, size_t size) {
|
|
|
|
base::Semaphore ready_semaphore(0);
|
|
|
|
|
|
|
|
IsolateData::SetupGlobalTasks frontend_extensions;
|
|
|
|
frontend_extensions.emplace_back(new UtilsExtension());
|
2020-11-09 14:18:44 +00:00
|
|
|
TaskRunner frontend_runner(std::move(frontend_extensions),
|
2020-11-12 12:57:48 +00:00
|
|
|
kSuppressUncaughtExceptions, &ready_semaphore,
|
|
|
|
nullptr, kNoInspector);
|
2020-11-02 13:06:11 +00:00
|
|
|
ready_semaphore.Wait();
|
|
|
|
|
|
|
|
int frontend_context_group_id = 0;
|
|
|
|
RunSyncTask(&frontend_runner,
|
|
|
|
[&frontend_context_group_id](IsolateData* data) {
|
|
|
|
frontend_context_group_id = data->CreateContextGroup();
|
|
|
|
});
|
|
|
|
|
|
|
|
IsolateData::SetupGlobalTasks backend_extensions;
|
|
|
|
backend_extensions.emplace_back(new SetTimeoutExtension());
|
|
|
|
backend_extensions.emplace_back(new InspectorExtension());
|
2020-11-12 12:57:48 +00:00
|
|
|
TaskRunner backend_runner(std::move(backend_extensions),
|
|
|
|
kSuppressUncaughtExceptions, &ready_semaphore,
|
|
|
|
nullptr, kWithInspector);
|
2020-11-02 13:06:11 +00:00
|
|
|
ready_semaphore.Wait();
|
|
|
|
UtilsExtension::set_backend_task_runner(&backend_runner);
|
|
|
|
|
2020-11-05 17:00:11 +00:00
|
|
|
task_runners = {&frontend_runner, &backend_runner};
|
|
|
|
|
|
|
|
Watchdog watchdog(&ready_semaphore);
|
2020-11-02 13:06:11 +00:00
|
|
|
|
2020-11-05 16:15:32 +00:00
|
|
|
frontend_runner.Append(std::make_unique<ExecuteStringTask>(
|
2020-11-02 13:06:11 +00:00
|
|
|
std::string{reinterpret_cast<const char*>(data), size},
|
|
|
|
frontend_context_group_id));
|
|
|
|
|
|
|
|
frontend_runner.Join();
|
|
|
|
backend_runner.Join();
|
|
|
|
|
2020-11-05 17:00:11 +00:00
|
|
|
ready_semaphore.Signal();
|
|
|
|
watchdog.Join();
|
|
|
|
|
2020-11-02 13:06:11 +00:00
|
|
|
UtilsExtension::ClearAllSessions();
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|