Fix unhandled promise rejections in REPRL mode

Previously, unhandled promise rejections weren't reset between REPRL
executions, leading to incorrect exit statuses being reported. This CL
fixes the issue and adds further tests to verify the correct behaviour.

Change-Id: Ied47d9359b0fbc05ebb211667687a0a4041ef767
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2431205
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Commit-Queue: Samuel Groß <saelo@google.com>
Cr-Commit-Position: refs/heads/master@{#70227}
This commit is contained in:
Samuel Groß 2020-09-25 14:39:30 +02:00 committed by Commit Bot
parent 5f1ae37a01
commit 919d1dd770
3 changed files with 45 additions and 32 deletions

View File

@ -1210,6 +1210,7 @@ int PerIsolateData::HandleUnhandledPromiseRejections() {
Shell::ReportException(isolate_, message, value);
}
unhandled_promises_.clear();
ignore_unhandled_promises_ = false;
return static_cast<int>(i);
}
@ -3603,6 +3604,9 @@ int Shell::RunMain(Isolate* isolate, bool last_run) {
printf("%i pending unhandled Promise rejection(s) detected.\n",
Shell::unhandled_promise_rejections_.load());
success = false;
// RunMain may be executed multiple times, e.g. in REPRL mode, so we have to
// reset this counter.
Shell::unhandled_promise_rejections_.store(0);
}
// In order to finish successfully, success must be != expected_to_throw.
return success == Shell::options.expected_to_throw ? 1 : 0;

View File

@ -2,14 +2,13 @@
## Source code
On low level fuzzilli communicates with v8 through Swift C API library in `Sources/libreprl/libreprl.c`
On a low level, Fuzzilli communicates with v8 through the REPRL protocol, implemented on the fuzzer side by the libreprl C library in `Sources/libreprl/`. The main way of using the library is through the following three functions:
`reprl_spawn_child` fucntions spawns child process. It does that by creating pipes, forking itself, then setting filedescriptors, and then transforming itself using `execve` into v8 process. Afterwords it checks for receiving 4 byte string and it sends the exact same string back.
`reprl_create_context()` this creates a new, empty REPRL context to be used by the following APIs.
`fetch_output` fetches the output from the child and returns its size and pointer to data.
`reprl_initialize_context(ctx, argv, envp)` this initializes the given context and sets the argv and envp vectors to use for the child processes.
`execute script`
writes `exec`, and size of script, into the command write pipe and sends script through data write pipe
`reprl_execute(ctx, code)` this executes the given code and returns the exit status. If necessary, a new child process is created for this. This involves creating pipes, forking itself, then setting filedescriptors, and using `execve` to execute the d8 binary. A child process can be reused for multiple executions, thus increasing fuzzing performance as the overhead of fork and execve are removed.
## Coverage

View File

@ -5,12 +5,34 @@
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libreprl.h"
struct reprl_context* ctx;
int execute(const char* code) {
uint64_t exec_time;
return reprl_execute(ctx, code, strlen(code), 1000, &exec_time, 0);
}
void expect_success(const char* code) {
if (execute(code) != 0) {
printf("Execution of \"%s\" failed\n", code);
exit(1);
}
}
void expect_failure(const char* code) {
if (execute(code) == 0) {
printf("Execution of \"%s\" unexpectedly succeeded\n", code);
exit(1);
}
}
int main(int argc, char** argv) {
struct reprl_context* ctx = reprl_create_context();
ctx = reprl_create_context();
const char* env[] = {nullptr};
const char* prog = argc > 1 ? argv[1] : "./out.gn/x64.debug/d8";
@ -20,39 +42,27 @@ int main(int argc, char** argv) {
return -1;
}
uint64_t exec_time;
// Basic functionality test
const char* code = "let greeting = \"Hello World!\";";
if (reprl_execute(ctx, code, strlen(code), 1000, &exec_time, 0) != 0) {
printf("Execution of \"%s\" failed\n", code);
printf("Is %s the path to d8 built with v8_fuzzilli=true?\n", prog);
if (execute("let greeting = \"Hello World!\";") != 0) {
printf(
"Script execution failed, is %s the path to d8 built with "
"v8_fuzzilli=true?\n",
prog);
return -1;
}
// Verify that runtime exceptions can be detected
code = "throw 'failure';";
if (reprl_execute(ctx, code, strlen(code), 1000, &exec_time, 0) == 0) {
printf("Execution of \"%s\" unexpectedly succeeded\n", code);
return -1;
}
expect_failure("throw 'failure';");
// Verify that existing state is property reset between executions
code = "globalProp = 42; Object.prototype.foo = \"bar\";";
if (reprl_execute(ctx, code, strlen(code), 1000, &exec_time, 0) != 0) {
printf("Execution of \"%s\" failed\n", code);
return -1;
}
code = "if (typeof(globalProp) !== 'undefined') throw 'failure'";
if (reprl_execute(ctx, code, strlen(code), 1000, &exec_time, 0) != 0) {
printf("Execution of \"%s\" failed\n", code);
return -1;
}
code = "if (typeof(Object.prototype.foo) !== 'undefined') throw 'failure'";
if (reprl_execute(ctx, code, strlen(code), 1000, &exec_time, 0) != 0) {
printf("Execution of \"%s\" failed\n", code);
return -1;
}
expect_success("globalProp = 42; Object.prototype.foo = \"bar\";");
expect_success("if (typeof(globalProp) !== 'undefined') throw 'failure'");
expect_success("if (typeof(({}).foo) !== 'undefined') throw 'failure'");
// Verify that rejected promises are properly reset between executions
expect_failure("async function fail() { throw 42; }; fail()");
expect_success("42");
expect_failure("async function fail() { throw 42; }; fail()");
puts("OK");
return 0;