Skip to content

AVRO-3569: [csharp] Fix infinite recursion in AvroDecimal IConvertible conversions - #3951

Open
iemejia wants to merge 1 commit into
apache:mainfrom
iemejia:AVRO-3569-csharp-avrodecimal-recursion
Open

AVRO-3569: [csharp] Fix infinite recursion in AvroDecimal IConvertible conversions#3951
iemejia wants to merge 1 commit into
apache:mainfrom
iemejia:AVRO-3569-csharp-avrodecimal-recursion

Conversation

@iemejia

@iemejia iemejia commented Aug 23, 2026

Copy link
Copy Markdown
Member

What

Every explicit IConvertible.ToXxx(IFormatProvider) implementation on AvroDecimal called Convert.ToXxx(this, provider). Since this is an IConvertible, Convert.ToXxx(IConvertible, provider) calls straight back into the same IConvertible.ToXxx(provider) method → unbounded recursion → StackOverflowException, which is uncatchable in .NET and crashes the process.

Reproducer (from the issue):

Convert.ToByte(new AvroDecimal(0)); // StackOverflowException

This affected ToBoolean, ToByte, ToDecimal, ToDouble, ToInt16/32/64, ToSByte, ToSingle, ToUInt16/32/64 and ToString.

Fix

IConvertible.ToType(Type, provider) is the only implementation that does real work (converts the unscaled/scaled value to a decimal and calls Convert.ChangeType). Route the 12 numeric/boolean conversions through it (preserving the IFormatProvider), and make ToString(provider) use the public ToString() override. No Convert.To*(this, provider) self-calls remain.

Tests

Adds TestAvroDecimalIConvertibleDoesNotRecurse exercising all conversions (each would previously overflow the stack). Full C# suite passes (1528 tests, 0 failures).

JIRA: https://issues.apache.org/jira/browse/AVRO-3569

…e conversions

Every explicit IConvertible.ToXxx(IFormatProvider) implementation on
AvroDecimal called Convert.ToXxx(this, provider). Because 'this' is an
IConvertible, Convert.ToXxx(IConvertible, provider) calls back into the same
IConvertible.ToXxx(provider) method, recursing until a StackOverflowException
(which is uncatchable and crashes the process). Reproducer:
Convert.ToByte(new AvroDecimal(0)).

Route the 12 numeric/boolean conversions through IConvertible.ToType(type,
provider) (the one method that performs the real conversion via
Convert.ChangeType on the underlying decimal), and make ToString(provider)
use the public ToString() override. Adds a test exercising all conversions.
@github-actions github-actions Bot added the C# label Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant