Commit 70eb0898 authored by Peter Ralbovsky's avatar Peter Ralbovsky Committed by Commit Bot

Integrate fuzzilli into v8

Fuzzilli is open source fuzzer by Samuel Groß (saelo@google.com)
that can be used to find bugs in v8 javascript engine. As we want
to automate fuzzing for current versions of v8, we want to merge
fuzzilli toolkit into v8 code, so that fuzzer can automatically
update to the newest version.
So far Fuzzilli has been maintained at
https://github.com/googleprojectzero/fuzzilli .


Bug tracker Id: https://bugs.chromium.org/p/v8/issues/detail?id=10571

Change-Id: I83ddc7e8bb31664c19e4044395bb9044a1c12031
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2201760Reviewed-by: 's avatarTamer Tas <tmrts@chromium.org>
Reviewed-by: 's avatarMichael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68132}
parent d9337dc1
......@@ -577,6 +577,9 @@ config("features") {
if (v8_enable_nci_code) {
defines += [ "V8_ENABLE_NCI_CODE" ]
}
if (v8_fuzzilli) {
defines += [ "V8_FUZZILLI" ]
}
}
config("toolchain") {
......@@ -3235,6 +3238,13 @@ v8_source_set("v8_base_without_compiler") {
]
}
if (v8_fuzzilli) {
sources += [
"src/d8/cov.cc",
"src/d8/cov.h",
]
}
if (v8_check_header_includes) {
# This file will be generated by tools/generate-header-include-checks.py
# if the "check_v8_header_includes" gclient variable is set.
......
......@@ -57,11 +57,14 @@ declare_args() {
# Implement tracing using Perfetto (https://perfetto.dev).
v8_use_perfetto = false
# Override global symbol level setting for v8
# Override global symbol level setting for v8.
v8_symbol_level = symbol_level
# Enable WebAssembly debugging via GDB-remote protocol.
v8_enable_wasm_gdb_remote_debugging = false
# Add fuzzilli fuzzer support.
v8_fuzzilli = false
}
if (v8_use_external_startup_data == "") {
......
......@@ -503,7 +503,11 @@ void Utils::ReportOOMFailure(i::Isolate* isolate, const char* location,
if (fatal_callback == nullptr) {
base::OS::PrintError("\n#\n# Fatal %s OOM in %s\n#\n\n",
is_heap_oom ? "javascript" : "process", location);
#ifdef V8_FUZZILLI
exit(0);
#else
base::OS::Abort();
#endif // V8_FUZZILLI
} else {
fatal_callback(location,
is_heap_oom
......
// 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;
}
// 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_
This diff is collapsed.
......@@ -438,6 +438,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.
......
......@@ -9,8 +9,8 @@ group("gn_all") {
data_deps = [
"benchmarks:v8_benchmarks",
"intl:v8_intl",
"fuzzer:v8_fuzzer",
"intl:v8_intl",
"message:v8_message",
"mjsunit:v8_mjsunit",
"mozilla:v8_mozilla",
......@@ -26,6 +26,10 @@ group("gn_all") {
"wasm-api-tests:wasm_api_tests",
]
if (v8_fuzzilli) {
deps += [ "fuzzilli:v8_fuzzilli_test" ]
}
if (host_os != "mac" || !is_android) {
# These items don't compile for Android on Mac.
deps += [
......@@ -44,8 +48,8 @@ group("v8_perf") {
testonly = true
data_deps = [
"..:v8_python_base",
"..:d8",
"..:v8_python_base",
"../tools:v8_android_test_runner_deps",
"../tools:v8_testrunner",
]
......
# 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.
import("../../gni/v8.gni")
v8_executable("v8_fuzzilli_test") {
testonly = true
sources = [
"libreprl.c",
"libreprl.h",
"main.cc",
]
configs = []
deps = []
data = []
}
# Communication model of fuzzilli with V8
## Source code
On low level fuzzilli communicates with v8 through Swift C API library in `Sources/libreprl/libreprl.c`
`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.
`fetch_output` fetches the output from the child and returns its size and pointer to data.
`execute script`
writes `exec`, and size of script, into the command write pipe and sends script through data write pipe
## Coverage
Coverage information are being monitored through shared memory. On the side of v8 it is monitored through SanitizerCoverage module of Clang compiler ( https://clang.llvm.org/docs/SanitizerCoverage.html ) Through shared memory information about edges are shared with fuzzilli which implements counter for error and covered branches of the V8 code in Sources/libcoverage/coverage.c
# 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.
[]
// 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.
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include "libreprl.h"
// Well-known file descriptor numbers for fuzzer <-> fuzzee communication on child process side.
#define CRFD 100
#define CWFD 101
#define DRFD 102
#define DWFD 103
#define CHECK_SUCCESS(cond) if((cond) < 0) { perror(#cond); abort(); }
#define CHECK(cond) if(!(cond)) { fprintf(stderr, "(" #cond ") failed!"); abort(); }
static uint64_t current_millis()
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
int reprl_spawn_child(char** argv, char** envp, struct reprl_child_process* child)
{
// We need to make sure that our fds don't end up being 100 - 104.
if (fcntl(CRFD, F_GETFD) == -1) {
int devnull = open("/dev/null", O_RDWR);
dup2(devnull, CRFD);
dup2(devnull, CWFD);
dup2(devnull, DRFD);
dup2(devnull, DWFD);
close(devnull);
}
int crpipe[2] = { 0, 0 }; // control channel child -> fuzzer
int cwpipe[2] = { 0, 0 }; // control channel fuzzer -> child
int drpipe[2] = { 0, 0 }; // data channel child -> fuzzer
int dwpipe[2] = { 0, 0 }; // data channel fuzzer -> child
int res = 0;
res |= pipe(crpipe);
res |= pipe(cwpipe);
res |= pipe(drpipe);
res |= pipe(dwpipe);
if (res != 0) {
if (crpipe[0] != 0) { close(crpipe[0]); close(crpipe[1]); }
if (cwpipe[0] != 0) { close(cwpipe[0]); close(cwpipe[1]); }
if (drpipe[0] != 0) { close(drpipe[0]); close(drpipe[1]); }
if (dwpipe[0] != 0) { close(dwpipe[0]); close(dwpipe[1]); }
fprintf(stderr, "[REPRL] Could not setup pipes for communication with child: %s\n", strerror(errno));
return -1;
}
child->crfd = crpipe[0];
child->cwfd = cwpipe[1];
child->drfd = drpipe[0];
child->dwfd = dwpipe[1];
int flags;
flags = fcntl(child->drfd, F_GETFL, 0);
fcntl(child->drfd, F_SETFL, flags | O_NONBLOCK);
fcntl(child->crfd, F_SETFD, FD_CLOEXEC);
fcntl(child->cwfd, F_SETFD, FD_CLOEXEC);
fcntl(child->drfd, F_SETFD, FD_CLOEXEC);
fcntl(child->dwfd, F_SETFD, FD_CLOEXEC);
int pid = fork();
if (pid == 0) {
dup2(cwpipe[0], CRFD);
dup2(crpipe[1], CWFD);
dup2(dwpipe[0], DRFD);
dup2(drpipe[1], DWFD);
close(cwpipe[0]);
close(crpipe[1]);
close(dwpipe[0]);
close(drpipe[1]);
int devnull = open("/dev/null", O_RDWR);
dup2(devnull, 0);
dup2(devnull, 1);
dup2(devnull, 2);
close(devnull);
execve(argv[0], argv, envp);
fprintf(stderr, "[REPRL] Failed to spawn child process\n");
_exit(-1);
} else if (pid < 0) {
fprintf(stderr, "[REPRL] Failed to fork\n");
return -1;
}
close(crpipe[1]);
close(cwpipe[0]);
close(drpipe[1]);
close(dwpipe[0]);
child->pid = pid;
int helo;
if (read(child->crfd, &helo, 4) != 4 || write(child->cwfd, &helo, 4) != 4) {
fprintf(stderr, "[REPRL] Failed to communicate with child process\n");
close(child->crfd);
close(child->cwfd);
close(child->drfd);
close(child->dwfd);
int status;
kill(pid, SIGKILL);
waitpid(pid, &status, 0);
return -1;
}
return 0;
}
static char* fetch_output(int fd, size_t* outsize)
{
ssize_t rv;
*outsize = 0;
size_t remaining = 0x1000;
char* outbuf = malloc(remaining + 1);
do {
rv = read(fd, outbuf + *outsize, remaining);
if (rv == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
fprintf(stderr, "[REPRL] Error while receiving data: %s\n", strerror(errno));
}
break;
}
*outsize += rv;
remaining -= rv;
if (remaining == 0) {
remaining = *outsize;
outbuf = realloc(outbuf, *outsize * 2 + 1);
if (!outbuf) {
fprintf(stderr, "[REPRL] Could not allocate output buffer");
_exit(-1);
}
}
} while (rv > 0);
outbuf[*outsize] = 0;
return outbuf;
}
// Execute one script, 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)
{
uint64_t start_time = current_millis();
if (write(cwfd, "exec", 4) != 4 ||
write(cwfd, &script_length, 8) != 8) {
fprintf(stderr, "[REPRL] Failed to send command to child process\n");
return -1;
}
int64_t remaining = script_length;
while (remaining > 0) {
ssize_t rv = write(dwfd, script, remaining);
if (rv <= 0) {
fprintf(stderr, "[REPRL] Failed to send script to child process\n");
return -1;
}
remaining -= rv;
script += rv;
}
struct pollfd fds = {.fd = crfd, .events = POLLIN, .revents = 0};
if (poll(&fds, 1, timeout) != 1) {
kill(pid, SIGKILL);
waitpid(pid, &result->status, 0);
result->child_died = 1;
} else {
result->child_died = 0;
ssize_t rv = read(crfd, &result->status, 4);
if (rv != 4) {
// This should not happen...
kill(pid, SIGKILL);
waitpid(pid, &result->status, 0);
result->child_died = 1;
}
}
result->output = fetch_output(drfd, &result->output_size);
result->exec_time = current_millis() - start_time;
return 0;
}
// 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.
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __LIBREPRL_H__
#define __LIBREPRL_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);
#endif
// 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.
extern "C" {
#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);
return 0;
}
# Copyright 2016 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.
import os
from testrunner.local import testsuite
from testrunner.objects import testcase
SUB_TESTS = [
'test',
]
class VariantsGenerator(testsuite.VariantsGenerator):
def _get_variants(self, test):
return self._standard_variant
class TestLoader(testsuite.GenericTestLoader):
@property
def test_dirs(self):
return SUB_TESTS
def _to_relpath(self, abspath, _):
return os.path.relpath(abspath, self.suite.root)
class TestSuite(testsuite.TestSuite):
def _test_loader_class(self):
return TestLoader
def _test_class(self):
return TestCase
def _variants_gen_class(self):
return VariantsGenerator
class TestCase(testcase.TestCase):
def _get_files_params(self):
suite, name = self.path.split(os.path.sep)
return [os.path.join(self.suite.root, suite, name)]
def _get_variant_flags(self):
return []
def _get_statusfile_flags(self):
return []
def _get_mode_flags(self):
return []
def get_shell(self):
return 'v8_fuzzilli_test'
def GetSuite(*args, **kwargs):
return TestSuite(*args, **kwargs)
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