Skip to content

minecraft/nbt: Bound array and list allocations by the input - #528

Open
HashimTheArab wants to merge 3 commits into
Sandertv:masterfrom
HashimTheArab:fix/nbt-network-lengths
Open

HashimTheArab wants to merge 3 commits into
Sandertv:masterfrom
HashimTheArab:fix/nbt-network-lengths

Conversation

@HashimTheArab

Copy link
Copy Markdown
Contributor

#516 bounded the fixed-width Int32Slice/Int64Slice of the little- and big-endian encodings, but the paths packets actually use were still open:

  • networkLittleEndian.Int32Slice / Int64Slice allocated make([]intN, n) from the declared count before reading an element. Ten bytes (0a 00 0b 00 fe ff ff ff 0f 00) allocate 8 GiB; the long-array variant 16 GiB. Each attempt takes about 2 ms and then fails with BufferOverrun, so a peer can drive a process into OOM or a GC spiral with a handful of block-actor NBT payloads.
  • TAG_List of any element type did reflect.MakeSlice(len, len) with the declared length, in every encoding.
  • TAG_Byte_Array and TAG_List of TAG_Byte checked Len() only when the reader had one, then make([]byte, length).

Variable-width arrays and lists now go through offsetReader.sliceCap: with a reader that reports its length, a count that cannot fit in the remaining bytes (at least one byte per element) is a BufferOverrunError; otherwise the slice starts with a small capacity and grows with the elements actually read. Byte arrays and byte lists use readArrayBytes like the fixed-width arrays.

Checked locally (not committed): the six hostile shapes above (int/long array, byte array, byte list, string list, TAG_End list) allocate 0 MiB whether or not the reader exposes Len(), and a struct with []int32, []int64, []string, [][]int32, [4]byte and []struct fields round-trips unchanged through all three encodings, including empty non-nil slices.

The network encoding's Int32Slice and Int64Slice, TAG_Byte_Array, TAG_List
of TAG_Byte and every other TAG_List still allocated the full declared
length before reading a single element. Ten bytes of network NBT declaring
an int array of 2^31-2 elements allocated 8 GiB (16 GiB for a long array),
and a list of any type allocated a slice of that many elements.

Variable-width arrays and lists now reject a count that cannot fit in the
bytes remaining when the reader reports them, and otherwise start with a
small capacity and grow with the elements actually read. Byte arrays and
byte lists go through readArrayBytes like the fixed-width arrays do.
reflect.Append allocated a new slice header for every element, which
made decoding a valid 10k-element list about four times slower than
before the bound. The slice is now grown in place and decoded into by
index; with a known input length it is allocated once, as before.
HashimTheArab added a commit to HashimTheArab/gophertunnel that referenced this pull request Sep 26, 2026
…) into lunar

Brings in the 13 new upstream commits together with the fork's two
open upstream fix PRs in one commit, so the diff shows the net result:
Sandertv#527 reverts the deferred-packet re-check that master added, so that
churn cancels out here.

New upstream behaviour folded into the fork's reworked login stack:
- ListenConfig.LoginTimeout and the authenticated flag + closeTransport
  it relies on.
- ListenerGroup shared player counts across listeners.
- GameType on ServerStatus and the pong game-type field.
- ClientBoundDebugRenderer encoding/build fixes.

Kept the fork's versions where it deliberately diverges: the reworked
decoder (buffer pooling, per-batch algorithm, 1600-packet cap), the
login state machine and StartGame flow, the realms client (version
negotiation; already carries Stories settings), and dial (no pong-port
following, so DisablePortFollowing/EnableLegacyAuth are not added).
nbt allocation bounding is already present from an earlier fork change,
so Sandertv#528 is a no-op here apart from keeping checkRemaining, which the
fork's raw-message decoder still uses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant