Repository navigation
Closed
Labels
area/jsonSystem.Text.Json and TypeConverter wire formsSystem.Text.Json and TypeConverter wire formsarea/literalsParse, TryParse and formattingParse, TryParse and formattingarea/rangesRange unions, variants and the shape enginesRange unions, variants and the shape enginesarea/value-setsValue set families and wrapper aritiesValue set families and wrapper aritiesbugSomething isn't workingSomething isn't workingpackage/coreAffects CodoMetis.ValueRangesAffects CodoMetis.ValueRangesseverity/criticalData loss, corruption, or silently wrong results in common useData loss, corruption, or silently wrong results in common use
Milestone
Description
On main (1353260, unreleased), DecimalLiteral.Represents (src/CodoMetis.ValueRanges/Internals/DecimalLiteral.cs, from #166 for #115) sizes its work by the supplied text instead of by what decimal can hold:
- The digit buffer is stack-allocated at the text's length (line 59). A long element that
decimal.Parseaccepts exhausts the stack, which no handler can catch, so it reachesTryParseand JSON too. Same class as A deeply nested value set literal overflows the stack and kills the process #248. - The powers of ten (lines 71 and 80) take their exponent from the text, so a short exponent literal costs work that grows superlinearly with the exponent.
- The exponent is read with
int.Parse(line 53), which throwsOverflowExceptionwhereParsedocumentsFormatException.
Every entry point is affected: DecimalRange, RangeSet<DecimalRange>, DecimalSet, DecimalSet<T>, in a literal, JSON, TypeConverter and the EF Core JSON document.
Fix: normalize before any arithmetic (strip leading and trailing zeros, adjust the spelled scale) and refuse when the significant digits or the scale exceed what decimal can hold, so the buffer and the powers are bounded by a constant; read the exponent with TryParse. Review the sibling readers (FloatLiteral, IntegerLiteral, UuidLiteral, IsoTemporal) for the same pattern.
Metadata
Metadata
Assignees
Labels
area/jsonSystem.Text.Json and TypeConverter wire formsSystem.Text.Json and TypeConverter wire formsarea/literalsParse, TryParse and formattingParse, TryParse and formattingarea/rangesRange unions, variants and the shape enginesRange unions, variants and the shape enginesarea/value-setsValue set families and wrapper aritiesValue set families and wrapper aritiesbugSomething isn't workingSomething isn't workingpackage/coreAffects CodoMetis.ValueRangesAffects CodoMetis.ValueRangesseverity/criticalData loss, corruption, or silently wrong results in common useData loss, corruption, or silently wrong results in common use
Activity
CaffeinatedCoder commentedon Oct 19, 2026
Another symptom (reported by the user):
{0e-10000000}ran for more than five seconds before the process was killed.BigInteger.Powexpanded the exponent even though the value is zero. Measured onmain1353260 throughDecimalSet.TryParseon a 1 MB thread:0e-100000001e-10000000e99999999999OverflowExceptionfromParse1e-99999999999OverflowExceptionfromParseFormatException0.+ 1M zeros +11.+ 2M zerosFix on
fix/276-decimal-literal-bounds:Representsreduces both sides to a significand with no trailing zero times a power of ten, then compares two integers. It counts significant digits before keeping any and refuses more than 29, the most a decimal mantissa can have. It refuses an exponent past ±28. That leaves one pass over the text plus a constant, with no stack buffer and noBigInteger. The exponent is read withTryParse. A differential run of the old and new check over 2.5 million random texts thatdecimal.Parseaccepts found 0 mismatches.Sibling readers: none has the pattern.
FloatLiteralusesBigInteger.Powonly inFormat, and there the exponent comes from the value's binary exponent (at most about 1,100), not from the text. Its parse side isdouble.Parseplus linear scans.IntegerLiteralstops at the first overflow,UuidLiteraluses a fixed 16-byte buffer, andIsoTemporalparses fixed two-digit slices.