Il2CppInterop icon indicating copy to clipboard operation
Il2CppInterop copied to clipboard

New function metadata init cause runtime crash with XrefScan

Open HookedBehemoth opened this issue 2 years ago • 2 comments

In the past, il2cpp would emit a single call to il2cpp_codegen_initialize_method with a method token. Now there are multiple calls to il2cpp_codegen_initialize_runtime_metadata with potentially multiple tokens. When using XrefScan on a method before the runtime, values can be left uninitialized, resulting in a segfault/AccessViolation.

Didn't bisect when exactly codegen was altered but I can observe this behavior in 2022.3.5

I couldn't find any conversation on this issue so far. I'll work on a patch myself if you don't have something waiting for it already. This would probably change the Cache-Attribute parameter "public long MetadataInitTokenRva;" to public long[] MetadataInitTokenRvas;. I don't expect this to cause any major breakage on any prior version so I don't think I'll actually have to bisect the version and nail this logic down.

Steps for reproduction

- Project on ~2022.3.5 - Have class like ```cs using UnityEngine;

public class Test : MonoBehaviour { void Update() { Debug.Log("test"); Debug.Log("This is a test with a long string"); } }

- Compile with il2cpp
- Install mod framework using il2cppinterop
- Invoke xref on Test::Update before the game does
```cs
var method = typeof(Il2Cpp.Test)
    .GetMethod("Update", Public | Instance);

foreach (var str in XrefScanner
    .XrefScan(method)
    .Where(scan => scan.Type == XrefType.Global)
    .Select(scan => scan.ReadAsObject().ToString())) {
    LoggerInstance.Msg(str);
};
  • Observe crash
  • Observe that it doesn't crash if you add this snippet before the xref
var test = new Il2Cpp.Test();
test.Update();

HookedBehemoth avatar Sep 15 '23 23:09 HookedBehemoth

Ok I missed that those aren't tokens but object pointers.

HookedBehemoth avatar Sep 16 '23 01:09 HookedBehemoth

Ok this is even more annoying than I had anticipated.

  1. I couldn't get Cecil to store the arrays. I'll look into that but for now I'm just testing without caching.
  2. I found a function that has multiple initializer blocks sprinkled in. I couldn't find such code in the generated C++ files but it might be inlined by the compiler... Not entirely sure how I'd want to deal with that just yet. I could just call the metadata init function before dereferencing any of the found globals but that's messy, potentially slow and I'm not sure if those are always immutable or if a game could alter those.

HookedBehemoth avatar Sep 16 '23 16:09 HookedBehemoth