summaryrefslogtreecommitdiff
path: root/chromium/v8/src/d8
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/v8/src/d8
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/src/d8')
-rw-r--r--chromium/v8/src/d8/cov.cc74
-rw-r--r--chromium/v8/src/d8/cov.h15
-rw-r--r--chromium/v8/src/d8/d8-platforms.cc2
-rw-r--r--chromium/v8/src/d8/d8-posix.cc16
-rw-r--r--chromium/v8/src/d8/d8.cc579
-rw-r--r--chromium/v8/src/d8/d8.h25
6 files changed, 587 insertions, 124 deletions
diff --git a/chromium/v8/src/d8/cov.cc b/chromium/v8/src/d8/cov.cc
new file mode 100644
index 00000000000..47e2af599c0
--- /dev/null
+++ b/chromium/v8/src/d8/cov.cc
@@ -0,0 +1,74 @@
+// 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 "src/d8/cov.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define SHM_SIZE 0x100000
+#define MAX_EDGES ((SHM_SIZE - 4) * 8)
+
+struct shmem_data {
+ uint32_t num_edges;
+ unsigned char edges[];
+};
+
+struct shmem_data* shmem;
+
+uint32_t *__edges_start, *__edges_stop;
+void __sanitizer_cov_reset_edgeguards() {
+ uint32_t N = 0;
+ for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
+ *x = ++N;
+}
+
+extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
+ uint32_t* stop) {
+ // Map the shared memory region
+ const char* shm_key = getenv("SHM_ID");
+ if (!shm_key) {
+ puts("[COV] no shared memory bitmap available, skipping");
+ shmem = (struct shmem_data*)malloc(SHM_SIZE);
+ } else {
+ int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
+ if (fd <= -1) {
+ fprintf(stderr, "[COV] Failed to open shared memory region\n");
+ _exit(-1);
+ }
+
+ shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (shmem == MAP_FAILED) {
+ fprintf(stderr, "[COV] Failed to mmap shared memory region\n");
+ _exit(-1);
+ }
+ }
+
+ __edges_start = start;
+ __edges_stop = stop;
+ __sanitizer_cov_reset_edgeguards();
+
+ shmem->num_edges = static_cast<uint32_t>(stop - start);
+ printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
+ shm_key, shmem->num_edges);
+}
+
+extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
+ // There's a small race condition here: if this function executes in two
+ // threads for the same edge at the same time, the first thread might disable
+ // the edge (by setting the guard to zero) before the second thread fetches
+ // the guard value (and thus the index). However, our instrumentation ignores
+ // the first edge (see libcoverage.c) and so the race is unproblematic.
+ uint32_t index = *guard;
+ shmem->edges[index / 8] |= 1 << (index % 8);
+ *guard = 0;
+}
diff --git a/chromium/v8/src/d8/cov.h b/chromium/v8/src/d8/cov.h
new file mode 100644
index 00000000000..d2d26752337
--- /dev/null
+++ b/chromium/v8/src/d8/cov.h
@@ -0,0 +1,15 @@
+// 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.
+
+#ifndef V8_D8_COV_H_
+#define V8_D8_COV_H_
+
+// This file is defining functions to handle coverage which are needed for
+// fuzzilli fuzzer It communicates coverage bitmap with fuzzilli through shared
+// memory
+// https://clang.llvm.org/docs/SanitizerCoverage.html
+
+void __sanitizer_cov_reset_edgeguards();
+
+#endif // V8_D8_COV_H_
diff --git a/chromium/v8/src/d8/d8-platforms.cc b/chromium/v8/src/d8/d8-platforms.cc
index 5ea43b14760..8f528e2d43c 100644
--- a/chromium/v8/src/d8/d8-platforms.cc
+++ b/chromium/v8/src/d8/d8-platforms.cc
@@ -102,7 +102,7 @@ class DelayedTasksPlatform : public Platform {
DCHECK_NOT_NULL(platform_);
}
- ~DelayedTasksPlatform() {
+ ~DelayedTasksPlatform() override {
// When the platform shuts down, all task runners must be freed.
DCHECK_EQ(0, delayed_task_runners_.size());
}
diff --git a/chromium/v8/src/d8/d8-posix.cc b/chromium/v8/src/d8/d8-posix.cc
index 1c9f506641c..ba6356e4479 100644
--- a/chromium/v8/src/d8/d8-posix.cc
+++ b/chromium/v8/src/d8/d8-posix.cc
@@ -761,20 +761,18 @@ char* Shell::ReadCharsFromTcpPort(const char* name, int* size_out) {
void Shell::AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ) {
if (options.enable_os_system) {
- os_templ->Set(String::NewFromUtf8Literal(isolate, "system"),
- FunctionTemplate::New(isolate, System));
+ os_templ->Set(isolate, "system", FunctionTemplate::New(isolate, System));
}
- os_templ->Set(String::NewFromUtf8Literal(isolate, "chdir"),
+ os_templ->Set(isolate, "chdir",
FunctionTemplate::New(isolate, ChangeDirectory));
- os_templ->Set(String::NewFromUtf8Literal(isolate, "setenv"),
+ os_templ->Set(isolate, "setenv",
FunctionTemplate::New(isolate, SetEnvironment));
- os_templ->Set(String::NewFromUtf8Literal(isolate, "unsetenv"),
+ os_templ->Set(isolate, "unsetenv",
FunctionTemplate::New(isolate, UnsetEnvironment));
- os_templ->Set(String::NewFromUtf8Literal(isolate, "umask"),
- FunctionTemplate::New(isolate, SetUMask));
- os_templ->Set(String::NewFromUtf8Literal(isolate, "mkdirp"),
+ os_templ->Set(isolate, "umask", FunctionTemplate::New(isolate, SetUMask));
+ os_templ->Set(isolate, "mkdirp",
FunctionTemplate::New(isolate, MakeDirectory));
- os_templ->Set(String::NewFromUtf8Literal(isolate, "rmdir"),
+ os_templ->Set(isolate, "rmdir",
FunctionTemplate::New(isolate, RemoveDirectory));
}
diff --git a/chromium/v8/src/d8/d8.cc b/chromium/v8/src/d8/d8.cc
index fe1bb58e4a9..117df1cc526 100644
--- a/chromium/v8/src/d8/d8.cc
+++ b/chromium/v8/src/d8/d8.cc
@@ -12,6 +12,7 @@
#include <iomanip>
#include <iterator>
#include <string>
+#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -35,7 +36,6 @@
#include "src/d8/d8.h"
#include "src/debug/debug-interface.h"
#include "src/deoptimizer/deoptimizer.h"
-#include "src/diagnostics/basic-block-profiler.h"
#include "src/execution/vm-state-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/init/v8.h"
@@ -55,6 +55,10 @@
#include "src/utils/utils.h"
#include "src/wasm/wasm-engine.h"
+#ifdef V8_FUZZILLI
+#include "src/d8/cov.h"
+#endif // V8_FUZZILLI
+
#ifdef V8_USE_PERFETTO
#include "perfetto/tracing.h"
#endif // V8_USE_PERFETTO
@@ -92,6 +96,19 @@ namespace {
const int kMB = 1024 * 1024;
+#ifdef V8_FUZZILLI
+// REPRL = read-eval-print-loop
+// These file descriptors are being opened when Fuzzilli uses fork & execve to
+// run V8.
+#define REPRL_CRFD 100 // Control read file decriptor
+#define REPRL_CWFD 101 // Control write file decriptor
+#define REPRL_DRFD 102 // Data read file decriptor
+#define REPRL_DWFD 103 // Data write file decriptor
+bool fuzzilli_reprl = true;
+#else
+bool fuzzilli_reprl = false;
+#endif // V8_FUZZILLI
+
const int kMaxSerializerMemoryUsage =
1 * kMB; // Arbitrary maximum for testing.
@@ -417,7 +434,7 @@ static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
class ExternalOwningOneByteStringResource
: public String::ExternalOneByteStringResource {
public:
- ExternalOwningOneByteStringResource() {}
+ ExternalOwningOneByteStringResource() = default;
ExternalOwningOneByteStringResource(
std::unique_ptr<base::OS::MemoryMappedFile> file)
: file_(std::move(file)) {}
@@ -444,9 +461,11 @@ std::unordered_set<std::shared_ptr<Worker>> Shell::running_workers_;
std::atomic<bool> Shell::script_executed_{false};
base::LazyMutex Shell::isolate_status_lock_;
std::map<v8::Isolate*, bool> Shell::isolate_status_;
+std::map<v8::Isolate*, int> Shell::isolate_running_streaming_tasks_;
base::LazyMutex Shell::cached_code_mutex_;
std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
Shell::cached_code_map_;
+std::atomic<int> Shell::unhandled_promise_rejections_{0};
Global<Context> Shell::evaluation_context_;
ArrayBuffer::Allocator* Shell::array_buffer_allocator;
@@ -486,6 +505,61 @@ void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
ScriptCompiler::CachedData::BufferOwned));
}
+// Dummy external source stream which returns the whole source in one go.
+// TODO(leszeks): Also test chunking the data.
+class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
+ public:
+ explicit DummySourceStream(Local<String> source) : done_(false) {
+ source_buffer_ = Utils::OpenHandle(*source)->ToCString(
+ i::ALLOW_NULLS, i::FAST_STRING_TRAVERSAL, &source_length_);
+ }
+
+ size_t GetMoreData(const uint8_t** src) override {
+ if (done_) {
+ return 0;
+ }
+ *src = reinterpret_cast<uint8_t*>(source_buffer_.release());
+ done_ = true;
+
+ return source_length_;
+ }
+
+ private:
+ int source_length_;
+ std::unique_ptr<char[]> source_buffer_;
+ bool done_;
+};
+
+class StreamingCompileTask final : public v8::Task {
+ public:
+ StreamingCompileTask(Isolate* isolate,
+ v8::ScriptCompiler::StreamedSource* streamed_source)
+ : isolate_(isolate),
+ script_streaming_task_(v8::ScriptCompiler::StartStreamingScript(
+ isolate, streamed_source)) {
+ Shell::NotifyStartStreamingTask(isolate_);
+ }
+
+ void Run() override {
+ script_streaming_task_->Run();
+ // Signal that the task has finished using the task runner to wake the
+ // message loop.
+ Shell::PostForegroundTask(isolate_, std::make_unique<FinishTask>(isolate_));
+ }
+
+ private:
+ class FinishTask final : public v8::Task {
+ public:
+ explicit FinishTask(Isolate* isolate) : isolate_(isolate) {}
+ void Run() final { Shell::NotifyFinishStreamingTask(isolate_); }
+ Isolate* isolate_;
+ };
+
+ Isolate* isolate_;
+ std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
+ script_streaming_task_;
+};
+
// Executes a string within the current v8 context.
bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, PrintResult print_result,
@@ -512,7 +586,12 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
i::Handle<i::Script> script = parse_info.CreateScript(
i_isolate, str, i::kNullMaybeHandle, ScriptOriginOptions());
- if (!i::parsing::ParseProgram(&parse_info, script, i_isolate)) {
+ if (!i::parsing::ParseProgram(&parse_info, script, i_isolate,
+ i::parsing::ReportStatisticsMode::kYes)) {
+ parse_info.pending_error_handler()->PrepareErrors(
+ i_isolate, parse_info.ast_value_factory());
+ parse_info.pending_error_handler()->ReportErrors(i_isolate, script);
+
fprintf(stderr, "Failed parsing\n");
return false;
}
@@ -547,6 +626,19 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
maybe_script = ScriptCompiler::Compile(
context, &script_source, ScriptCompiler::kNoCompileOptions);
}
+ } else if (options.streaming_compile) {
+ v8::ScriptCompiler::StreamedSource streamed_source(
+ std::make_unique<DummySourceStream>(source),
+ v8::ScriptCompiler::StreamedSource::UTF8);
+
+ PostBlockingBackgroundTask(
+ std::make_unique<StreamingCompileTask>(isolate, &streamed_source));
+
+ // Pump the loop until the streaming task completes.
+ Shell::CompleteMessageLoop(isolate);
+
+ maybe_script =
+ ScriptCompiler::Compile(context, &streamed_source, source, origin);
} else {
ScriptCompiler::Source script_source(source, origin);
maybe_script = ScriptCompiler::Compile(context, &script_source,
@@ -575,7 +667,10 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
StoreInCodeCache(isolate, source, cached_data);
delete cached_data;
}
- if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
+ if (process_message_queue) {
+ if (!EmptyMessageQueues(isolate)) success = false;
+ if (!HandleUnhandledPromiseRejections(isolate)) success = false;
+ }
data->realm_current_ = data->realm_switch_;
}
Local<Value> result;
@@ -1071,6 +1166,45 @@ MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
return result;
}
+void PerIsolateData::RemoveUnhandledPromise(Local<Promise> promise) {
+ // Remove handled promises from the list
+ DCHECK_EQ(promise->GetIsolate(), isolate_);
+ for (auto it = unhandled_promises_.begin(); it != unhandled_promises_.end();
+ ++it) {
+ v8::Local<v8::Promise> unhandled_promise = std::get<0>(*it).Get(isolate_);
+ if (unhandled_promise == promise) {
+ unhandled_promises_.erase(it--);
+ }
+ }
+}
+
+void PerIsolateData::AddUnhandledPromise(Local<Promise> promise,
+ Local<Message> message,
+ Local<Value> exception) {
+ DCHECK_EQ(promise->GetIsolate(), isolate_);
+ unhandled_promises_.push_back(
+ std::make_tuple(v8::Global<v8::Promise>(isolate_, promise),
+ v8::Global<v8::Message>(isolate_, message),
+ v8::Global<v8::Value>(isolate_, exception)));
+}
+
+size_t PerIsolateData::GetUnhandledPromiseCount() {
+ return unhandled_promises_.size();
+}
+
+int PerIsolateData::HandleUnhandledPromiseRejections() {
+ int unhandled_promises_count = 0;
+ v8::HandleScope scope(isolate_);
+ for (auto& tuple : unhandled_promises_) {
+ Local<v8::Message> message = std::get<1>(tuple).Get(isolate_);
+ Local<v8::Value> value = std::get<2>(tuple).Get(isolate_);
+ Shell::ReportException(isolate_, message, value);
+ unhandled_promises_count++;
+ }
+ unhandled_promises_.clear();
+ return unhandled_promises_count;
+}
+
PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
data_->realm_count_ = 1;
data_->realm_current_ = 0;
@@ -1326,8 +1460,10 @@ void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
Throw(args.GetIsolate(), "Invalid argument");
return;
}
+ ScriptOrigin origin(String::NewFromUtf8Literal(isolate, "(d8)",
+ NewStringType::kInternalized));
ScriptCompiler::Source script_source(
- args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
+ args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked(), origin);
Local<UnboundScript> script;
if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
.ToLocal(&script)) {
@@ -1694,6 +1830,57 @@ void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
.ToLocalChecked());
}
+#ifdef V8_FUZZILLI
+
+// We have to assume that the fuzzer will be able to call this function e.g. by
+// enumerating the properties of the global object and eval'ing them. As such
+// this function is implemented in a way that requires passing some magic value
+// as first argument (with the idea being that the fuzzer won't be able to
+// generate this value) which then also acts as a selector for the operation
+// to perform.
+void Shell::Fuzzilli(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+
+ String::Utf8Value operation(args.GetIsolate(), args[0]);
+ if (*operation == nullptr) {
+ return;
+ }
+
+ if (strcmp(*operation, "FUZZILLI_CRASH") == 0) {
+ auto arg = args[1]
+ ->Int32Value(args.GetIsolate()->GetCurrentContext())
+ .FromMaybe(0);
+ switch (arg) {
+ case 0:
+ V8_IMMEDIATE_CRASH();
+ break;
+ case 1:
+ CHECK(0);
+ break;
+ default:
+ DCHECK(false);
+ break;
+ }
+ } else if (strcmp(*operation, "FUZZILLI_PRINT") == 0) {
+ static FILE* fzliout = fdopen(REPRL_DWFD, "w");
+ if (!fzliout) {
+ fprintf(
+ stderr,
+ "Fuzzer output channel not available, printing to stdout instead\n");
+ fzliout = stdout;
+ }
+
+ String::Utf8Value string(args.GetIsolate(), args[1]);
+ if (*string == nullptr) {
+ return;
+ }
+ fprintf(fzliout, "%s\n", *string);
+ fflush(fzliout);
+ }
+}
+
+#endif // V8_FUZZILLI
+
void Shell::ReportException(Isolate* isolate, Local<v8::Message> message,
Local<v8::Value> exception_obj) {
HandleScope handle_scope(isolate);
@@ -1958,6 +2145,13 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
AddOSMethods(isolate, os_templ);
global_template->Set(isolate, "os", os_templ);
+#ifdef V8_FUZZILLI
+ global_template->Set(
+ String::NewFromUtf8(isolate, "fuzzilli", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Fuzzilli), PropertyAttribute::DontEnum);
+#endif // V8_FUZZILLI
+
if (i::FLAG_expose_async_hooks) {
Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
async_hooks_templ->Set(
@@ -2009,8 +2203,50 @@ static void PrintMessageCallback(Local<Message> message, Local<Value> error) {
printf("%s:%i: %s\n", filename_string, linenum, msg_string);
}
+void Shell::PromiseRejectCallback(v8::PromiseRejectMessage data) {
+ if (options.ignore_unhandled_promises) return;
+ if (data.GetEvent() == v8::kPromiseRejectAfterResolved ||
+ data.GetEvent() == v8::kPromiseResolveAfterResolved) {
+ // Ignore reject/resolve after resolved.
+ return;
+ }
+ v8::Local<v8::Promise> promise = data.GetPromise();
+ v8::Isolate* isolate = promise->GetIsolate();
+ PerIsolateData* isolate_data = PerIsolateData::Get(isolate);
+
+ if (data.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
+ isolate_data->RemoveUnhandledPromise(promise);
+ return;
+ }
+
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ bool capture_exceptions =
+ i_isolate->get_capture_stack_trace_for_uncaught_exceptions();
+ isolate->SetCaptureStackTraceForUncaughtExceptions(true);
+ v8::Local<Value> exception = data.GetValue();
+ v8::Local<Message> message;
+ // Assume that all objects are stack-traces.
+ if (exception->IsObject()) {
+ message = v8::Exception::CreateMessage(isolate, exception);
+ }
+ if (!exception->IsNativeError() &&
+ (message.IsEmpty() || message->GetStackTrace().IsEmpty())) {
+ // If there is no real Error object, manually throw and catch a stack trace.
+ v8::TryCatch try_catch(isolate);
+ try_catch.SetVerbose(true);
+ isolate->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8Literal(isolate, "Unhandled Promise.")));
+ message = try_catch.Message();
+ exception = try_catch.Exception();
+ }
+ isolate->SetCaptureStackTraceForUncaughtExceptions(capture_exceptions);
+
+ isolate_data->AddUnhandledPromise(promise, message, exception);
+}
+
void Shell::Initialize(Isolate* isolate, D8Console* console,
bool isOnMainThread) {
+ isolate->SetPromiseRejectCallback(PromiseRejectCallback);
if (isOnMainThread) {
// Set up counters
if (i::FLAG_map_counters[0] != '\0') {
@@ -2029,6 +2265,19 @@ void Shell::Initialize(Isolate* isolate, D8Console* console,
isolate->SetHostInitializeImportMetaObjectCallback(
Shell::HostInitializeImportMetaObject);
+#ifdef V8_FUZZILLI
+ // Let the parent process (Fuzzilli) know we are ready.
+ char helo[] = "HELO";
+ if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
+ fuzzilli_reprl = false;
+ }
+
+ if (memcmp(helo, "HELO", 4) != 0) {
+ fprintf(stderr, "Invalid response from parent\n");
+ _exit(-1);
+ }
+#endif // V8_FUZZILLI
+
debug::SetConsoleDelegate(isolate, console);
}
@@ -2167,11 +2416,6 @@ void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
}
void Shell::OnExit(v8::Isolate* isolate) {
- // Dump basic block profiling data.
- if (i::FLAG_turbo_profiling) {
- i::BasicBlockProfiler* profiler = i::BasicBlockProfiler::Get();
- i::StdoutStream{} << *profiler;
- }
isolate->Dispose();
if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
@@ -2518,6 +2762,36 @@ bool ends_with(const char* input, const char* suffix) {
bool SourceGroup::Execute(Isolate* isolate) {
bool success = true;
+#ifdef V8_FUZZILLI
+ HandleScope handle_scope(isolate);
+ Local<String> file_name =
+ String::NewFromUtf8(isolate, "fuzzcode.js", NewStringType::kNormal)
+ .ToLocalChecked();
+
+ size_t script_size;
+ CHECK_EQ(read(REPRL_CRFD, &script_size, 8), 8);
+ char* buffer = new char[script_size + 1];
+ char* ptr = buffer;
+ size_t remaining = script_size;
+ while (remaining > 0) {
+ ssize_t rv = read(REPRL_DRFD, ptr, remaining);
+ CHECK_GE(rv, 0);
+ remaining -= rv;
+ ptr += rv;
+ }
+ buffer[script_size] = 0;
+
+ Local<String> source =
+ String::NewFromUtf8(isolate, buffer, NewStringType::kNormal)
+ .ToLocalChecked();
+ delete[] buffer;
+ Shell::set_script_executed();
+ if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
+ Shell::kReportExceptions,
+ Shell::kNoProcessMessageQueue)) {
+ return false;
+ }
+#endif // V8_FUZZILLI
for (int i = begin_offset_; i < end_offset_; ++i) {
const char* arg = argv_[i];
if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
@@ -2780,9 +3054,7 @@ void Worker::ExecuteInThread() {
in_semaphore_.Wait();
std::unique_ptr<SerializationData> data;
if (!in_queue_.Dequeue(&data)) continue;
- if (!data) {
- break;
- }
+ if (!data) break;
v8::TryCatch try_catch(isolate);
try_catch.SetVerbose(true);
HandleScope scope(isolate);
@@ -2795,6 +3067,7 @@ void Worker::ExecuteInThread() {
USE(result);
}
}
+ // TODO(cbruni): Check for unhandled promises here.
}
}
}
@@ -2895,6 +3168,9 @@ bool Shell::SetOptions(int argc, char* argv[]) {
// Ignore any -f flags for compatibility with other stand-alone
// JavaScript engines.
continue;
+ } else if (strcmp(argv[i], "--ignore-unhandled-promises") == 0) {
+ options.ignore_unhandled_promises = true;
+ argv[i] = nullptr;
} else if (strcmp(argv[i], "--isolate") == 0) {
options.num_isolates++;
} else if (strcmp(argv[i], "--throws") == 0) {
@@ -2935,6 +3211,13 @@ bool Shell::SetOptions(int argc, char* argv[]) {
return false;
}
argv[i] = nullptr;
+ } else if (strcmp(argv[i], "--streaming-compile") == 0) {
+ options.streaming_compile = true;
+ argv[i] = nullptr;
+ } else if ((strcmp(argv[i], "--no-streaming-compile") == 0) ||
+ (strcmp(argv[i], "--nostreaming-compile") == 0)) {
+ options.streaming_compile = false;
+ argv[i] = nullptr;
} else if (strcmp(argv[i], "--enable-tracing") == 0) {
options.trace_enabled = true;
argv[i] = nullptr;
@@ -3045,6 +3328,7 @@ int Shell::RunMain(Isolate* isolate, bool last_run) {
PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
if (!options.isolate_sources[0].Execute(isolate)) success = false;
if (!CompleteMessageLoop(isolate)) success = false;
+ if (!HandleUnhandledPromiseRejections(isolate)) success = false;
}
if (!use_existing_context) {
DisposeModuleEmbedderData(context);
@@ -3072,6 +3356,11 @@ int Shell::RunMain(Isolate* isolate, bool last_run) {
}
}
WaitForRunningWorkers();
+ if (Shell::unhandled_promise_rejections_.load() > 0) {
+ printf("%i pending unhandled Promise rejection(s) detected.\n",
+ Shell::unhandled_promise_rejections_.load());
+ success = false;
+ }
// In order to finish successfully, success must be != expected_to_throw.
return success == Shell::options.expected_to_throw ? 1 : 0;
}
@@ -3093,11 +3382,20 @@ void Shell::CollectGarbage(Isolate* isolate) {
void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
base::MutexGuard guard(isolate_status_lock_.Pointer());
- if (isolate_status_.count(isolate) == 0) {
- isolate_status_.insert(std::make_pair(isolate, value));
- } else {
- isolate_status_[isolate] = value;
- }
+ isolate_status_[isolate] = value;
+}
+
+void Shell::NotifyStartStreamingTask(Isolate* isolate) {
+ DCHECK(options.streaming_compile);
+ base::MutexGuard guard(isolate_status_lock_.Pointer());
+ ++isolate_running_streaming_tasks_[isolate];
+}
+
+void Shell::NotifyFinishStreamingTask(Isolate* isolate) {
+ DCHECK(options.streaming_compile);
+ base::MutexGuard guard(isolate_status_lock_.Pointer());
+ --isolate_running_streaming_tasks_[isolate];
+ DCHECK_GE(isolate_running_streaming_tasks_[isolate], 0);
}
namespace {
@@ -3163,7 +3461,8 @@ bool Shell::CompleteMessageLoop(Isolate* isolate) {
i::wasm::WasmEngine* wasm_engine = i_isolate->wasm_engine();
bool should_wait = (options.wait_for_wasm &&
wasm_engine->HasRunningCompileJob(i_isolate)) ||
- isolate_status_[isolate];
+ isolate_status_[isolate] ||
+ isolate_running_streaming_tasks_[isolate] > 0;
return should_wait ? platform::MessageLoopBehavior::kWaitForWork
: platform::MessageLoopBehavior::kDoNotWait;
};
@@ -3175,6 +3474,24 @@ bool Shell::EmptyMessageQueues(Isolate* isolate) {
isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
}
+void Shell::PostForegroundTask(Isolate* isolate, std::unique_ptr<Task> task) {
+ g_default_platform->GetForegroundTaskRunner(isolate)->PostTask(
+ std::move(task));
+}
+
+void Shell::PostBlockingBackgroundTask(std::unique_ptr<Task> task) {
+ g_default_platform->CallBlockingTaskOnWorkerThread(std::move(task));
+}
+
+bool Shell::HandleUnhandledPromiseRejections(Isolate* isolate) {
+ if (options.ignore_unhandled_promises) return true;
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ int count = data->HandleUnhandledPromiseRejections();
+ Shell::unhandled_promise_rejections_.store(
+ Shell::unhandled_promise_rejections_.load() + count);
+ return count == 0;
+}
+
class Serializer : public ValueSerializer::Delegate {
public:
explicit Serializer(Isolate* isolate)
@@ -3505,8 +3822,14 @@ int Shell::Main(int argc, char* argv[]) {
std::ofstream trace_file;
if (options.trace_enabled && !i::FLAG_verify_predictable) {
tracing = std::make_unique<platform::tracing::TracingController>();
- trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
- DCHECK(trace_file.good());
+ const char* trace_path =
+ options.trace_path ? options.trace_path : "v8_trace.json";
+ trace_file.open(trace_path);
+ if (!trace_file.good()) {
+ printf("Cannot open trace file '%s' for writing: %s.\n", trace_path,
+ strerror(errno));
+ return 1;
+ }
#ifdef V8_USE_PERFETTO
// Set up the in-process backend that the tracing controller will connect
@@ -3611,112 +3934,140 @@ int Shell::Main(int argc, char* argv[]) {
Initialize(isolate, &console);
PerIsolateData data(isolate);
- if (options.trace_enabled) {
- platform::tracing::TraceConfig* trace_config;
- if (options.trace_config) {
- int size = 0;
- char* trace_config_json_str = ReadChars(options.trace_config, &size);
- trace_config =
- tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
- delete[] trace_config_json_str;
- } else {
- trace_config =
- platform::tracing::TraceConfig::CreateDefaultTraceConfig();
+ // Fuzzilli REPRL = read-eval-print-loop
+ do {
+#ifdef V8_FUZZILLI
+ if (fuzzilli_reprl) {
+ unsigned action = 0;
+ ssize_t nread = read(REPRL_CRFD, &action, 4);
+ if (nread != 4 || action != 'cexe') {
+ fprintf(stderr, "Unknown action: %u\n", action);
+ _exit(-1);
+ }
+ }
+#endif // V8_FUZZILLI
+
+ result = 0;
+
+ if (options.trace_enabled) {
+ platform::tracing::TraceConfig* trace_config;
+ if (options.trace_config) {
+ int size = 0;
+ char* trace_config_json_str = ReadChars(options.trace_config, &size);
+ trace_config = tracing::CreateTraceConfigFromJSON(
+ isolate, trace_config_json_str);
+ delete[] trace_config_json_str;
+ } else {
+ trace_config =
+ platform::tracing::TraceConfig::CreateDefaultTraceConfig();
+ }
+ tracing_controller->StartTracing(trace_config);
}
- tracing_controller->StartTracing(trace_config);
- }
-
- CpuProfiler* cpu_profiler;
- if (options.cpu_profiler) {
- cpu_profiler = CpuProfiler::New(isolate);
- CpuProfilingOptions profile_options;
- cpu_profiler->StartProfiling(String::Empty(isolate), profile_options);
- }
- if (options.stress_opt) {
- options.stress_runs = D8Testing::GetStressRuns();
- for (int i = 0; i < options.stress_runs && result == 0; i++) {
- printf("============ Stress %d/%d ============\n", i + 1,
- options.stress_runs);
- D8Testing::PrepareStressRun(i);
- bool last_run = i == options.stress_runs - 1;
- result = RunMain(isolate, last_run);
+ CpuProfiler* cpu_profiler;
+ if (options.cpu_profiler) {
+ cpu_profiler = CpuProfiler::New(isolate);
+ CpuProfilingOptions profile_options;
+ cpu_profiler->StartProfiling(String::Empty(isolate), profile_options);
}
- printf("======== Full Deoptimization =======\n");
- D8Testing::DeoptimizeAll(isolate);
- } else if (i::FLAG_stress_runs > 0) {
- options.stress_runs = i::FLAG_stress_runs;
- for (int i = 0; i < options.stress_runs && result == 0; i++) {
- printf("============ Run %d/%d ============\n", i + 1,
- options.stress_runs);
- bool last_run = i == options.stress_runs - 1;
+
+ if (options.stress_opt) {
+ options.stress_runs = D8Testing::GetStressRuns();
+ for (int i = 0; i < options.stress_runs && result == 0; i++) {
+ printf("============ Stress %d/%d ============\n", i + 1,
+ options.stress_runs);
+ D8Testing::PrepareStressRun(i);
+ bool last_run = i == options.stress_runs - 1;
+ result = RunMain(isolate, last_run);
+ }
+ printf("======== Full Deoptimization =======\n");
+ D8Testing::DeoptimizeAll(isolate);
+ } else if (i::FLAG_stress_runs > 0) {
+ options.stress_runs = i::FLAG_stress_runs;
+ for (int i = 0; i < options.stress_runs && result == 0; i++) {
+ printf("============ Run %d/%d ============\n", i + 1,
+ options.stress_runs);
+ bool last_run = i == options.stress_runs - 1;
+ result = RunMain(isolate, last_run);
+ }
+ } else if (options.code_cache_options !=
+ ShellOptions::CodeCacheOptions::kNoProduceCache) {
+ printf("============ Run: Produce code cache ============\n");
+ // First run to produce the cache
+ Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = Shell::array_buffer_allocator;
+ i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
+ Isolate* isolate2 = Isolate::New(create_params);
+ i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
+ {
+ D8Console console(isolate2);
+ Initialize(isolate2, &console);
+ PerIsolateData data(isolate2);
+ Isolate::Scope isolate_scope(isolate2);
+
+ result = RunMain(isolate2, false);
+ }
+ isolate2->Dispose();
+
+ // Change the options to consume cache
+ DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
+ options.compile_options ==
+ v8::ScriptCompiler::kNoCompileOptions);
+ options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
+ options.code_cache_options =
+ ShellOptions::CodeCacheOptions::kNoProduceCache;
+
+ printf("============ Run: Consume code cache ============\n");
+ // Second run to consume the cache in current isolate
+ result = RunMain(isolate, true);
+ options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
+ } else {
+ bool last_run = true;
result = RunMain(isolate, last_run);
}
- } else if (options.code_cache_options !=
- ShellOptions::CodeCacheOptions::kNoProduceCache) {
- printf("============ Run: Produce code cache ============\n");
- // First run to produce the cache
- Isolate::CreateParams create_params;
- create_params.array_buffer_allocator = Shell::array_buffer_allocator;
- i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
- Isolate* isolate2 = Isolate::New(create_params);
- i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
- {
- D8Console console(isolate2);
- Initialize(isolate2, &console);
- PerIsolateData data(isolate2);
- Isolate::Scope isolate_scope(isolate2);
- result = RunMain(isolate2, false);
+ // Run interactive shell if explicitly requested or if no script has been
+ // executed, but never on --test
+ if (use_interactive_shell()) {
+ RunShell(isolate);
}
- isolate2->Dispose();
-
- // Change the options to consume cache
- DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
- options.compile_options == v8::ScriptCompiler::kNoCompileOptions);
- options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
- options.code_cache_options =
- ShellOptions::CodeCacheOptions::kNoProduceCache;
-
- printf("============ Run: Consume code cache ============\n");
- // Second run to consume the cache in current isolate
- result = RunMain(isolate, true);
- options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
- } else {
- bool last_run = true;
- result = RunMain(isolate, last_run);
- }
- // Run interactive shell if explicitly requested or if no script has been
- // executed, but never on --test
- if (use_interactive_shell()) {
- RunShell(isolate);
- }
-
- if (i::FLAG_trace_ignition_dispatches &&
- i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
- WriteIgnitionDispatchCountersFile(isolate);
- }
+ if (i::FLAG_trace_ignition_dispatches &&
+ i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
+ WriteIgnitionDispatchCountersFile(isolate);
+ }
- if (options.cpu_profiler) {
- CpuProfile* profile = cpu_profiler->StopProfiling(String::Empty(isolate));
- if (options.cpu_profiler_print) {
- const internal::ProfileNode* root =
- reinterpret_cast<const internal::ProfileNode*>(
- profile->GetTopDownRoot());
- root->Print(0);
+ if (options.cpu_profiler) {
+ CpuProfile* profile =
+ cpu_profiler->StopProfiling(String::Empty(isolate));
+ if (options.cpu_profiler_print) {
+ const internal::ProfileNode* root =
+ reinterpret_cast<const internal::ProfileNode*>(
+ profile->GetTopDownRoot());
+ root->Print(0);
+ }
+ profile->Delete();
+ cpu_profiler->Dispose();
}
- profile->Delete();
- cpu_profiler->Dispose();
- }
- // Shut down contexts and collect garbage.
- cached_code_map_.clear();
- evaluation_context_.Reset();
- stringify_function_.Reset();
- CollectGarbage(isolate);
+ // Shut down contexts and collect garbage.
+ cached_code_map_.clear();
+ evaluation_context_.Reset();
+ stringify_function_.Reset();
+ CollectGarbage(isolate);
+
+#ifdef V8_FUZZILLI
+ // Send result to parent (fuzzilli) and reset edge guards.
+ if (fuzzilli_reprl) {
+ int status = result << 8;
+ CHECK_EQ(write(REPRL_CWFD, &status, 4), 4);
+ __sanitizer_cov_reset_edgeguards();
+ }
+#endif // V8_FUZZILLI
+ } while (fuzzilli_reprl);
}
OnExit(isolate);
+
V8::Dispose();
V8::ShutdownPlatform();
diff --git a/chromium/v8/src/d8/d8.h b/chromium/v8/src/d8/d8.h
index bd49b81fd02..203e9edb0cb 100644
--- a/chromium/v8/src/d8/d8.h
+++ b/chromium/v8/src/d8/d8.h
@@ -234,6 +234,12 @@ class PerIsolateData {
AsyncHooks* GetAsyncHooks() { return async_hooks_wrapper_; }
+ void RemoveUnhandledPromise(Local<Promise> promise);
+ void AddUnhandledPromise(Local<Promise> promise, Local<Message> message,
+ Local<Value> exception);
+ int HandleUnhandledPromiseRejections();
+ size_t GetUnhandledPromiseCount();
+
private:
friend class Shell;
friend class RealmScope;
@@ -245,6 +251,8 @@ class PerIsolateData {
Global<Value> realm_shared_;
std::queue<Global<Function>> set_timeout_callbacks_;
std::queue<Global<Context>> set_timeout_contexts_;
+ std::vector<std::tuple<Global<Promise>, Global<Message>, Global<Value>>>
+ unhandled_promises_;
AsyncHooks* async_hooks_wrapper_;
int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args,
@@ -272,6 +280,7 @@ class ShellOptions {
bool interactive_shell = false;
bool test_shell = false;
bool expected_to_throw = false;
+ bool ignore_unhandled_promises = false;
bool mock_arraybuffer_allocator = false;
size_t mock_arraybuffer_allocator_limit = 0;
bool multi_mapped_mock_allocator = false;
@@ -280,6 +289,7 @@ class ShellOptions {
v8::ScriptCompiler::CompileOptions compile_options =
v8::ScriptCompiler::kNoCompileOptions;
CodeCacheOptions code_cache_options = CodeCacheOptions::kNoProduceCache;
+ bool streaming_compile = false;
SourceGroup* isolate_sources = nullptr;
const char* icu_data_file = nullptr;
const char* icu_locale = nullptr;
@@ -331,6 +341,11 @@ class Shell : public i::AllStatic {
static bool EmptyMessageQueues(Isolate* isolate);
static bool CompleteMessageLoop(Isolate* isolate);
+ static bool HandleUnhandledPromiseRejections(Isolate* isolate);
+
+ static void PostForegroundTask(Isolate* isolate, std::unique_ptr<Task> task);
+ static void PostBlockingBackgroundTask(std::unique_ptr<Task> task);
+
static std::unique_ptr<SerializationData> SerializeValue(
Isolate* isolate, Local<Value> value, Local<Value> transfer);
static MaybeLocal<Value> DeserializeValue(
@@ -434,6 +449,10 @@ class Shell : public i::AllStatic {
Local<Module> module,
Local<Object> meta);
+#ifdef V8_FUZZILLI
+ static void Fuzzilli(const v8::FunctionCallbackInfo<v8::Value>& args);
+#endif // V8_FUZZILLI
+
// Data is of type DynamicImportData*. We use void* here to be able
// to conform with MicrotaskCallback interface and enqueue this
// function in the microtask queue.
@@ -446,6 +465,8 @@ class Shell : public i::AllStatic {
static ArrayBuffer::Allocator* array_buffer_allocator;
static void SetWaitUntilDone(Isolate* isolate, bool value);
+ static void NotifyStartStreamingTask(Isolate* isolate);
+ static void NotifyFinishStreamingTask(Isolate* isolate);
static char* ReadCharsFromTcpPort(const char* name, int* size_out);
@@ -462,6 +483,8 @@ class Shell : public i::AllStatic {
static void Initialize(Isolate* isolate, D8Console* console,
bool isOnMainThread = true);
+ static void PromiseRejectCallback(v8::PromiseRejectMessage reject_message);
+
private:
static Global<Context> evaluation_context_;
static base::OnceType quit_once_;
@@ -506,10 +529,12 @@ class Shell : public i::AllStatic {
// the isolate_status_ needs to be concurrency-safe.
static base::LazyMutex isolate_status_lock_;
static std::map<Isolate*, bool> isolate_status_;
+ static std::map<Isolate*, int> isolate_running_streaming_tasks_;
static base::LazyMutex cached_code_mutex_;
static std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
cached_code_map_;
+ static std::atomic<int> unhandled_promise_rejections_;
};
} // namespace v8