Skip to content

Three memory-safety defects in split-packet reassembly (remote): OOB write, unbounded allocation, heap overflow #82

Description

@mojibake-dev

Filed here because the security-report address in SLikeNet's readme (support@slikesoft.com, section 6.2) is
no longer deliverable: mail to it bounces. Proof-of-concept harnesses and full AddressSanitizer traces are
withheld from this public issue and are available to the maintainer privately on request.

SLikeNet's split-packet reassembly (Source/src/ReliabilityLayer.cpp, the SplitPacketSort path, which is
the default because PREALLOCATE_LARGE_MESSAGES is 0) carries three RakNet-lineage memory-safety defects.
All three were reproduced against master (d5f775d) built Release with AddressSanitizer; the reassembly
core is unchanged since 2018-06-22.

1. Out-of-bounds write from inconsistent fragments. SplitPacketSort::Add indexes m_data with the
wire splitPacketIndex; the only check against the allocated size is RakAssert(splitPacketIndex < m_allocationSize)
(:299), which defines.h:116 makes empty in release. The array is sized from the first fragment of a
channel (Preallocate, :259-260, looked up by splitPacketId alone at :3114), and a later fragment with
the same id is not checked for a matching count before Add (:3212). The per-packet check at :2835 only
compares a fragment's own index against its own count, so a second fragment declaring a large count and a
large index passes it and writes outside the array. Add reads the slot (:303) before writing it (:304),
so it is an OOB read then an OOB write of a heap pointer.

Reproduced: ASan heap-buffer-overflow at ReliabilityLayer.cpp:303, array from :260.

2. Unbounded allocation from a wire count. SplitPacketSort::Preallocate (:256-266) sizes an
allocation directly from a single fragment's splitPacketCount with no ceiling, and passes it through an
(int) cast: m_data = OP_NEW_ARRAY<InternalPacket*>((int)m_allocationSize, ...) (:260). One fragment with
splitPacketCount = 0x7FFFFFFF requests roughly 17 GB; one with 0x80000000 becomes negative at the cast
and the size computation wraps.

Reproduced: 17 GB requested from one packet; 0x80000000 gives ASan allocation-size-too-big at :260.

3. Heap overflow from a bit-to-byte rounding mismatch. BuildPacketFromSplitPacketList (:3260) sizes
the reassembly buffer by rounding the summed dataBitLength up to bytes once (:3279), then in the copy
loop rounds each fragment up independently (:3286) with the destination taken from a bit-accumulated
offset. Two 12-bit fragments give a 3-byte allocation but 4 bytes written: a one-byte overflow with an
attacker byte.

Reproduced: ASan heap-buffer-overflow WRITE at ReliabilityLayer.cpp:3286, buffer from :3279.

Reachability. These sit on the split-packet reassembly path in the RakNet-lineage receive code, below
application-level authentication (the same position as in the CrabNet sibling). A connected peer that has
completed the plaintext offline handshake reaches them.

Suggested fixes

  1. In SplitPacketSort::Add, bound splitPacketIndex against m_allocationSize with a real check that
    releases the packet, not a RakAssert; and in InsertIntoSplitPacketList, reject a later fragment whose
    splitPacketCount disagrees with the size the channel was allocated at.
  2. Cap splitPacketCount against an MTU-derived bound before Preallocate, and remove the (int) cast (or
    validate before it) so a large count cannot become negative.
  3. In BuildPacketFromSplitPacketList, compute the copy offset and each fragment's length from the same
    rounding, or reject any non-final fragment whose dataBitLength is not a multiple of eight.

Systemically, RakAssert should not be the sole guard on any attacker-controlled index on the parse path,
and should fail closed there in release rather than compiling away.

Prior art and scope. The same class is open upstream since 2020 (RakNet issue facebookarchive#143,
facebookarchive#143) and covered for a sibling implementation by RakLib
advisory GHSA-v8hj-23c3-8w33 (GHSA-v8hj-23c3-8w33). The
CrabNet fork carries the same three defects and is reported separately. These are RakNet-lineage issues
inherited from common ancestry, not novel.

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