locked
Unmanaged reference types? RRS feed

  • Question

  • I am curious about the feasibility of manipulating what I call "unmanaged reference types" that is instances of ref types that are not allocated in GC memory but some arbitrary memory.

    Getting technical, if I have a refrence variables (eg: SomeClass my_instance; ) then 'my_instance' is in fact a pointer to an instance in the GC heap (we know this pointer points to the instance data with object header, fields etc).

    So if I had a way of creating such an instance in (say) the local process heap (don't worry about how) and setting 'my_instance' to that address, it suggests that C# code would 'see' this as a real object reference and work with it correctly. I could pass the ref just like any other, I could access members just like any instance and so on.

    Of course the object would never b 'cleaned up' if it became unreferenced (i.e. no GC would take place) and having other real object instances refer to this object would also causes issues.

    But if these were prevented, would not C# and the CLR run fine with such an object?

    This is just an in-depth exploration of this idea not some formal proposal for anything.

     

    Cap'n

     

    Wednesday, July 27, 2011 12:02 PM

Answers

  • "would not C# and the CLR run fine with such an object?"
    Very likely. As the following crazy piece of code shows it:
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    
    [StructLayout(LayoutKind.Sequential)]
    class foo {
      public List<int> list = new List<int>();
    }
    
    [StructLayout(LayoutKind.Sequential)]
    unsafe struct ObjPtr {
      private object o;
      private int i;
    
      public ObjPtr(object o) {
        this.o = o;
        this.i = 0;
      }
    
      public int* Ptr() {
        fixed (int* pi = &i)
          return (int*)*(pi - 1);
      }
    }
    
    unsafe class Program {
      static void Main(string[] args) {
        foo f = new foo();
        f.list.Add(42);
    
        ObjPtr p = new ObjPtr(f);
        int** pfoolist = (int**)(p.Ptr() + 1); //+1 to skip foo's method table
    
        int* newListPtr = (int*)Marshal.AllocHGlobal(6 * 4); // space for 4 fields + method table + object header
        int* oldListPtr = *pfoolist - 1; //-1 to go to from object to object header
    
        for (uint i = 0; i < 6; i++)
          newListPtr[i] = oldListPtr[i];
    
        *pfoolist = newListPtr + 1; //+1 to go from object header to object
    
        Console.WriteLine(f.list[0]);
      }
    }
    
    

    This will create a List<int> on the managed heap, copy it to unmanaged memory and then replace the pointer to the managed list with the new unmanaged pointer. Not only that now you have a ref type stored in unmanaged memory but this ref type also have a reference to an object allocated in managed memory (List<int>'s items array). And it still works.
    I haven't tried to trigger a GC but I expect to get some sort of "GC heap corruption detected" error :)
    • Marked as answer by Paul Zhou Thursday, August 4, 2011 5:46 AM
    Wednesday, July 27, 2011 5:47 PM

All replies

  • "would not C# and the CLR run fine with such an object?"
    Very likely. As the following crazy piece of code shows it:
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    
    [StructLayout(LayoutKind.Sequential)]
    class foo {
      public List<int> list = new List<int>();
    }
    
    [StructLayout(LayoutKind.Sequential)]
    unsafe struct ObjPtr {
      private object o;
      private int i;
    
      public ObjPtr(object o) {
        this.o = o;
        this.i = 0;
      }
    
      public int* Ptr() {
        fixed (int* pi = &i)
          return (int*)*(pi - 1);
      }
    }
    
    unsafe class Program {
      static void Main(string[] args) {
        foo f = new foo();
        f.list.Add(42);
    
        ObjPtr p = new ObjPtr(f);
        int** pfoolist = (int**)(p.Ptr() + 1); //+1 to skip foo's method table
    
        int* newListPtr = (int*)Marshal.AllocHGlobal(6 * 4); // space for 4 fields + method table + object header
        int* oldListPtr = *pfoolist - 1; //-1 to go to from object to object header
    
        for (uint i = 0; i < 6; i++)
          newListPtr[i] = oldListPtr[i];
    
        *pfoolist = newListPtr + 1; //+1 to go from object header to object
    
        Console.WriteLine(f.list[0]);
      }
    }
    
    

    This will create a List<int> on the managed heap, copy it to unmanaged memory and then replace the pointer to the managed list with the new unmanaged pointer. Not only that now you have a ref type stored in unmanaged memory but this ref type also have a reference to an object allocated in managed memory (List<int>'s items array). And it still works.
    I haven't tried to trigger a GC but I expect to get some sort of "GC heap corruption detected" error :)
    • Marked as answer by Paul Zhou Thursday, August 4, 2011 5:46 AM
    Wednesday, July 27, 2011 5:47 PM
  • "would not C# and the CLR run fine with such an object?"
    Very likely. As the following crazy piece of code shows it:
    using System;
    
    using System.Collections.Generic;
    
    using System.Runtime.InteropServices;
    
    
    
    [StructLayout(LayoutKind.Sequential)]
    
    class foo {
    
     public List<int> list = new List<int>();
    
    }
    
    
    
    [StructLayout(LayoutKind.Sequential)]
    
    unsafe struct ObjPtr {
    
     private object o;
    
     private int i;
    
    
    
     public ObjPtr(object o) {
    
      this.o = o;
    
      this.i = 0;
    
     }
    
    
    
     public int* Ptr() {
    
      fixed (int* pi = &i)
    
       return (int*)*(pi - 1);
    
     }
    
    }
    
    
    
    unsafe class Program {
    
     static void Main(string[] args) {
    
      foo f = new foo();
    
      f.list.Add(42);
    
    
    
      ObjPtr p = new ObjPtr(f);
    
      int** pfoolist = (int**)(p.Ptr() + 1); //+1 to skip foo's method table
    
    
    
      int* newListPtr = (int*)Marshal.AllocHGlobal(6 * 4); // space for 4 fields + method table + object header
    
      int* oldListPtr = *pfoolist - 1; //-1 to go to from object to object header
    
    
    
      for (uint i = 0; i < 6; i++)
    
       newListPtr[i] = oldListPtr[i];
    
    
    
      *pfoolist = newListPtr + 1; //+1 to go from object header to object
    
    
    
      Console.WriteLine(f.list[0]);
    
     }
    
    }
    
    
    
    

    This will create a List<int> on the managed heap, copy it to unmanaged memory and then replace the pointer to the managed list with the new unmanaged pointer. Not only that now you have a ref type stored in unmanaged memory but this ref type also have a reference to an object allocated in managed memory (List<int>'s items array). And it still works.
    I haven't tried to trigger a GC but I expect to get some sort of "GC heap corruption detected" error :)


    Hi MIke

    Thks for posting that it is interesting, I've done similar stuff (also done this "kind of" thing as dynamic IL a couple of times) but your example does go to the heart of the question, most helpful.

     

    Thx

    Cap'n

    Saturday, August 6, 2011 5:03 PM