Skip to content

_from_bytes() doesn't validate binary input — invalid bytes accepted silently #110

Description

@sumanjeet0012

Multiaddr._from_bytes() stores raw bytes without any validation. Invalid binary multiaddrs are accepted silently and only fail later when __str__() or other methods try to parse them. This makes debugging difficult and violates the principle of fail-fast.

Problem

In multiaddr/multiaddr.py:

def _from_bytes(self, addr: bytes) -> None:
    if not addr:
        self._bytes = b""
        return
    self._bytes = addr    # ← No validation at all!

This means:

# These all succeed without error:
Multiaddr(b'\xff\xff\xff')           # Invalid varint
Multiaddr(b'\x99\x01\x00')          # Unknown protocol code 0x99
Multiaddr(b'\x04\x04\x01\x02')      # ip4 with only 3 bytes of address (needs 4)

# They only fail later:
str(Multiaddr(b'\xff\xff\xff'))      # BinaryParseError
str(Multiaddr(b'\x99\x01\x00'))      # BinaryParseError: Unknown Protocol

Go's NewMultiaddrBytes() validates immediately:

func NewMultiaddrBytes(b []byte) (a Multiaddr, err error) {
    bytesRead, m, err := readMultiaddr(b)
    if err != nil { return nil, err }
    if bytesRead != len(b) {
        return nil, fmt.Errorf("unexpected extra data. %v bytes leftover", len(b)-bytesRead)
    }
    return m, nil
}

Proposed Solution

Add validation in _from_bytes():

def _from_bytes(self, addr: bytes) -> None:
    if not addr:
        self._bytes = b""
        return

    # Validate by iterating all components
    consumed = 0
    for offset, proto, codec, part_value in bytes_iter(addr):
        consumed = offset + len(proto.vcode)
        if codec.SIZE < 0:
            consumed += len(varint.encode(len(part_value)))
        consumed += len(part_value)

    if consumed != len(addr):
        raise exceptions.BinaryParseError(
            f"unexpected extra data: {len(addr) - consumed} bytes leftover",
            addr,
            0,
        )

    self._bytes = addr

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions