Description
Description
This bug isn't really specific to XamlDirective but it's the class where I found the issue and it's the easiest to cause an issue. You can have the bug when using instances of XamlMember or of any of its descendants (In this case XamlDirective).
The bug is that XamlMember._reflector can be wrongly initialized when hitting this point from 3 threads and with very specific timing:
Here's the code for MemberReflector.UnknownReflector:
Here's the required timing of those 3 threads to have an invalid MemberReflector returned from MemberReflector.UnknownReflector:
Thread 1 and 2 invoke MemberReflector.UnknownReflector and find that s_UnknownReflector is null. Thread 1 assigns s_UnknownReflector to a new instance. Thread 3 invokes MemberReflector.UnknownReflector, finds that s_UnknownReflector is not null and returns s_UnknownReflector. Thread 2 assigns s_UnknownReflector to a new instance.
In this scenario thread 1 and 2 both return the same instance from MemberReflector.UnknownReflector while thread 3 returned an old instance from MemberReflector.UnknownReflector that wasn't fully initialized.
Now that I've explained how to have a partially initialized MemberReflector, here's how to have a stack overflow exception using XamlDirective:
We enter XamlDirective.Type when _reflector.Type is null which means that we enter this if block:
wpf/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlMember.cs
Lines 251 to 254 in b08d70d
In the if block it calls the virtual method XamlDirective.LookupType() which is implemented in XamlDirective to return base.Type:
We then have an infinite loop of Type -> LookupType() -> Type -> LookupType() -> etc.
Reproduction Steps
[Theory]
[InlineData("xamlNamespace", "name")]
public void Parallel_Ctor_String_String(string xamlNamespace, string? name)
{
Parallel.For(0, 1000, i =>
{
var directive = new XamlDirective(xamlNamespace, name);
Assert.Equal(XamlLanguage.Object, directive.Type);
});
}
Run the test a couple of times and it should crash the process with a stack overflow exception.
Expected behavior
The process shouldn't crash with a stack overflow exception.
Actual behavior
The process crashes with a stack overflow exception.
Regression?
No.
Known Workarounds
None.
Impact
Can cause the process to crash or weird bugs at runtime but the chance of getting this issue in practice is extremely low. As explained in the description, it requires 3 threads with very specific timing so I think it would be very hard to hit in practice.
Configuration
.Net 9.0
Windows 11
x64
Not specific to that configuration.
Other information
No response