Description
Description
Discovered in .NET Framework 4.8 and still present in .NET 8.0. (Please fix in .NET Framework as well.)
sizeof(int)
(4) is subtracted instead 8 for x64, causing an extra four bytes past the end of the boxed struct value to be compared:
runtime/src/coreclr/classlibnative/bcltype/objectnative.cpp
Lines 160 to 167 in 00874d6
That sizeof(int)
line was migrated as-is when this Git history started.
Reproduction Steps
For .NET Framework, where the bug was found, I'm not how to set up a repro. Four bytes past the end of most boxed values is often going to go into the object header of another object, and the object header of another object on x64 is four zero bytes for padding followed by four sync block bytes. Those four padding bytes are probably why repros are so hard to come by. There must have been a larger object that was freed, and this boxed object was allocated over memory that had not been zeroed.
My team members and I have been seeing this bug in RuntimeHelpers.Equals every month or two where it incorrectly returns false and triggers one of our debug assertions. When it incorrectly returns false, I verified that the boxed values being compared are identical in raw memory, but the four bytes following the boxed object do differ from zero in the cases where RuntimeHelpers.Equals is incorrectly returning false.
For .NET 6 and 8, this repro works instantly (thanks @EgorBo):
using System.Runtime.CompilerServices;
while (true)
{
if (!RuntimeHelpers.Equals(default(Guid), default(Guid)))
throw new Exception("!");
}
Expected behavior
RuntimeHelpers.Equals should compare only the instance data of the boxed values.
Actual behavior
RuntimeHelpers.Equals appears to compare an additional four bytes past the end of the instance data of the boxed values.
Regression?
This is broken in .NET Framework 4.8 as well.
Known Workarounds
No workarounds known since comparing memory inside boxed structs is not safe without pausing GCs.
Configuration
No response
Other information
No response