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 { ...@@ -100,7 +100,7 @@ namespace {
const int kMB = 1024 * 1024; const int kMB = 1024 * 1024;
#ifdef V8_FUZZILLI #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 // These file descriptors are being opened when Fuzzilli uses fork & execve to
// run V8. // run V8.
#define REPRL_CRFD 100 // Control read file decriptor #define REPRL_CRFD 100 // Control read file decriptor
...@@ -2821,33 +2821,35 @@ bool ends_with(const char* input, const char* suffix) { ...@@ -2821,33 +2821,35 @@ bool ends_with(const char* input, const char* suffix) {
bool SourceGroup::Execute(Isolate* isolate) { bool SourceGroup::Execute(Isolate* isolate) {
bool success = true; bool success = true;
#ifdef V8_FUZZILLI #ifdef V8_FUZZILLI
HandleScope handle_scope(isolate); if (fuzzilli_reprl) {
Local<String> file_name = HandleScope handle_scope(isolate);
String::NewFromUtf8(isolate, "fuzzcode.js", NewStringType::kNormal) Local<String> file_name =
.ToLocalChecked(); String::NewFromUtf8(isolate, "fuzzcode.js", NewStringType::kNormal)
.ToLocalChecked();
size_t script_size;
CHECK_EQ(read(REPRL_CRFD, &script_size, 8), 8); size_t script_size;
char* buffer = new char[script_size + 1]; CHECK_EQ(read(REPRL_CRFD, &script_size, 8), 8);
char* ptr = buffer; char* buffer = new char[script_size + 1];
size_t remaining = script_size; char* ptr = buffer;
while (remaining > 0) { size_t remaining = script_size;
ssize_t rv = read(REPRL_DRFD, ptr, remaining); while (remaining > 0) {
CHECK_GE(rv, 0); ssize_t rv = read(REPRL_DRFD, ptr, remaining);
remaining -= rv; CHECK_GE(rv, 0);
ptr += rv; remaining -= rv;
} ptr += rv;
buffer[script_size] = 0; }
buffer[script_size] = 0;
Local<String> source =
String::NewFromUtf8(isolate, buffer, NewStringType::kNormal) Local<String> source =
.ToLocalChecked(); String::NewFromUtf8(isolate, buffer, NewStringType::kNormal)
delete[] buffer; .ToLocalChecked();
Shell::set_script_executed(); delete[] buffer;
if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult, Shell::set_script_executed();
Shell::kReportExceptions, if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
Shell::kNoProcessMessageQueue)) { Shell::kReportExceptions,
return false; Shell::kNoProcessMessageQueue)) {
return false;
}
} }
#endif // V8_FUZZILLI #endif // V8_FUZZILLI
for (int i = begin_offset_; i < end_offset_; ++i) { for (int i = begin_offset_; i < end_offset_; ++i) {
...@@ -4258,6 +4260,10 @@ int Shell::Main(int argc, char* argv[]) { ...@@ -4258,6 +4260,10 @@ int Shell::Main(int argc, char* argv[]) {
<< bitmap.size() << std::endl; << bitmap.size() << std::endl;
iteration_counter++; 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); CHECK_EQ(write(REPRL_CWFD, &status, 4), 4);
sanitizer_cov_reset_edgeguards(); sanitizer_cov_reset_edgeguards();
if (options.fuzzilli_enable_builtins_coverage) { if (options.fuzzilli_enable_builtins_coverage) {
......
This diff is collapsed.
...@@ -18,37 +18,100 @@ ...@@ -18,37 +18,100 @@
#ifndef __LIBREPRL_H__ #ifndef __LIBREPRL_H__
#define __LIBREPRL_H__ #define __LIBREPRL_H__
#include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
struct reprl_child_process { /// Maximum size for data transferred through REPRL. In particular, this is the
// Read file descriptor of the control pipe. /// maximum size of scripts that can be executed. Currently, this is 16MB.
int crfd; /// Executing a 16MB script file is very likely to take longer than the typical
// Write file descriptor of the control pipe. /// timeout, so the limit on script size shouldn't be a problem in practice.
int cwfd; #define REPRL_MAX_DATA_SIZE (16 << 20)
// Read file descriptor of the data pipe.
int drfd; /// Opaque struct representing a REPRL execution context.
// Write file descriptor of the data pipe. struct reprl_context;
int dwfd;
// PID of the child process. /// Allocates a new REPRL context.
int pid; /// @return an uninitialzed REPRL context
}; struct reprl_context* reprl_create_context();
struct reprl_result { /// Initializes a REPRL context.
int child_died; /// @param ctx An uninitialized context
int status; /// @param argv The argv vector for the child processes
unsigned long exec_time; /// @param envp The envp vector for the child processes
char* output; /// @param capture_stdout Whether this REPRL context should capture the child's
size_t output_size; /// stdout
}; /// @param capture_stderr Whether this REPRL context should capture the child's
/// stderr
// Spawn a child process implementing the REPRL protocol. /// @return zero in case of no errors, otherwise a negative value
int reprl_spawn_child(char** argv, char** envp, int reprl_initialize_context(struct reprl_context* ctx, const char** argv,
struct reprl_child_process* child); const char** envp, int capture_stdout,
int capture_stderr);
// Execute the provided script in the child process, wait for its completion,
// and return the result. /// Destroys a REPRL context, freeing all resources held by it.
int reprl_execute_script(int pid, int crfd, int cwfd, int drfd, int dwfd, /// @param ctx The context to destroy
int timeout, const char* script, int64_t script_length, void reprl_destroy_context(struct reprl_context* ctx);
struct reprl_result* result);
/// 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 #endif
...@@ -3,16 +3,58 @@ ...@@ -3,16 +3,58 @@
// found in the LICENSE file. // found in the LICENSE file.
extern "C" { extern "C" {
#include <stdio.h>
#include <string.h>
#include "libreprl.h" #include "libreprl.h"
}
int main() { int main(int argc, char** argv) {
struct reprl_child_process child; struct reprl_context* ctx = reprl_create_context();
char* env[] = {nullptr};
char prog[] = "./out.gn/x64.debug/d8"; const char* env[] = {nullptr};
char*(argv[]) = {prog, nullptr}; const char* prog = argc > 1 ? argv[1] : "./out.gn/x64.debug/d8";
if (reprl_spawn_child(argv, env, &child) == -1) return -1; const char* args[] = {prog, nullptr};
// struct reprl_result res; if (reprl_initialize_context(ctx, args, env, 1, 1) != 0) {
// reprl_execute_script(child.pid, child.crfd, child.cwfd, child.drfd, printf("REPRL initialization failed\n");
// child.dwfd, 1, ,,&res); 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; 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