Skip to content

Commit

Permalink
Fix crash in deserializer (quickjs-ng#602)
Browse files Browse the repository at this point in the history
Check inside the deserializer that const atoms are indeed const, don't
trust the input. The serializer only writes type 0 records for const
atoms but the byte stream may have been corrupted or manipulated.

Overlooked during review of c25aad7 ("Add ability to (de)serialize
symbols")

Found with libfuzzer and it found it _really_ fast. Great tool.
  • Loading branch information
bnoordhuis authored Oct 17, 2024
1 parent e4406fa commit a1d1bce
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ build/
unicode/
test262_*.txt
.idea
cmake-*
cmake-*
fuzz
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
23 changes: 23 additions & 0 deletions fuzz.c
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>

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;
}
4 changes: 4 additions & 0 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
55 changes: 55 additions & 0 deletions tests/test_bjson.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -221,6 +275,7 @@ function bjson_test_all()
bjson_test_map();
bjson_test_set();
bjson_test_symbol();
bjson_test_fuzz();
}

bjson_test_all();

0 comments on commit a1d1bce

Please sign in to comment.