Blob deserialize: reject over-long paths, treat size >= MAX_SIZE as unknown, make slice() non-panicking
Closed
JA
19d ago+86+86+01234567890123456789-15-15-01234567890123456789
What
Structured-clone / node:v8 deserialization of a Blob record accepted untrusted fields that later crash:
- a file-backed Blob with
size >= 2^63deserialized fine and thenblob.slice(0, 10)panicked inBlob::get_sliceoni64::try_from(size).expect(...)(panic: int cast: TryFromIntError(PosOverflow), exit 134 on release); - a file-backed Blob whose path is
>= 4096bytes deserialized and then.stream()hit the debugpanic!("path too long")inresolve_path::z(release: ENOENT).
The decoder now rejects a Path record with path_len >= MAX_PATH_BYTES (same bound the JS entry enforces) and treats a v4 size >= MAX_SIZE as the unknown-size sentinel; get_slice computes the i64 size once with unwrap_or(i64::MAX) and uses saturating adds, so no size value can panic.
Repro (before)
const v8 = require("node:v8");
const u32 = n => { const b = Buffer.alloc(4); b.writeUInt32LE(n); return b; }, u64 = n => { const b = Buffer.alloc(8); b.writeBigUInt64LE(BigInt(n)); return b; };
const P = Buffer.from("/nonexistent");
const bytes = Buffer.concat([Buffer.from([0x0e,0,0,0, 0xfe, 4]), u64(0), u32(0), Buffer.from([0, 0]), u64(1n << 63n), Buffer.from([1]), u32(P.length), P, Buffer.from([0]), Buffer.alloc(8)]);
v8.deserialize(bytes).slice(0, 10); // panic: int cast → "Bun has crashed", exit 134
Tests
test/js/web/structured-clone-blob-file.test.ts › "deserialize of crafted payloads" — size-past-MAX_SIZE slices as unknown size; path longer than PATH_MAX is rejected at deserialize. Both fail on the ASan canary and debug main; pass here.