diff --git a/decimal.go b/decimal.go index 608a12e5..178e7966 100644 --- a/decimal.go +++ b/decimal.go @@ -78,6 +78,14 @@ var TrimTrailingZeros = true // digits are unknown. With this set to true, that number would be expressed as "1.2E3" instead. var UseScientificNotation = false +// ExpMaxLimit limits the maximum allowed exponent when parsing or operating on decimals. +// Extremely large exponents can cause memory leaks/OOM panics due to how math/big.Int allocates memory. +// By default, this is set to math.MaxInt32 for backwards compatibility, but it's strongly recommended +// to set this to a lower bound (e.g., 10000) for APIs that accept user input. +var ExpMaxLimit = int32(math.MaxInt32) + +// ExpMinLimit limits the minimum allowed exponent when parsing decimals. +var ExpMinLimit = int32(math.MinInt32) // ExpMaxIterations specifies the maximum number of iterations needed to calculate // precise natural exponent value using ExpHullAbrham method. var ExpMaxIterations = 1000 @@ -269,6 +277,10 @@ func NewFromString(value string) (Decimal, error) { return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", originalInput) } + if exp < int64(ExpMinLimit) || exp > int64(ExpMaxLimit) { + return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent out of bounds", originalInput) + } + return Decimal{ value: dValue, exp: int32(exp), diff --git a/decimal_test.go b/decimal_test.go index 161facf1..51d0ef0d 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -3970,3 +3970,33 @@ func ExampleNewFromFloat() { //0.123123123123123 //-10000000000000 } + +func TestNewFromStringExpLimit(t *testing.T) { + oldMax := ExpMaxLimit + oldMin := ExpMinLimit + defer func() { + ExpMaxLimit = oldMax + ExpMinLimit = oldMin + }() + + ExpMaxLimit = 10000 + ExpMinLimit = -10000 + + // Should pass + _, err := NewFromString("1e10000") + if err != nil { + t.Fatalf("expected 1e10000 to pass, got %v", err) + } + + // Should fail (exceeds max) + _, err = NewFromString("1e10001") + if err == nil { + t.Fatal("expected 1e10001 to fail") + } + + // Should fail (exceeds min) + _, err = NewFromString("1e-10001") + if err == nil { + t.Fatal("expected 1e-10001 to fail") + } +}