Hey! We need help from somebody who knows the details about changes in ReadOnlySpan => new byte[ ] pattern in il2cpp in recent unity versions.
After upgrading to a new unity version (from 2021.3.2 to 2021.3.9 and then to 2022.1.16f1) we are seeing a crash in the Global Metadata CPP file when getting these ReadOnlySpan fields. Here is the full callstack for windows platform:
The build was compiled with minimal stripping. When we use the debug version of il2cpp we see the error that the fieldRefSize is out of bounds, and then a crash.
We are reading lots of different read only span values from different classes during serialization and deserialization, and everything goes ok for a while, but crashes eventually. Here is the example of fields we are using:
Code Spoiler
public sealed class JointObjectSaveDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter<global::Game.SaveSystem.DataModels.Construction.JointObjectSaveData>
{
// ToObjectId
private static global::System.ReadOnlySpan<byte> GetSpan_ToObjectId() => new byte[1 + 10] { 170, 84, 111, 79, 98, 106, 101, 99, 116, 73, 100 };
// FromObjectId
private static global::System.ReadOnlySpan<byte> GetSpan_FromObjectId() => new byte[1 + 12] { 172, 70, 114, 111, 109, 79, 98, 106, 101, 99, 116, 73, 100 };
// ParentId
private static global::System.ReadOnlySpan<byte> GetSpan_ParentId() => new byte[1 + 8] { 168, 80, 97, 114, 101, 110, 116, 73, 100 };
// LocalTransformData
private static global::System.ReadOnlySpan<byte> GetSpan_LocalTransformData() => new byte[1 + 18] { 178, 76, 111, 99, 97, 108, 84, 114, 97, 110, 115, 102, 111, 114, 109, 68, 97, 116, 97 };
// IsWrongOrder
private static global::System.ReadOnlySpan<byte> GetSpan_IsWrongOrder() => new byte[1 + 12] { 172, 73, 115, 87, 114, 111, 110, 103, 79, 114, 100, 101, 114 };
// JointType
private static global::System.ReadOnlySpan<byte> GetSpan_JointType() => new byte[1 + 9] { 169, 74, 111, 105, 110, 116, 84, 121, 112, 101 };
// IfCanChangeType
private static global::System.ReadOnlySpan<byte> GetSpan_IfCanChangeType() => new byte[1 + 15] { 175, 73, 102, 67, 97, 110, 67, 104, 97, 110, 103, 101, 84, 121, 112, 101 };
// IfAlwaysFixed
private static global::System.ReadOnlySpan<byte> GetSpan_IfAlwaysFixed() => new byte[1 + 13] { 173, 73, 102, 65, 108, 119, 97, 121, 115, 70, 105, 120, 101, 100 };
// JointCells
private static global::System.ReadOnlySpan<byte> GetSpan_JointCells() => new byte[1 + 10] { 170, 74, 111, 105, 110, 116, 67, 101, 108, 108, 115 };
// JointSides
private static global::System.ReadOnlySpan<byte> GetSpan_JointSides() => new byte[1 + 10] { 170, 74, 111, 105, 110, 116, 83, 105, 100, 101, 115 };
// IfAlwaysReal
private static global::System.ReadOnlySpan<byte> GetSpan_IfAlwaysReal() => new byte[1 + 12] { 172, 73, 102, 65, 108, 119, 97, 121, 115, 82, 101, 97, 108 };
// IfMoving
private static global::System.ReadOnlySpan<byte> GetSpan_IfMoving() => new byte[1 + 8] { 168, 73, 102, 77, 111, 118, 105, 110, 103 };
// IndexInOtherJoints
private static global::System.ReadOnlySpan<byte> GetSpan_IndexInOtherJoints() => new byte[1 + 18] { 178, 73, 110, 100, 101, 120, 73, 110, 79, 116, 104, 101, 114, 74, 111, 105, 110, 116, 115 };
// IndexInMyJoints
private static global::System.ReadOnlySpan<byte> GetSpan_IndexInMyJoints() => new byte[1 + 15] { 175, 73, 110, 100, 101, 120, 73, 110, 77, 121, 74, 111, 105, 110, 116, 115 };
// IndexInOtherRealJoints
private static global::System.ReadOnlySpan<byte> GetSpan_IndexInOtherRealJoints() => new byte[1 + 22] { 182, 73, 110, 100, 101, 120, 73, 110, 79, 116, 104, 101, 114, 82, 101, 97, 108, 74, 111, 105, 110, 116, 115 };
}
Here is how our call looks like from a cpp generated file:
il2cpp code
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR ReadOnlySpan_1_tA850A6C0E88ABBA37646A078ACBC24D6D5FD9B4D LastUsedSaveDataContainerFormatter_GetSpan_Value_m6C903997D56582E16748B4F6400CFEEE2959503F (const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&ReadOnlySpan_1__ctor_m470D1527EF015478E8677C7BCB52C8410A1DB604_RuntimeMethod_var);
il2cpp_codegen_initialize_runtime_metadata((uintptr_t*)&U3CPrivateImplementationDetailsU3E_tDFF6183F1647CB4ABECDCF95F4E30358653AC416_StaticFields____FEE9E4E29E345D1FAF32A6AB1CEDB4769703E70A4688A5D122367C3AA8BC75FF_134_RVAStorage);
s_Il2CppMethodInitialized = true;
}
{
// private static global::System.ReadOnlySpan<byte> GetSpan_Value() => new byte[1 + 5] { 165, 86, 97, 108, 117, 101 };
ReadOnlySpan_1_tA850A6C0E88ABBA37646A078ACBC24D6D5FD9B4D L_0;
memset((&L_0), 0, sizeof(L_0));
ReadOnlySpan_1__ctor_m470D1527EF015478E8677C7BCB52C8410A1DB604_inline((&L_0), (void*)((__StaticArrayInitTypeSizeU3D6_t7E0A8B1D1528CB0F606906B97D5BC2A8406346F9*)U3CPrivateImplementationDetailsU3E_tDFF6183F1647CB4ABECDCF95F4E30358653AC416_StaticFields____FEE9E4E29E345D1FAF32A6AB1CEDB4769703E70A4688A5D122367C3AA8BC75FF_134_RVAStorage), 6, /*hidden argument*/ReadOnlySpan_1__ctor_m470D1527EF015478E8677C7BCB52C8410A1DB604_RuntimeMethod_var);
return L_0;
}
}
The exact filed that fails varies depending on the platform, but consistent between runs within the one platform. Repo rate of the crash is 7/10 for windows and 10/10 for sony platforms (both ps4 and ps5).
We couldn’t reproduce this crash in a separate small project yet, unfortunately.