summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile23
-rw-r--r--fuzz/README27
-rw-r--r--fuzz/fuzz.dict257
-rw-r--r--fuzz/fuzz_common.h22
-rw-r--r--fuzz/fuzz_compile.c93
-rw-r--r--fuzz/fuzz_eval.c49
-rw-r--r--fuzz/fuzz_regexp.c59
-rw-r--r--fuzz/generate_dict.js24
8 files changed, 553 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 0270a6a..be0ff47 100644
--- a/Makefile
+++ b/Makefile
@@ -111,6 +111,7 @@ ifdef CONFIG_CLANG
AR=$(CROSS_PREFIX)ar
endif
endif
+ LIB_FUZZING_ENGINE ?= "-fsanitize=fuzzer"
else ifdef CONFIG_COSMO
CONFIG_LTO=
HOST_CC=gcc
@@ -248,6 +249,17 @@ qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+fuzz_eval: $(OBJDIR)/fuzz_eval.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
+ $(CC) $(CFLAGS_OPT) $^ -o fuzz_eval $(LIB_FUZZING_ENGINE)
+
+fuzz_compile: $(OBJDIR)/fuzz_compile.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
+ $(CC) $(CFLAGS_OPT) $^ -o fuzz_compile $(LIB_FUZZING_ENGINE)
+
+fuzz_regexp: $(OBJDIR)/fuzz_regexp.o $(OBJDIR)/libregexp.fuzz.o $(OBJDIR)/cutils.fuzz.o $(OBJDIR)/libunicode.fuzz.o
+ $(CC) $(CFLAGS_OPT) $^ -o fuzz_regexp $(LIB_FUZZING_ENGINE)
+
+libfuzzer: fuzz_eval fuzz_compile fuzz_regexp
+
ifneq ($(CROSS_PREFIX),)
$(QJSC): $(OBJDIR)/qjsc.host.o \
@@ -289,6 +301,9 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^
endif # CONFIG_LTO
+libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS))
+ $(AR) rcs $@ $^
+
repl.c: $(QJSC) repl.js
$(QJSC) -c -o $@ -m repl.js
@@ -317,6 +332,9 @@ run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -c -o $@ $<
+$(OBJDIR)/fuzz_%.o: fuzz/fuzz_%.c | $(OBJDIR)
+ $(CC) $(CFLAGS_OPT) -c -I. -o $@ $<
+
$(OBJDIR)/%.host.o: %.c | $(OBJDIR)
$(HOST_CC) $(CFLAGS_OPT) -c -o $@ $<
@@ -335,6 +353,9 @@ $(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_DEBUG) -c -o $@ $<
+$(OBJDIR)/%.fuzz.o: %.c | $(OBJDIR)
+ $(CC) $(CFLAGS_OPT) -fsanitize=fuzzer-no-link -c -o $@ $<
+
$(OBJDIR)/%.check.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<
@@ -346,7 +367,7 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
clean:
rm -f repl.c qjscalc.c out.c
- rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS)
+ rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS)
rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug
diff --git a/fuzz/README b/fuzz/README
new file mode 100644
index 0000000..18c71cd
--- /dev/null
+++ b/fuzz/README
@@ -0,0 +1,27 @@
+libFuzzer support for QuickJS
+=============================
+
+Build QuickJS with libFuzzer support as follows:
+
+ CONFIG_CLANG=y make libfuzzer
+
+This can be extended with sanitizer support to improve efficacy:
+
+ CONFIG_CLANG=y CONFIG_ASAN=y make libfuzzer
+
+
+Currently, there are three fuzzing targets defined: fuzz_eval, fuzz_compile and fuzz_regexp.
+The above build command will produce an executable binary for each of them, which can be
+simply executed as:
+
+ ./fuzz_eval
+
+or with an initial corpus:
+
+ ./fuzz_compile corpus_dir/
+
+or with a predefined dictionary to improve its efficacy:
+
+ ./fuzz_eval -dict fuzz/fuzz.dict
+
+or with arbitrary CLI arguments provided by libFuzzer (https://llvm.org/docs/LibFuzzer.html).
diff --git a/fuzz/fuzz.dict b/fuzz/fuzz.dict
new file mode 100644
index 0000000..a5010e4
--- /dev/null
+++ b/fuzz/fuzz.dict
@@ -0,0 +1,257 @@
+"__loadScript"
+"abs"
+"acos"
+"acosh"
+"add"
+"AggregateError"
+"and"
+"apply"
+"Array"
+"ArrayBuffer"
+"asin"
+"asinh"
+"atan"
+"atan2"
+"atanh"
+"Atomics"
+"BigDecimal"
+"BigFloat"
+"BigFloatEnv"
+"BigInt"
+"BigInt64Array"
+"BigUint64Array"
+"Boolean"
+"cbrt"
+"ceil"
+"chdir"
+"clearTimeout"
+"close"
+"clz32"
+"compareExchange"
+"console"
+"construct"
+"cos"
+"cosh"
+"DataView"
+"Date"
+"decodeURI"
+"decodeURIComponent"
+"defineProperty"
+"deleteProperty"
+"dup"
+"dup2"
+"E"
+"encodeURI"
+"encodeURIComponent"
+"err"
+"Error"
+"escape"
+"eval"
+"EvalError"
+"evalScript"
+"exchange"
+"exec"
+"exit"
+"exp"
+"expm1"
+"fdopen"
+"Float32Array"
+"Float64Array"
+"floor"
+"fround"
+"Function"
+"gc"
+"get"
+"getcwd"
+"getenv"
+"getenviron"
+"getOwnPropertyDescriptor"
+"getpid"
+"getPrototypeOf"
+"globalThis"
+"has"
+"hypot"
+"imul"
+"in"
+"Infinity"
+"Int16Array"
+"Int32Array"
+"Int8Array"
+"InternalError"
+"isatty"
+"isExtensible"
+"isFinite"
+"isLockFree"
+"isNaN"
+"iterateBuiltIns"
+"JSON"
+"kill"
+"length"
+"LN10"
+"LN2"
+"load"
+"loadFile"
+"loadScript"
+"log"
+"log10"
+"LOG10E"
+"log1p"
+"log2"
+"LOG2E"
+"lstat"
+"Map"
+"Math"
+"max"
+"min"
+"mkdir"
+"NaN"
+"notify"
+"now"
+"Number"
+"O_APPEND"
+"O_CREAT"
+"O_EXCL"
+"O_RDONLY"
+"O_RDWR"
+"O_TRUNC"
+"O_WRONLY"
+"Object"
+"open"
+"Operators"
+"or"
+"os"
+"out"
+"ownKeys"
+"parse"
+"parseExtJSON"
+"parseFloat"
+"parseInt"
+"PI"
+"pipe"
+"platform"
+"popen"
+"pow"
+"preventExtensions"
+"print"
+"printf"
+"Promise"
+"Proxy"
+"puts"
+"random"
+"RangeError"
+"read"
+"readdir"
+"readlink"
+"realpath"
+"ReferenceError"
+"Reflect"
+"RegExp"
+"remove"
+"rename"
+"round"
+"S_IFBLK"
+"S_IFCHR"
+"S_IFDIR"
+"S_IFIFO"
+"S_IFLNK"
+"S_IFMT"
+"S_IFREG"
+"S_IFSOCK"
+"S_ISGID"
+"S_ISUID"
+"scriptArgs"
+"seek"
+"SEEK_CUR"
+"SEEK_END"
+"SEEK_SET"
+"set"
+"Set"
+"setenv"
+"setPrototypeOf"
+"setReadHandler"
+"setTimeout"
+"setWriteHandler"
+"SharedArrayBuffer"
+"SIGABRT"
+"SIGALRM"
+"SIGCHLD"
+"SIGCONT"
+"SIGFPE"
+"SIGILL"
+"SIGINT"
+"sign"
+"signal"
+"SIGPIPE"
+"SIGQUIT"
+"SIGSEGV"
+"SIGSTOP"
+"SIGTERM"
+"SIGTSTP"
+"SIGTTIN"
+"SIGTTOU"
+"SIGUSR1"
+"SIGUSR2"
+"sin"
+"sinh"
+"sleep"
+"sleepAsync"
+"sprintf"
+"sqrt"
+"SQRT1_2"
+"SQRT2"
+"stat"
+"std"
+"store"
+"strerror"
+"String"
+"stringify"
+"sub"
+"Symbol"
+"symlink"
+"SyntaxError"
+"tan"
+"tanh"
+"tmpfile"
+"trunc"
+"ttyGetWinSize"
+"ttySetRaw"
+"TypeError"
+"Uint16Array"
+"Uint32Array"
+"Uint8Array"
+"Uint8ClampedArray"
+"undefined"
+"unescape"
+"unsetenv"
+"URIError"
+"urlGet"
+"utimes"
+"wait"
+"waitpid"
+"WeakMap"
+"WeakSet"
+"WNOHANG"
+"Worker"
+"write"
+"xor"
+"v0"
+"v1"
+"v2"
+"v3"
+"v4"
+"v5"
+"v6"
+"v7"
+"v8"
+"v9"
+"v10"
+"v11"
+"v12"
+"v13"
+"v14"
+"v15"
+"v16"
+"v17"
+"v18"
+"v19"
+"v20"
diff --git a/fuzz/fuzz_common.h b/fuzz/fuzz_common.h
new file mode 100644
index 0000000..10cb497
--- /dev/null
+++ b/fuzz/fuzz_common.h
@@ -0,0 +1,22 @@
+/* Copyright 2020 Google Inc.
+
+ 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
+
+ http://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 "quickjs.h"
+#include "quickjs-libc.h"
+
+static int nbinterrupts = 0;
+
+void reset_nbinterrupts();
+void test_one_input_init(JSRuntime *rt, JSContext *ctx);
diff --git a/fuzz/fuzz_compile.c b/fuzz/fuzz_compile.c
new file mode 100644
index 0000000..0ab1b03
--- /dev/null
+++ b/fuzz/fuzz_compile.c
@@ -0,0 +1,93 @@
+/* Copyright 2020 Google Inc.
+
+ 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
+
+ http://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 "quickjs.h"
+#include "quickjs-libc.h"
+#include "cutils.h"
+#include "fuzz/fuzz_common.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size == 0)
+ return 0;
+
+ JSRuntime *rt = JS_NewRuntime();
+ JSContext *ctx = JS_NewContext(rt);
+ test_one_input_init(rt, ctx);
+
+ uint8_t *null_terminated_data = malloc(size + 1);
+ memcpy(null_terminated_data, data, size);
+ null_terminated_data[size] = 0;
+
+ JSValue obj = JS_Eval(ctx, (const char *)null_terminated_data, size, "<none>", JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_MODULE);
+ free(null_terminated_data);
+ //TODO target with JS_ParseJSON
+ if (JS_IsException(obj)) {
+ js_std_free_handlers(rt);
+ JS_FreeValue(ctx, obj);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+ return 0;
+ }
+ obj = js_std_await(ctx, obj);
+ size_t bytecode_size;
+ uint8_t* bytecode = JS_WriteObject(ctx, &bytecode_size, obj, JS_WRITE_OBJ_BYTECODE);
+ JS_FreeValue(ctx, obj);
+ if (!bytecode) {
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+ return 0;
+ }
+ obj = JS_ReadObject(ctx, bytecode, bytecode_size, JS_READ_OBJ_BYTECODE);
+ js_free(ctx, bytecode);
+ if (JS_IsException(obj)) {
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+ return 0;
+ }
+ reset_nbinterrupts();
+ /* this is based on
+ * js_std_eval_binary(ctx, bytecode, bytecode_size, 0);
+ * modified so as not to exit on JS exception
+ */
+ JSValue val;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
+ if (JS_ResolveModule(ctx, obj) < 0) {
+ JS_FreeValue(ctx, obj);
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+ return 0;
+ }
+ js_module_set_import_meta(ctx, obj, FALSE, TRUE);
+ }
+ val = JS_EvalFunction(ctx, obj);
+ if (JS_IsException(val)) {
+ js_std_dump_error(ctx);
+ } else {
+ js_std_loop(ctx);
+ }
+ JS_FreeValue(ctx, val);
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+
+ return 0;
+}
diff --git a/fuzz/fuzz_eval.c b/fuzz/fuzz_eval.c
new file mode 100644
index 0000000..aa26f1e
--- /dev/null
+++ b/fuzz/fuzz_eval.c
@@ -0,0 +1,49 @@
+/* Copyright 2020 Google Inc.
+
+ 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
+
+ http://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 "quickjs.h"
+#include "quickjs-libc.h"
+#include "fuzz/fuzz_common.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size == 0)
+ return 0;
+
+ JSRuntime *rt = JS_NewRuntime();
+ JSContext *ctx = JS_NewContext(rt);
+ test_one_input_init(rt, ctx);
+
+ uint8_t *null_terminated_data = malloc(size + 1);
+ memcpy(null_terminated_data, data, size);
+ null_terminated_data[size] = 0;
+
+ reset_nbinterrupts();
+ //the final 0 does not count (as in strlen)
+ JSValue val = JS_Eval(ctx, (const char *)null_terminated_data, size, "<none>", JS_EVAL_TYPE_GLOBAL);
+ free(null_terminated_data);
+ //TODO targets with JS_ParseJSON, JS_ReadObject
+ if (!JS_IsException(val)) {
+ js_std_loop(ctx);
+ JS_FreeValue(ctx, val);
+ }
+ js_std_free_handlers(rt);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+ return 0;
+}
diff --git a/fuzz/fuzz_regexp.c b/fuzz/fuzz_regexp.c
new file mode 100644
index 0000000..29d1951
--- /dev/null
+++ b/fuzz/fuzz_regexp.c
@@ -0,0 +1,59 @@
+/* Copyright 2020 Google Inc.
+
+ 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
+
+ http://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 "libregexp.h"
+#include "quickjs-libc.h"
+
+
+int lre_check_stack_overflow(void *opaque, size_t alloca_size) { return 0; }
+
+void *lre_realloc(void *opaque, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ int len, ret, i;
+ uint8_t *bc;
+ char error_msg[64];
+ const uint8_t *input;
+ uint8_t *capture[255 * 2];
+ size_t size1 = size;
+
+ //Splits buffer into 2 sub buffers delimited by null character
+ for (i = 0; i < size; i++) {
+ if (data[i] == 0) {
+ size1 = i;
+ break;
+ }
+ }
+ if (size1 == size) {
+ //missing delimiter
+ return 0;
+ }
+ bc = lre_compile(&len, error_msg, sizeof(error_msg), (const char *) data,
+ size1, 0, NULL);
+ if (!bc) {
+ return 0;
+ }
+ input = data + size1 + 1;
+ ret = lre_exec(capture, bc, input, 0, size - (size1 + 1), 0, NULL);
+ if (ret == 1) {
+ lre_get_capture_count(bc);
+ }
+ free(bc);
+
+ return 0;
+}
diff --git a/fuzz/generate_dict.js b/fuzz/generate_dict.js
new file mode 100644
index 0000000..1366fea
--- /dev/null
+++ b/fuzz/generate_dict.js
@@ -0,0 +1,24 @@
+// Function to recursively iterate through built-in names.
+function collectBuiltinNames(obj, visited = new Set(), result = new Set()) {
+ // Check if the object has already been visited to avoid infinite recursion.
+ if (visited.has(obj))
+ return;
+
+ // Add the current object to the set of visited objects
+ visited.add(obj);
+ // Get the property names of the current object
+ const properties = Object.getOwnPropertyNames(obj);
+ // Iterate through each property
+ for (var i=0; i < properties.length; i++) {
+ var property = properties[i];
+ if (property != "collectBuiltinNames" && typeof property != "number")
+ result.add(property);
+ // Check if the property is an object and if so, recursively iterate through its properties.
+ if (typeof obj[property] === 'object' && obj[property] !== null)
+ collectBuiltinNames(obj[property], visited, result);
+ }
+ return result;
+}
+
+// Start the recursive iteration with the global object.
+console.log(Array.from(collectBuiltinNames(this)).join('\n'));