Skip to content

Creating a map for a type with a circular reference causes a stack overflow #2316

Open
@Gerfatz

Description

@Gerfatz

Describe the bug
When CsvHelper tries to auto map a type that references itself, it can run into a stack overflow. This happens only under certail conditions. The self referential property needs to be declared after another property that requires mapping.

To Reproduce

using System.Globalization;
using CsvHelper;

public class MyTypedId
{
	public Guid Value { get; set; }
}

public sealed class MyClass
{
	public MyTypedId Id { get; set; }
	public MyClass Parent { get; set; }
}

public class Program
{
	public static void Main(string[] args)
	{
		var buffer = new BufferedStream(new MemoryStream());
		var csvWriter = new CsvWriter(new StreamWriter(buffer), CultureInfo.InvariantCulture);
		var context = new CsvContext(csvWriter);
		var type = typeof(MyClass);
		context.AutoMap(type);
	}
}

Changing MyClass in the above example to either of the following does not produce a stack overflow:

public sealed class MyClass
{
	public MyClass Parent { get; set; }
	public MyTypedId Id { get; set; }
}
public sealed class MyClass
{
	public Guid Id { get; set; }
	public MyClass Parent { get; set; }
}

Expected behavior
Creating a class map should not result in a stack overflow. It should detect the recursion an stop, or throw an exception.

Screenshots
If applicable, add screenshots to help explain your problem.

Additional context
I already started looking into what could be the causes of this. ClassMap.cs line 403 looks like the likely culprit to me. During mapping of the example provided, mapParents contains two entries for MyClass. One for the type being mapped, the other for the property. When the Id property gets evaluated, mapParents.Find(type) returns the first node. mapParents.Drop then drops both entries, though in this case it should only drop the second node. This explains why no stack overflow occurs when the proprties in MyClass are flipped.

Either changing Find to FindLast or keeping a reference to the node from when it was added fixes the issue. I can however not tell wheter this has implications on some other use case. If i get the time during the weekend I would fix this myself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions