Conversation
RFC 6733 section 3 defines Message Length as the length of the message including the 20 byte header, so a smaller value describes no message that can exist. DecodeFromBytes accepted it anyway, and readBody then computed int(Header.MessageLength - HeaderLength) in unsigned arithmetic: for a declared length of 0 that is uint32(0)-20, or 4294967276, which readerBufferSlice hands straight to make(). Twenty bytes on the wire therefore allocate very nearly 4 GiB, measured at 4294970504 bytes and 12.5s on this commit's parent. The only gate before readBody is FindCommand, which merely requires the command and application to exist in the dictionary, and CER — command code 257, application 0, the first message of any Diameter connection — satisfies that on any dictionary. So the allocation is reachable from any peer that can open a connection, before capabilities are exchanged and before any handler runs. An out-of-memory is a fatal runtime error rather than a panic, so the recover in conn.serve cannot contain it, and under a container memory limit the process dies. Rejected in Header.DecodeFromBytes, with a second check in readBody to cover a Header that was built by hand rather than decoded from the wire. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Header.DecodeFromBytesaccepts a Message Length below the 20 byte header, andreadBodythen computesint(m.Header.MessageLength - HeaderLength)in unsigned arithmetic. For a declared length of 0 that isuint32(0) - 20, or4294967276, whichreaderBufferSlicehands straight tomake().Twenty bytes on the wire allocate very nearly 4 GiB.
Reproducing on
main(ad16c7e)Under a 2 GiB address-space cap:
Uncapped it completes, having allocated 4294970504 bytes in 12.5 s.
Why it is reachable before authentication
The only gate before
readBodyisFindCommand, which merely requires the command and application to exist in the dictionary. CER — command code 257, application 0 — is the first message of any Diameter connection and satisfies that on any dictionary. So the allocation is reachable from any peer that can open a connection, before capabilities are exchanged and before any handler runs.recover()does not help: an out-of-memory is a fatal runtime error rather than a panic, so therecoverinconn.servecannot contain it. For a server with a container memory limit, one unauthenticated 20 byte write ends the process.The fix
RFC 6733 section 3 defines Message Length as the length of the message including the header, so a value below
HeaderLengthdescribes no message that can exist. Rejected inHeader.DecodeFromBytes, with a second check inreadBodycovering aHeaderbuilt by hand rather than decoded from the wire.Two tests added; both fail without the patch. The boundary is inclusive — a bodyless message is exactly
HeaderLengthand is still accepted.Found with
go test -fuzz.go test ./...passes on all packages.Independent of #261 (nil
Datadereference) — that one touches a different region ofmessage.go, and the two branches auto-merge. Either can go first.