diff --git a/Makefile b/Makefile index 94684e1802..f6ec8a9b15 100644 --- a/Makefile +++ b/Makefile @@ -295,7 +295,10 @@ fuzz_compile: $(OBJDIR)/fuzz_compile.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a 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 +fuzz_bytecode: $(OBJDIR)/fuzz_bytecode.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a + $(CC) $(CFLAGS_OPT) $^ -o fuzz_bytecode $(LIB_FUZZING_ENGINE) + +libfuzzer: fuzz_eval fuzz_compile fuzz_regexp fuzz_bytecode ifneq ($(CROSS_PREFIX),) diff --git a/fuzz/fuzz_bytecode.c b/fuzz/fuzz_bytecode.c index bb616a7bdc..a9a465a6ce 100644 --- a/fuzz/fuzz_bytecode.c +++ b/fuzz/fuzz_bytecode.c @@ -1,142 +1,57 @@ // Copyright 2025 Google LLC -// Fuzz target for QuickJS bytecode execution +// Fuzz target for QuickJS bytecode loading. The raw fuzzer input is treated +// as a JS_READ_OBJ_BYTECODE object, i.e. exactly the bytes qjsc emits and an +// embedding host may load from disk. The input is first deserialised with +// JS_ReadObject (the bytecode reader) and then executed like +// js_std_eval_binary does (module resolve, import meta, JS_EvalFunction, +// js_std_loop), so both the reader and the interpreter run on +// attacker-controlled bytecode. Memory/stack/interrupt limits come from +// fuzz_common.c, matching the other quickjs targets. #include "quickjs.h" #include "quickjs-libc.h" +#include "cutils.h" +#include "fuzz/fuzz_common.h" + #include -#include #include -#include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - if (size < 8) return 0; // Need at least minimal bytecode header - - JSRuntime* rt = JS_NewRuntime(); - if (!rt) return 0; - - JSContext* ctx = JS_NewContext(rt); - if (!ctx) { + if (size == 0) + return 0; + + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + test_one_input_init(rt, ctx); + + JSValue obj = JS_ReadObject(ctx, data, size, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) { + js_std_dump_error(ctx); + js_std_free_handlers(rt); + JS_FreeContext(ctx); JS_FreeRuntime(rt); return 0; } - - char load_script[256]; - snprintf(load_script, sizeof(load_script), - "(function() { " - " var buf = new Uint8Array(%zu); " - " for (var i = 0; i < %zu; i++) buf[i] = 0; " - " return evalBinary(buf); " - "})()", size, size); - - JSValue eval_result = JS_Eval(ctx, load_script, strlen(load_script), - "", 0); - - if (!JS_IsException(eval_result)) { - JS_FreeValue(ctx, eval_result); - } else { - JS_GetException(ctx); - } - - const char* simple_script = "({ a: 1, b: 'test', c: function() { return 42; } })"; - - JSValue bytecode = JS_Eval(ctx, simple_script, strlen(simple_script), - "", JS_EVAL_FLAG_COMPILE_ONLY); - - if (!JS_IsException(bytecode)) { - size_t bytecode_len; - uint8_t* bytecode_buf = JS_WriteObject(ctx, &bytecode_len, bytecode, - JS_WRITE_OBJ_BYTECODE); - - if (bytecode_buf) { - JSValue loaded = JS_ReadObject(ctx, bytecode_buf, bytecode_len, - JS_READ_OBJ_BYTECODE); - - if (!JS_IsException(loaded)) { - JSValue result = JS_EvalFunction(ctx, loaded); - if (!JS_IsException(result)) { - JS_FreeValue(ctx, result); - } else { - JS_GetException(ctx); - } - } else { - JS_GetException(ctx); - } - - js_free(ctx, bytecode_buf); - } - - JS_FreeValue(ctx, bytecode); - } else { - JS_GetException(ctx); - } - - if (size >= 8) { - uint8_t* fake_bytecode = malloc(size); - if (fake_bytecode) { - memcpy(fake_bytecode, data, size); - - if (data[0] % 2 == 0) { - fake_bytecode[0] = 'Q'; - fake_bytecode[1] = 'C'; - fake_bytecode[2] = 'A'; - fake_bytecode[3] = 'M'; - } - - JSValue malformed = JS_ReadObject(ctx, fake_bytecode, size, - JS_READ_OBJ_BYTECODE); - if (!JS_IsException(malformed)) { - JS_FreeValue(ctx, malformed); - } else { - JS_GetException(ctx); - } - - free(fake_bytecode); + + reset_nbinterrupts(); + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (JS_ResolveModule(ctx, obj) < 0) { + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 0; } + js_module_set_import_meta(ctx, obj, FALSE, TRUE); } - - const char* eval_script_test = "typeof std !== 'undefined' ? std.evalScript : null"; - JSValue std_check = JS_Eval(ctx, eval_script_test, strlen(eval_script_test), - "", 0); - if (!JS_IsException(std_check)) { - JS_FreeValue(ctx, std_check); - } else { - JS_GetException(ctx); - } - - const char* module_script = "export default 42; export const x = 123;"; - JSValue module_bytecode = JS_Eval(ctx, module_script, strlen(module_script), - "", - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - - if (!JS_IsException(module_bytecode)) { - size_t mod_bc_len; - uint8_t* mod_bc_buf = JS_WriteObject(ctx, &mod_bc_len, module_bytecode, - JS_WRITE_OBJ_BYTECODE); - - if (mod_bc_buf) { - JSValue mod_loaded = JS_ReadObject(ctx, mod_bc_buf, mod_bc_len, - JS_READ_OBJ_BYTECODE); - if (!JS_IsException(mod_loaded)) { - JSValue mod_result = JS_EvalFunction(ctx, mod_loaded); - if (!JS_IsException(mod_result)) { - JS_FreeValue(ctx, mod_result); - } else { - JS_GetException(ctx); - } - } else { - JS_GetException(ctx); - } - - js_free(ctx, mod_bc_buf); - } - - JS_FreeValue(ctx, module_bytecode); + JSValue val = JS_EvalFunction(ctx, obj); + if (!JS_IsException(val)) { + js_std_loop(ctx); + JS_FreeValue(ctx, val); } else { - JS_GetException(ctx); + js_std_dump_error(ctx); } - + js_std_free_handlers(rt); JS_FreeContext(ctx); JS_FreeRuntime(rt); - return 0; } diff --git a/fuzz/fuzz_bytecode.dict b/fuzz/fuzz_bytecode.dict new file mode 100644 index 0000000000..1167d606ea --- /dev/null +++ b/fuzz/fuzz_bytecode.dict @@ -0,0 +1,21 @@ +"\x05" +"\x05\x01" +"\x01\x00\x00" +"QC" +"other.js" +"main" +"default" +"constructor" +"length" +"prototype" +"async" +"static" +"get" +"set" +"eval" +"import" +"export" +"await" +"label" +"finally" +"instanceof" diff --git a/fuzz/fuzz_bytecode_seeds/seed-00.bin b/fuzz/fuzz_bytecode_seeds/seed-00.bin new file mode 100644 index 0000000000..26e9292322 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-00.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-01.bin b/fuzz/fuzz_bytecode_seeds/seed-01.bin new file mode 100644 index 0000000000..a9917adb04 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-01.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-02.bin b/fuzz/fuzz_bytecode_seeds/seed-02.bin new file mode 100644 index 0000000000..5aff339304 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-02.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-03.bin b/fuzz/fuzz_bytecode_seeds/seed-03.bin new file mode 100644 index 0000000000..8a90b6b5eb Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-03.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-04.bin b/fuzz/fuzz_bytecode_seeds/seed-04.bin new file mode 100644 index 0000000000..27d866b25e Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-04.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-05.bin b/fuzz/fuzz_bytecode_seeds/seed-05.bin new file mode 100644 index 0000000000..7841d53748 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-05.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-06.bin b/fuzz/fuzz_bytecode_seeds/seed-06.bin new file mode 100644 index 0000000000..6fa3b4ed18 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-06.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-07.bin b/fuzz/fuzz_bytecode_seeds/seed-07.bin new file mode 100644 index 0000000000..5efc55089c Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-07.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-08.bin b/fuzz/fuzz_bytecode_seeds/seed-08.bin new file mode 100644 index 0000000000..ca8f308488 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-08.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-09.bin b/fuzz/fuzz_bytecode_seeds/seed-09.bin new file mode 100644 index 0000000000..81ac630dbe Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-09.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-10.bin b/fuzz/fuzz_bytecode_seeds/seed-10.bin new file mode 100644 index 0000000000..86df1d027b Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-10.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-11.bin b/fuzz/fuzz_bytecode_seeds/seed-11.bin new file mode 100644 index 0000000000..7af0b107a1 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-11.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-12.bin b/fuzz/fuzz_bytecode_seeds/seed-12.bin new file mode 100644 index 0000000000..5c5a841c46 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-12.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-13.bin b/fuzz/fuzz_bytecode_seeds/seed-13.bin new file mode 100644 index 0000000000..a796bd9c38 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-13.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-14.bin b/fuzz/fuzz_bytecode_seeds/seed-14.bin new file mode 100644 index 0000000000..1c1945286d Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-14.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-15.bin b/fuzz/fuzz_bytecode_seeds/seed-15.bin new file mode 100644 index 0000000000..86e5aa1d2a Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-15.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-16.bin b/fuzz/fuzz_bytecode_seeds/seed-16.bin new file mode 100644 index 0000000000..cf3a42db10 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-16.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-17.bin b/fuzz/fuzz_bytecode_seeds/seed-17.bin new file mode 100644 index 0000000000..cf0f02cc93 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-17.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-18.bin b/fuzz/fuzz_bytecode_seeds/seed-18.bin new file mode 100644 index 0000000000..3062ff1215 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-18.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-19.bin b/fuzz/fuzz_bytecode_seeds/seed-19.bin new file mode 100644 index 0000000000..f1880b8563 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-19.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-20.bin b/fuzz/fuzz_bytecode_seeds/seed-20.bin new file mode 100644 index 0000000000..2529dcd719 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-20.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-21.bin b/fuzz/fuzz_bytecode_seeds/seed-21.bin new file mode 100644 index 0000000000..e9427e1402 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-21.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-22.bin b/fuzz/fuzz_bytecode_seeds/seed-22.bin new file mode 100644 index 0000000000..235683b37b Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-22.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-23.bin b/fuzz/fuzz_bytecode_seeds/seed-23.bin new file mode 100644 index 0000000000..2733ab8cc1 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-23.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-24.bin b/fuzz/fuzz_bytecode_seeds/seed-24.bin new file mode 100644 index 0000000000..a8ba4283f0 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-24.bin differ diff --git a/fuzz/fuzz_bytecode_seeds/seed-25.bin b/fuzz/fuzz_bytecode_seeds/seed-25.bin new file mode 100644 index 0000000000..1ba30baf84 Binary files /dev/null and b/fuzz/fuzz_bytecode_seeds/seed-25.bin differ