diff --git a/.gitignore b/.gitignore index fbeb98f..4e44ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ build/ unicode/ test262_*.txt .idea -cmake-* \ No newline at end of file +cmake-* +fuzz diff --git a/Makefile b/Makefile index a5a5734..684fe17 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,10 @@ endif all: $(QJS) +fuzz: + clang -g -O1 -fsanitize=fuzzer -o fuzz fuzz.c + ./fuzz + $(BUILD_DIR): cmake -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) @@ -106,4 +110,4 @@ unicode_gen: $(BUILD_DIR) libunicode-table.h: unicode_gen $(BUILD_DIR)/unicode_gen unicode $@ -.PHONY: all debug install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen $(QJS) $(QJSC) +.PHONY: all debug fuzz install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen $(QJS) $(QJSC) diff --git a/fuzz.c b/fuzz.c new file mode 100644 index 0000000..cb5381d --- /dev/null +++ b/fuzz.c @@ -0,0 +1,23 @@ +// clang -g -O1 -fsanitize=fuzzer -o fuzz fuzz.c +#include "quickjs.h" +#include "quickjs.c" +#include "cutils.c" +#include "libbf.c" +#include "libregexp.c" +#include "libunicode.c" +#include + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + JSRuntime *rt = JS_NewRuntime(); + if (!rt) + exit(1); + JSContext *ctx = JS_NewContext(rt); + if (!ctx) + exit(1); + JSValueConst val = JS_ReadObject(ctx, buf, len, /*flags*/0); + JS_FreeValue(ctx, val); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 0; +} diff --git a/quickjs.c b/quickjs.c index 3642aea..90e9729 100644 --- a/quickjs.c +++ b/quickjs.c @@ -35587,6 +35587,10 @@ static int JS_ReadObjectAtoms(BCReaderState *s) if (type == 0) { if (bc_get_u32(s, &atom)) return -1; + if (!__JS_AtomIsConst(atom)) { + JS_ThrowInternalError(s->ctx, "out of range atom"); + return -1; + } } else { if (type < JS_ATOM_TYPE_STRING || type >= JS_ATOM_TYPE_PRIVATE) { JS_ThrowInternalError(s->ctx, "invalid symbol type %d", type); diff --git a/tests/test_bjson.js b/tests/test_bjson.js index 0a92162..c1f72bb 100644 --- a/tests/test_bjson.js +++ b/tests/test_bjson.js @@ -1,6 +1,45 @@ import * as bjson from "bjson"; import { assert } from "./assert.js"; +function base64decode(s) { + var A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var n = s.indexOf("="); + if (n < 0) n = s.length; + if (n & 3 === 1) throw Error("bad base64"); // too much padding + var r = new Uint8Array(3 * (n>>2) + (n>>1 & 1) + (n & 1)); + var a, b, c, d, i, j; + a = b = c = d = i = j = 0; + while (i+3 < n) { + a = A.indexOf(s[i++]); + b = A.indexOf(s[i++]); + c = A.indexOf(s[i++]); + d = A.indexOf(s[i++]); + if (~63 & (a|b|c|d)) throw Error("bad base64"); + r[j++] = a<<2 | b>>4; + r[j++] = 255 & b<<4 | c>>2; + r[j++] = 255 & c<<6 | d; + } + switch (n & 3) { + case 2: + a = A.indexOf(s[i++]); + b = A.indexOf(s[i++]); + if (~63 & (a|b)) throw Error("bad base64"); + if (b & 15) throw Error("bad base64"); + r[j++] = a<<2 | b>>4; + break; + case 3: + a = A.indexOf(s[i++]); + b = A.indexOf(s[i++]); + c = A.indexOf(s[i++]); + if (~63 & (a|b|c)) throw Error("bad base64"); + if (c & 3) throw Error("bad base64"); + r[j++] = a<<2 | b>>4; + r[j++] = 255 & b<<4 | c>>2; + break; + } + return r.buffer; +} + function toHex(a) { var i, s = "", tab, v; @@ -188,6 +227,21 @@ function bjson_test_symbol() assert(o, r); } +function bjson_test_fuzz() +{ + var corpus = [ + "EBAAAAAABGA=", + ]; + for (var input of corpus) { + var buf = base64decode(input); + try { + bjson.read(buf, 0, buf.byteLength); + } catch (e) { + // okay, ignore + } + } +} + function bjson_test_all() { var obj; @@ -221,6 +275,7 @@ function bjson_test_all() bjson_test_map(); bjson_test_set(); bjson_test_symbol(); + bjson_test_fuzz(); } bjson_test_all();