Description
Description
Crash originates from emitted ir for a write barrier for a memcpy essentially. It appears we are providing an offset from null to the copy function resulting in mono's signal handling not recognizing the crash as a NullReferenceException but rather just a fatal invalid memory access.
Also filed a bug in mono/mono: mono/mono#21623
Reproduction Steps
Build & Run the following:
using System.Runtime.CompilerServices;
public class Program
{
public Foo currentFoo;
public Program()
{
Bacon defaultBacon = new Bacon(-180, 180, true, false, 300f, 0.1f, 0.1f, "Foo", false);
currentFoo = new Foo();
currentFoo.GetBar().m_Bacon = defaultBacon;
Console.WriteLine("Foo");
}
static void Main(string[] args)
{
new Program();
}
}
public class Foo
{
private Bar m_Bar;
public Bar GetBar()
{
return m_Bar;
}
}
public class Bar
{
public Bacon m_Bacon = new Bacon(-180, 180, true, false, 300f, 0.1f, 0.1f, "Foo", false);
}
public struct Bacon
{
public float Value;
public enum FooEnum
{
One,
Two
};
public FooEnum m_FooEnum;
public float m_f1;
public float m_f2;
public float m_f3;
public string m_s1;
public float m_f8;
public bool m_bool1;
public float m_f4;
public float m_f5;
public bool m_bool2;
public FooBar m_FooBar;
float m_f6;
float m_f7;
int m_i1;
public bool bool3 { get; set; }
public bool bool4 { get; set; }
public interface IFooInterface
{
float GetFooValue(int foo);
}
IFooInterface m_FooProvider;
int m_i2;
public Bacon(
float minValue, float maxValue, bool wrap, bool rangeLocked,
float maxSpeed, float accelTime, float decelTime,
string name, bool invert)
{
m_f4 = minValue;
m_f5 = maxValue;
m_bool2 = wrap;
bool3 = rangeLocked;
bool4 = false;
m_FooBar = new FooBar(false, 1, 2);
m_FooEnum = FooEnum.One;
m_f1 = maxSpeed;
m_f2 = accelTime;
m_f3 = decelTime;
Value = (minValue + maxValue) / 2;
m_s1 = name;
m_f8 = 0;
m_bool1 = invert;
m_f6 = 0f;
m_FooProvider = null;
m_i2 = 0;
m_f7 = 0;
m_i1 = 0;
}
public struct FooBar
{
public bool m_FooBar_bool1;
public float m_FooBar_f1;
public float m_FooBar_f2;
float m_FooBar_f3;
float m_FooBar_f4;
float m_FooBar_f5;
int m_FooBar_i1;
int m_FooBar_i2;
public FooBar(bool b1, float f1, float f2)
{
m_FooBar_bool1 = b1;
m_FooBar_f1 = f1;
m_FooBar_f2 = f2;
m_FooBar_f4 = 0;
m_FooBar_f5 = 0;
m_FooBar_i1 = m_FooBar_i2 = -1;
m_FooBar_f3 = 0;
}
}
}
Expected behavior
A C# NullReferenceException is thrown.
Actual behavior
Fatal crash. For downstream mono the crash callstack looks like:
clr.dll!JIT_ByRefWriteBarrier�() Unknown
00007ffb219709f9() Unknown
00007ffb219708cf() Unknown
clr.dll!CallDescrWorkerInternal�() Unknown
clr.dll!CallDescrWorkerWithHandler() Unknown
clr.dll!MethodDescCallSite::CallTargetWorker() Unknown
clr.dll!RunMain() Unknown
clr.dll!Assembly::ExecuteMainMethod() Unknown
clr.dll!SystemDomain::ExecuteMainMethod() Unknown
clr.dll!ExecuteEXE(struct HINSTANCE__ *) Unknown
clr.dll!_CorExeMainInternal() Unknown
clr.dll!_CorExeMain�() Unknown
mscoreei.dll!_CorExeMain�() Unknown
mscoree.dll!_CorExeMain_Exported�() Unknown
kernel32.dll!BaseThreadInitThunk() Unknown
ntdll.dll!RtlUserThreadStart�() Unknown
I suspect upstream will look a little different but similar.
Regression?
No not a regression as far as I'm aware.
Known Workarounds
Null checking correctly C# avoids this crash.
Configuration
Environment I reproduced under
- Net 6 and .Net 7
- Mono only, CoreCLR is fine
- Windows
- x64
Yes from looking at the IR emitting code I am fairly confident that this crash will reproduce on other architectures & operating systems.
Other information
No response