Blob deserialize: reject over-long paths, treat size >= MAX_SIZE as unknown, make slice() non-panicking

Closed
JA
19d ago
+86-15

What

Structured-clone / node:v8 deserialization of a Blob record accepted untrusted fields that later crash:

  • a file-backed Blob with size >= 2^63 deserialized fine and then blob.slice(0, 10) panicked in Blob::get_slice on i64::try_from(size).expect(...) (panic: int cast: TryFromIntError(PosOverflow), exit 134 on release);
  • a file-backed Blob whose path is >= 4096 bytes deserialized and then .stream() hit the debug panic!("path too long") in resolve_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.