Commit d46723ae authored by Samuel Groß's avatar Samuel Groß Committed by Commit Bot

Updated libreprl from Fuzzilli and improved Fuzzilli test

The test now verifies that JavaScript programs can be executed
over the REPRL interface, that runtime exceptions can be detected,
and that the engine's state is properly reset between executions.

Change-Id: Ic8032c07e222307cbb4d332e7eaec61936a10ccd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2396082Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Commit-Queue: Samuel Groß <saelo@google.com>
Cr-Commit-Position: refs/heads/master@{#69883}
parent 345518a0
......@@ -100,7 +100,7 @@ namespace {
const int kMB = 1024 * 1024;
#ifdef V8_FUZZILLI
// REPRL = read-eval-print-loop
// REPRL = read-eval-print-reset-loop
// These file descriptors are being opened when Fuzzilli uses fork & execve to
// run V8.
#define REPRL_CRFD 100 // Control read file decriptor
......@@ -2821,33 +2821,35 @@ 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;
if (fuzzilli_reprl) {
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) {
......@@ -4258,6 +4260,10 @@ int Shell::Main(int argc, char* argv[]) {
<< bitmap.size() << std::endl;
iteration_counter++;
}
// In REPRL mode, stdout and stderr can be regular files, so they need
// to be flushed after every execution
fflush(stdout);
fflush(stderr);
CHECK_EQ(write(REPRL_CWFD, &status, 4), 4);
sanitizer_cov_reset_edgeguards();
if (options.fuzzilli_enable_builtins_coverage) {
......
This diff is collapsed.
......@@ -18,37 +18,100 @@
#ifndef __LIBREPRL_H__
#define __LIBREPRL_H__
#include <stdint.h>
#include <sys/types.h>
struct reprl_child_process {
// Read file descriptor of the control pipe.
int crfd;
// Write file descriptor of the control pipe.
int cwfd;
// Read file descriptor of the data pipe.
int drfd;
// Write file descriptor of the data pipe.
int dwfd;
// PID of the child process.
int pid;
};
struct reprl_result {
int child_died;
int status;
unsigned long exec_time;
char* output;
size_t output_size;
};
// Spawn a child process implementing the REPRL protocol.
int reprl_spawn_child(char** argv, char** envp,
struct reprl_child_process* child);
// Execute the provided script in the child process, wait for its completion,
// and return the result.
int reprl_execute_script(int pid, int crfd, int cwfd, int drfd, int dwfd,
int timeout, const char* script, int64_t script_length,
struct reprl_result* result);
/// Maximum size for data transferred through REPRL. In particular, this is the
/// maximum size of scripts that can be executed. Currently, this is 16MB.
/// Executing a 16MB script file is very likely to take longer than the typical
/// timeout, so the limit on script size shouldn't be a problem in practice.
#define REPRL_MAX_DATA_SIZE (16 << 20)
/// Opaque struct representing a REPRL execution context.
struct reprl_context;
/// Allocates a new REPRL context.
/// @return an uninitialzed REPRL context
struct reprl_context* reprl_create_context();
/// Initializes a REPRL context.
/// @param ctx An uninitialized context
/// @param argv The argv vector for the child processes
/// @param envp The envp vector for the child processes
/// @param capture_stdout Whether this REPRL context should capture the child's
/// stdout
/// @param capture_stderr Whether this REPRL context should capture the child's
/// stderr
/// @return zero in case of no errors, otherwise a negative value
int reprl_initialize_context(struct reprl_context* ctx, const char** argv,
const char** envp, int capture_stdout,
int capture_stderr);
/// Destroys a REPRL context, freeing all resources held by it.
/// @param ctx The context to destroy
void reprl_destroy_context(struct reprl_context* ctx);
/// Executes the provided script in the target process, wait for its completion,
/// and return the result. If necessary, or if fresh_instance is true, this will
/// automatically spawn a new instance of the target process.
///
/// @param ctx The REPRL context
/// @param script The script to execute
/// @param script_length The size of the script in bytes
/// @param timeout The maximum allowed execution time in milliseconds
/// @param execution_time A pointer to which, if execution succeeds, the
/// execution time in milliseconds is written to
/// @param fresh_instance if true, forces the creation of a new instance of the
/// target
/// @return A REPRL exit status (see below) or a negative number in case of an
/// error
int reprl_execute(struct reprl_context* ctx, const char* script,
uint64_t script_length, uint64_t timeout,
uint64_t* execution_time, int fresh_instance);
/// Returns true if the execution terminated due to a signal.
int RIFSIGNALED(int status);
/// Returns true if the execution finished normally.
int RIFEXITED(int status);
/// Returns true if the execution terminated due to a timeout.
int RIFTIMEDOUT(int status);
/// Returns the terminating signal in case RIFSIGNALED is true.
int RTERMSIG(int status);
/// Returns the exit status in case RIFEXITED is true.
int REXITSTATUS(int status);
/// Returns the stdout data of the last successful execution if the context is
/// capturing stdout, otherwise an empty string. The output is limited to
/// REPRL_MAX_FAST_IO_SIZE (currently 16MB).
/// @param ctx The REPRL context
/// @return A string pointer which is owned by the REPRL context and thus should
/// not be freed by the caller
const char* reprl_fetch_stdout(struct reprl_context* ctx);
/// Returns the stderr data of the last successful execution if the context is
/// capturing stderr, otherwise an empty string. The output is limited to
/// REPRL_MAX_FAST_IO_SIZE (currently 16MB).
/// @param ctx The REPRL context
/// @return A string pointer which is owned by the REPRL context and thus should
/// not be freed by the caller
const char* reprl_fetch_stderr(struct reprl_context* ctx);
/// Returns the fuzzout data of the last successful execution.
/// The output is limited to REPRL_MAX_FAST_IO_SIZE (currently 16MB).
/// @param ctx The REPRL context
/// @return A string pointer which is owned by the REPRL context and thus should
/// not be freed by the caller
const char* reprl_fetch_fuzzout(struct reprl_context* ctx);
/// Returns a string describing the last error that occurred in the given
/// context.
/// @param ctx The REPRL context
/// @return A string pointer which is owned by the REPRL context and thus should
/// not be freed by the caller
const char* reprl_get_last_error(struct reprl_context* ctx);
#endif
......@@ -3,16 +3,58 @@
// found in the LICENSE file.
extern "C" {
#include <stdio.h>
#include <string.h>
#include "libreprl.h"
}
int main() {
struct reprl_child_process child;
char* env[] = {nullptr};
char prog[] = "./out.gn/x64.debug/d8";
char*(argv[]) = {prog, nullptr};
if (reprl_spawn_child(argv, env, &child) == -1) return -1;
// struct reprl_result res;
// reprl_execute_script(child.pid, child.crfd, child.cwfd, child.drfd,
// child.dwfd, 1, ,,&res);
int main(int argc, char** argv) {
struct reprl_context* ctx = reprl_create_context();
const char* env[] = {nullptr};
const char* prog = argc > 1 ? argv[1] : "./out.gn/x64.debug/d8";
const char* args[] = {prog, nullptr};
if (reprl_initialize_context(ctx, args, env, 1, 1) != 0) {
printf("REPRL initialization failed\n");
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);
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;
}
// 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;
}
puts("OK");
return 0;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment