v8/test/fuzzilli/libreprl.c
Peter Ralbovsky 70eb08982c 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/+/2201760
Reviewed-by: Tamer Tas <tmrts@chromium.org>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68132}
2020-06-03 09:53:24 +00:00

220 lines
6.4 KiB
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 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;
}