Browse Source

Merge pull request #2321 from WilliamParks/fuzzilli

Base integration of duktape with the Fuzzilli Javascript engine fuzzer
pull/2329/head
Sami Vaarala 4 years ago
committed by GitHub
parent
commit
7cbbcfe74e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 1
      AUTHORS.rst
  3. 11
      Makefile
  4. 7
      config/config-options/DUK_USE_FUZZILLI.yaml
  5. 249
      examples/cmdline/duk_cmdline.c
  6. 9
      src-input/duk_error_misc.c
  7. 10
      util/makeduk_fuzz.yaml

1
.gitignore

@ -11,6 +11,7 @@
/dukd
/dukd.*
/duk-clang
/duk-fuzzilli
/duk-sanitize-clang
/duk-g++
/duk-size

1
AUTHORS.rst

@ -67,6 +67,7 @@ and agreed to irrevocably license their contributions under the Duktape
* Craig Leres (https://github.com/leres)
* Maurici Abad (https://github.com/mauriciabad)
* Nancy Li (https://github.com/NancyLi1013)
* William Parks (https://github.com/WilliamParks)
Other contributions
===================

11
Makefile

@ -91,6 +91,7 @@ CONFIGOPTS_DEBUG_SCANBUILD = --option-file util/makeduk_base.yaml --option-file
CONFIGOPTS_DEBUG_ROM = --rom-support --rom-auto-lightfunc --option-file util/makeduk_base.yaml --option-file util/makeduk_debug.yaml -DDUK_USE_ROM_STRINGS -DDUK_USE_ROM_OBJECTS -DDUK_USE_ROM_GLOBAL_INHERIT -UDUK_USE_HSTRING_ARRIDX
CONFIGOPTS_EMDUK = -UDUK_USE_FASTINT -UDUK_USE_PACKED_TVAL
CONFIGOPTS_DUKWEB = --option-file util/dukweb_base.yaml --fixup-file util/dukweb_fixup.h
CONFIGOPTS_FUZZ = --option-file util/makeduk_base.yaml --option-file util/makeduk_fuzz.yaml
# Profile guided optimization test set.
PGO_TEST_SET = \
@ -266,7 +267,7 @@ clean:
@rm -rf /tmp/dukweb-test/
@rm -f massif-*.out
@rm -f literal_intern_test
@rm -f duk-fuzzilli
.PHONY: cleanall
cleanall: clean
# Don't delete these in 'clean' to avoid re-downloading them over and over
@ -325,6 +326,9 @@ prep/nondebug-rom: prep
prep/debug: prep
@rm -rf ./prep/debug
$(PYTHON) tools/configure.py --output-directory ./prep/debug --source-directory src-input --config-metadata config $(CONFIGOPTS_DEBUG) --line-directives
prep/fuzz: prep
@rm -rf ./prep/fuzz
$(PYTHON) tools/configure.py --output-directory ./prep/fuzz --source-directory src-input --config-metadata config $(CONFIGOPTS_FUZZ) --line-directives
prep/debug-scanbuild: prep
@rm -rf ./prep/debug-scanbuild
$(PYTHON) tools/configure.py --output-directory ./prep/debug-scanbuild --source-directory src-input --config-metadata config $(CONFIGOPTS_DEBUG_SCANBUILD) --separate-sources --line-directives
@ -466,6 +470,11 @@ duk-sanitize-clang: linenoise prep/nondebug
clang -o $@ -Wcast-align -Wshift-sign-overflow -fsanitize=undefined -Iprep/nondebug $(CLANG_CCOPTS_NONDEBUG) prep/nondebug/duktape.c $(DUKTAPE_CMDLINE_SOURCES) $(LINENOISE_SOURCES) $(CCLIBS)
@ls -l $@
-@size $@
duk-fuzzilli: linenoise prep/fuzz examples/cmdline/duk_cmdline.c
# Target for fuzzilli. Adds in the appropriate debug flags, without doing the debug prints
clang -O3 -o $@ -Wcast-align -Wshift-sign-overflow -fsanitize=undefined -fsanitize-coverage=trace-pc-guard -Iprep/fuzz $(CLANG_CCOPTS_DEBUG) prep/fuzz/duktape.c $(DUKTAPE_CMDLINE_SOURCES) $(LINENOISE_SOURCES) $(CCLIBS)
@ls -l $@
-@size $@
duk-perf-clang: linenoise prep/nondebug-perf
clang -o $@ -Wcast-align -Wshift-sign-overflow -Iprep/nondebug-perf $(CLANG_CCOPTS_NONDEBUG) prep/nondebug-perf/duktape.c $(DUKTAPE_CMDLINE_SOURCES) $(LINENOISE_SOURCES) $(CCLIBS)
@ls -l $@

7
config/config-options/DUK_USE_FUZZILLI.yaml

@ -0,0 +1,7 @@
define: DUK_USE_FUZZILLI
introduced: 3.0.0
default: false
tags:
- fuzzing
description: >
Enables access to the assertion debug wrapper needed for fuzzilli integration

249
examples/cmdline/duk_cmdline.c

@ -101,6 +101,21 @@
#include "duk_trans_socket.h"
#endif
#if defined(DUK_USE_FUZZILLI)
/* REPRL Pipe file descriptors for quicker fuzzing */
#define REPRL_CRFD 100
#define REPRL_CWFD 101
#define REPRL_DRFD 102
#define REPRL_DWFD 103
/* Required includes for coverage tracking */
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#define MEM_LIMIT_NORMAL (128*1024*1024) /* 128 MB */
#define MEM_LIMIT_HIGH (2047*1024*1024) /* ~2 GB */
#define LINEBUF_SIZE 65536
@ -974,6 +989,65 @@ static duk_ret_t sys_execute(duk_context *ctx) {
}
#endif
#if defined(DUK_USE_FUZZILLI)
/*
* Custom builtin for Fuzzilli, used to ensure the fuzzer is properly catching crashes and assert failures
*/
void duk_assert_wrapper(int x);
static duk_ret_t fuzzilli(duk_context *ctx) {
duk_idx_t i, nargs;
nargs = duk_get_top(ctx);
/* Ensure right number of args */
if(nargs != 2){
return -1;
}
/* Ensure first arg is a string */
if(!duk_is_string(ctx, 0)){
return -1;
}
const char * first_arg = duk_get_string(ctx, 0);
/*
* This is to enable
*/
if(strcmp(first_arg, "FUZZILLI_CRASH") == 0){
duk_int_t second_arg = duk_get_int(ctx, 1);
switch(second_arg){
case 0:
*((duk_int_t *)0x41414141) = 0x1337; /* intentionally segfault */
break;
default:
duk_assert_wrapper(0); /* Intentionally fail assertion */
break;
}
}else if(strcmp(first_arg, "FUZZILLI_PRINT") == 0){
FILE* fzliout = fdopen(REPRL_DWFD, "w");
if (!fzliout) {
fprintf(stderr, "Fuzzer output channel not available, printing to stdout instead\n");
fzliout = stdout;
}
const char * string = duk_get_string(ctx, 1);
if (string == NULL) {
return -1;
}
fprintf(fzliout, "%s\n", string);
fflush(fzliout);
}
return 0;
}
#endif /* DUK_USE_FUZZILLI */
/*
* String.fromBufferRaw()
*/
@ -1245,6 +1319,15 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int lo
}
#endif
#if defined(DUK_USE_FUZZILLI)
/*
* Add the custom function for fuzzilli
*/
duk_push_c_function(ctx, fuzzilli, DUK_VARARGS);
duk_put_global_string(ctx, "fuzzilli");
#endif /* DUK_USE_FUZZILLI */
return ctx;
}
@ -1276,6 +1359,93 @@ static void destroy_duktape_heap(duk_context *ctx, int alloc_provider) {
#endif
}
#if defined(DUK_USE_FUZZILLI)
/*
* Enables coverage tracking for fuzzing
*/
void __sanitizer_cov_reset_edgeguards();
#define SHM_SIZE 0x100000
#define MAX_EDGES ((SHM_SIZE - 4) * 8)
#define CHECK(cond) if (!(cond)) { fprintf(stderr, "\"" #cond "\" failed\n"); _exit(-1); }
struct shmem_data {
duk_uint32_t num_edges;
unsigned char edges[];
};
struct shmem_data* __shmem;
duk_uint32_t *__edges_start, *__edges_stop;
void __sanitizer_cov_reset_edgeguards() {
duk_uint64_t N = 0;
for (duk_uint32_t *x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
*x = ++N;
}
void __sanitizer_cov_trace_pc_guard_init(duk_uint32_t *start, duk_uint32_t *stop) {
/*
* Avoid duplicate initialization
*/
if (start == stop || *start)
return;
if (__edges_start != NULL || __edges_stop != NULL) {
fprintf(stderr, "Coverage instrumentation is only supported for a single module\n");
_exit(-1);
}
__edges_start = start;
__edges_stop = 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, "Failed to open shared memory region: %s\n", strerror(errno));
_exit(-1);
}
__shmem = (struct shmem_data*) mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (__shmem == MAP_FAILED) {
fprintf(stderr, "Failed to mmap shared memory region\n");
_exit(-1);
}
}
__sanitizer_cov_reset_edgeguards();
__shmem->num_edges = stop - start;
printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n", shm_key, __shmem->num_edges);
}
void __sanitizer_cov_trace_pc_guard(duk_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.
*/
duk_uint32_t index = *guard;
/*
* If this function is called before coverage instrumentation is properly initialized we want to return early.
*/
if (!index) return;
__shmem->edges[index / 8] |= 1 << (index % 8);
*guard = 0;
}
#endif /* DUK_USE_FUZZILLI */
/*
* Main
*/
@ -1296,6 +1466,9 @@ int main(int argc, const char *argv[]) {
int run_stdin = 0;
const char *compile_filename = NULL;
int i;
#if defined(DUK_USE_FUZZILLI)
int reprl_mode = 0;
#endif /* DUK_USE_FUZZILLI */
main_argc = argc;
main_argv = (const char **) argv;
@ -1410,7 +1583,11 @@ int main(int argc, const char *argv[]) {
} else if (strcmp(arg, "--reattach") == 0) {
debugger_reattach = 1;
#endif
} else if (strcmp(arg, "--recreate-heap") == 0) {
#if defined(DUK_USE_FUZZILLI)
} else if (strcmp(arg, "--reprl") == 0) {
reprl_mode = 1;
#endif
} else if (strcmp(arg, "--recreate-heap") == 0) {
recreate_heap = 1;
} else if (strcmp(arg, "--no-heap-destroy") == 0) {
no_heap_destroy = 1;
@ -1502,7 +1679,75 @@ int main(int argc, const char *argv[]) {
}
}
if (run_stdin) {
#if defined(DUK_USE_FUZZILLI)
/* Run the fuzzilli run-eval-print-repeat loop, and exit at the end*/
if(reprl_mode){
/* REPRL: let parent know we are ready */
char helo[4] = "HELO";
if (write(REPRL_CWFD, helo, 4) != 4 ||
read(REPRL_CRFD, helo, 4) != 4) {
reprl_mode = 0;
}
if (memcmp(helo, "HELO", 4) != 0) {
fprintf(stderr, "Invalid response from parent\n");
_exit(-1);
}
while(reprl_mode) {
unsigned action = 0;
duk_int64_t nread = read(REPRL_CRFD, &action, 4);
if (nread != 4 || action != 'cexe') {
fprintf(stderr, "Unknown action: %u\n", action);
duk_assert_wrapper(0);
_exit(-1);
}
duk_size_t script_size;
duk_assert_wrapper(read(REPRL_CRFD, &script_size, 8) == 8);
char static_buff[4096];
char * buffer;
/* In practice, we're never going to get to 4k long input scripts (21 core days got to ~80 bytes) */
if(script_size > 4095) {
buffer = (char *) malloc((sizeof(char) * (script_size + 1)));
}else{
buffer = static_buff;
}
char *ptr = buffer;
duk_size_t remaining = script_size;
while (remaining > 0) {
duk_int64_t rv = read(REPRL_DRFD, ptr, remaining);
duk_assert_wrapper(rv >= 0);
remaining -= rv;
ptr += rv;
}
buffer[script_size] = 0;
/* Actually execute */
duk_push_string(ctx, buffer);
duk_int_t rc = duk_peval(ctx);
/* Return result to parent */
rc <<= 8;
duk_assert_wrapper(write(REPRL_CWFD, &rc, 4) == 4);
/* Clean up this round */
if(script_size > 4095) {
free(buffer);
}
duk_destroy_heap(ctx);
ctx = create_duktape_heap(alloc_provider, debugger, lowmem_log);
__sanitizer_cov_reset_edgeguards();
}
return 0;
}
#endif
if (run_stdin) {
/* Running stdin like a full file (reading all lines before
* compiling) is useful with emduk:
* cat test.js | ./emduk --run-stdin

9
src-input/duk_error_misc.c

@ -172,3 +172,12 @@ DUK_INTERNAL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_t
DUK_ASSERT_LJSTATE_SET(heap);
}
#if defined(DUK_USE_FUZZILLI)
/*
* Wrapper for easy usage in duk_fuzzillies. Goal is to have an easy test case, that ASSERT is triggered properly
*/
DUK_EXTERNAL_DECL void duk_assert_wrapper(int x){
DUK_ASSERT(x);
}
#endif /* DUK_USE_FUZZILLI */

10
util/makeduk_fuzz.yaml

@ -0,0 +1,10 @@
# Additional options on top of makeduk_base.yaml to enable fuzzilli integration. Currently only has assertions enabled
#DUK_USE_DEBUG_BUFSIZE: 512
#DUK_USE_DEBUG: true
#DUK_USE_DEBUG_LEVEL: 0
#DUK_USE_DEBUG_LEVEL: 1
#DUK_USE_DEBUG_WRITE:
# verbatim: "#define DUK_USE_DEBUG_WRITE(level,file,line,func,msg) do {fprintf(stderr, \"D%ld %s:%ld (%s): %s\\n\", (long) (level), (file), (long) (line), (func), (msg)); fflush(stderr);} while(0)"
DUK_USE_ASSERTIONS: true
DUK_USE_FUZZILLI: true
Loading…
Cancel
Save