BepInEx / Il2CppInterop

A tool interoperate between CoreCLR and Il2Cpp at runtime
GNU Lesser General Public License v3.0
206 stars 66 forks source link

Injected class fields are collected #40

Open limoka opened 2 years ago

limoka commented 2 years ago

Issue

Any fields in injected classes are being collected by garbage collector. This includes both fields coming from parent classes, and injected ones.

Fields which store a class, which is also stored somewhere, where it is recognized by garbage collector stops that. For example any Unity Game Objects and Components are not collected, as they are stored in Unity hierarchy.

When this happens field's wasCollected property is never set, in fact the object is replaced by some random allocated object. This causes severe data corruption if such classes are introduced into game state.

Steps to Reproduce

Create an injected class like this, and add it to any Game Object on Load()

public class TestClass : MonoBehaviour
{
    public Il2CppReferenceField<List<MyClass>> myClassList;

    public TestClass(IntPtr ptr) : base(ptr) { }

    private GCHandle myClassListHandle;

    private void Awake()
    {
    // In awake I initialize the field
        List<MyClass> list = new List<MyClass>();
    // Remove this line to remove the temporary fix
        myClassListHandle = GCHandle.Alloc(list, GCHandleType.Normal);
        myClassList.Set(list);
    }

    private void OnDestroy()
    {
    // Remove this line to remove the temporary fix
        myClassListHandle.Free();
    }
}

MyClass can be any class. For example a AudioClip, or some other serialized class.

Now check the value of the myClassList field after the game has loaded fully. It will contain garbage object, which is likely is not even related to original class. It will not contain any data you store there.

In the snippet I also included a temporary fix I found. If I create a GCHandle for my field object, it will not get collected.