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:
parent
5f1ae37a01
commit
919d1dd770
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user