You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
HexEncodingProvider.TryEncode throws after partly writing the destination for inputs ≥ 1 GiB, and GetMaxEncodedLength returns a negative bound #150
L33: if (destination.Length < data.Length * 2) return false;
L44: bytesWritten = data.Length * 2;
All three are unchecked int arithmetic, and the repo doesn't enable CheckForOverflowUnderflow. For any input of 1,073,741,824 bytes or more, data.Length * 2 wraps to a negative number. Two things follow.
The size guard never rejects.destination.Length < negative is false, so a destination that is too small is accepted. The loop at L38-42 writes into it until the span bounds check throws IndexOutOfRangeException. The caller gets an exception from a Try method instead of false, and the destination has already been partly overwritten. HexObfuscationProvider.TryObfuscate delegates here, so it behaves the same way.
The bound is negative. For example, GetMaxEncodedLength(int.MaxValue) returns -2.
IEncodingProvider.Encode(ReadOnlySpan<byte>) (Essentials/IEncodingProvider.cs ~L65) passes that value to ArrayPool<byte>.Shared.Rent, which throws an ArgumentOutOfRangeException about pool sizes rather than about the input.
Callers that size their own buffer from the bound get a negative size.
The other providers already handle this:
Base64EncodingProvider uses Base64.GetMaxEncodedToUtf8Length, which throws when the bound wouldn't fit, and returns false for a short destination.
GetMaxEncodedLength throws ArgumentOutOfRangeException when 2L * sourceLength > int.MaxValue.
TryEncode returns false when 2L * data.Length > destination.Length.
Acceptance criteria
GetMaxEncodedLength(int.MaxValue) throws ArgumentOutOfRangeException instead of returning a negative value.
TryEncode never throws for a destination that is too small, and leaves bytesWritten == 0. The guard can be tested without allocating 1 GiB, for example through GetMaxEncodedLength or an internal helper.
HexObfuscationProvider.GetMaxObfuscatedLength and TryObfuscate get the same behaviour through delegation.
Priority: Low. It needs inputs of 1 GiB or more, and the result is an exception (not silent corruption), but a Try method should never throw and GetMaxEncodedLength should never go negative.
Area / suggested assignee:Essentials.EncodingProviders.Hex/HexEncodingProvider.cs, and HexObfuscationProvider through delegation. Owner: @matt-edmondson
Next step: compute the bound in long, throw ArgumentOutOfRangeException from GetMaxEncodedLength when it doesn't fit, and return false from TryEncode, testing the guard without allocating 1 GiB.
Activity
matt-edmondson commentedon Oct 19, 2026
Triage
Trymethod should never throw andGetMaxEncodedLengthshould never go negative.Essentials.EncodingProviders.Hex/HexEncodingProvider.cs, andHexObfuscationProviderthrough delegation. Owner: @matt-edmondsonDeflateBound). Open PR Encode correctly when the Hex or Base64 source overlaps the destination [patch] #148 ("Encode correctly when the Hex or Base64 source overlaps the destination") edits the same file but a different bug; land this after it, or fold it in, to avoid a conflict.long, throwArgumentOutOfRangeExceptionfromGetMaxEncodedLengthwhen it doesn't fit, and returnfalsefromTryEncode, testing the guard without allocating 1 GiB.Generated by Claude Code