Skip to content

std: ReadTlvStream accepts a huge TLV-LENGTH, desyncing the stream or crashing the process #211

Open

Description

@Harsh23Kashyap

Problem

ReadTlvStream in std/utils/io/stream_read.go computes the frame size as typ.EncodingLength() + len.EncodingLength() + int(len). A TLV-LENGTH above math.MaxInt64 wraps negative in the int conversion. The framer then treats the wrapped value as a valid frame size: it delivers garbage bytes to the caller from the wrong offset and loses stream synchronization (observed: one crafted block turned into 34 bogus frames), and for lengths near 2^63 the size goes negative enough that slicing the buffer panics with an out-of-range error (verified: slice bounds out of range [:-9223372036854775795]).

Impact

ReadTlvStream is the receive loop for every stream transport: unicast TCP, unicast and multicast UDP, Unix stream faces, and the client-side StreamFace used by apps built on this library. A malformed or malicious peer on any of these can desync the face or crash the whole process.

Reproduction

Feed the framer a valid 3-byte frame followed by a block with type 0x64 and TLV-LENGTH 0xFFFFFFFFFFFFFFFF. Expected: the stream aborts with an error after the valid frame. Actual: dozens of garbage frames are delivered. With TLV-LENGTH 0x8000000000000000 the process panics.

Proposed fix

Reject any block whose declared length exceeds the receive buffer capacity (8 * MaxNDNPacketSize); such a block can never complete anyway, and the guard keeps the size computation out of the overflow range. PR includes a regression test that fails before the fix and passes after.

Activity

  1. Harsh23Kashyap commented on Sep 16, 2026

    @Harsh23Kashyap
    Author

    Repro evidence (Go 1.26.6, linux/amd64, ndnd main @ d51682b).

    Stream containing one valid 3-byte frame, then a block with type 0x64 and TLV-LENGTH 0xFFFFFFFFFFFFFFFF, run against unpatched main:

    $ go test ./std/utils/io/ -run TestReadTlvStreamRejectsHugeLength -v
    === RUN   TestReadTlvStreamRejectsHugeLength
        stream_read_test.go:27:
            	Error:      	An error is expected but got nil.
            	Messages:   	oversized TLV-LENGTH must abort the stream
        stream_read_test.go:28:
            	Error:      	Not equal:
            	            	expected: 1
            	            	actual  : 34
            	Messages:   	only the valid frame may be delivered
    --- FAIL: TestReadTlvStreamRejectsHugeLength (0.00s)
    FAIL
    

    The crafted block desyncs the framer into delivering 34 bogus frames. With TLV-LENGTH 0x8000000000000000 the same path panics the process:

    $ go test ./std/utils/io/ -run TestReadTlvStreamPanicOnHugeLength -v
    --- FAIL: TestReadTlvStreamPanicOnHugeLength (0.00s)
    panic: runtime error: slice bounds out of range [:-9223372036854775795] [recovered, repanicked]
    

    Against the fix branch (fix/tlv-stream-length-overflow):

    $ go test ./std/utils/io/ -run TestReadTlvStreamRejectsHugeLength -v
    === RUN   TestReadTlvStreamRejectsHugeLength
    --- PASS: TestReadTlvStreamRejectsHugeLength (0.00s)
    PASS
    ok  	github.com/named-data/ndnd/std/utils/io	0.003s
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions