Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/encoding.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -869,3 +869,46 @@ export const testTerminatedEncodering = _tc => {
t.compare(readBuf1, buf1)
t.compare(readBuf2, buf2)
}

/**
* A length prefix may not reach beyond the bytes the decoder was given.
*
* Before the bound, `readUint8Array` sliced `decoder.arr.buffer` -- the whole
* underlying ArrayBuffer -- so an over-declared length returned memory outside
* the message. Every small Node Buffer is a view into a shared 64 KiB slab, so
* in a server decoding frames from untrusted peers those bytes belonged to
* other connections.
*
* Note for anyone editing this test: the backing buffer must be large enough
* that the unbounded read would *succeed*, and filled with valid UTF-8, or the
* Uint8Array constructor / the fatal TextDecoder throws and the test passes for
* the wrong reason.
*
* @param {t.TestCase} _tc
*/
export const testReadUint8ArrayDoesNotReadPastTheView = _tc => {
const backing = new Uint8Array(1024)
backing.fill(0x41)
// a 4-byte "message" carrying 2 bytes of payload, as a view inside `backing`
const message = new Uint8Array(backing.buffer, 8, 4)
message[0] = 0x80 // varUint 512
message[1] = 0x04
message[2] = 0x41
message[3] = 0x41

t.fails(() => { decoding.readVarUint8Array(decoding.createDecoder(message)) })
t.fails(() => { decoding.readVarString(decoding.createDecoder(message)) })
t.fails(() => { decoding.readUint8Array(decoding.createDecoder(message), 5) })
t.fails(() => { decoding.readUint8Array(decoding.createDecoder(message), -1) })

// exact-fit and short reads still work
const decoder = decoding.createDecoder(message)
const exact = decoding.readUint8Array(decoder, 4)
t.assert(exact.length === 4)
t.assert(exact[0] === 0x80 && exact[3] === 0x41)

// readTailAsUint8Array passes exactly the remaining length, so it is unaffected
const decoder2 = decoding.createDecoder(message)
decoding.readUint8(decoder2)
t.assert(decoding.readTailAsUint8Array(decoder2).length === 3)
}
Loading