Edit: I'm well aware of that this works very well with value types, my specific question is about using this for reference types.
Edit2: I'm also aware that you can't overlay reference types and value types in a struct, this is just for the case of overlaying several reference type fields with each other.
I've been tinkering around with structs in .NET/C#, and I just found out that you can do this:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1 {
class Foo { }
class Bar { }
[StructLayout(LayoutKind.Explicit)]
struct Overlaid {
[FieldOffset(0)] public object AsObject;
[FieldOffset(0)] public Foo AsFoo;
[FieldOffset(0)] public Bar AsBar;
}
class Program {
static void Main(string[] args) {
var overlaid = new Overlaid();
overlaid.AsObject = new Bar();
Console.WriteLine(overlaid.AsBar);
overlaid.AsObject = new Foo();
Console.WriteLine(overlaid.AsFoo);
Console.ReadLine();
}
}
}
Basically circumventing having to do dynamic casting during runtime by using a struct that has an explicit field layout and then accessing the object inside as it's correct type.
Now my question is: Can this lead to memory leaks somehow, or any other undefined behavior inside the CLR? Or is this a fully supported convention that is usable without any issues?
I'm aware that this is one of the darker corners of the CLR, and that this technique is only a viable option in very few specific cases.
Since the garbage collector is untyped and only distinguishes between object references and plain bits, overlapping references won't confuse it. However, while one object reference can completely overlap another, this is unverifiable, aka unsafe (ECMA-335 standard, page 180, II.10.7 Controlling instance layout). It's easy to construct a program that exploits this unverifiability to crash horrendously:
using System.Runtime.InteropServices;
class Bar
{
public virtual void func() { }
}
[StructLayout(LayoutKind.Explicit)]
struct Overlaid
{
[FieldOffset(0)]
public object foo;
[FieldOffset(0)]
public Bar bar;
}
class Program
{
static void Main(string[] args)
{
var overlaid = new Overlaid();
overlaid.foo = new object();
overlaid.bar.func();
}
}
Here the func call loads a function pointer from one past the last element of object class's virtual table. According to this article following the vtbl there's a handle table. Treating it as a function pointer leads to a System.AccessViolationException.
Well, you found a loop hole, the CLR permits it since all overlapped fields are objects. Anything that would allow you to mess with an object reference directly gets rejected with a TypeLoadException:
[StructLayout(LayoutKind.Explicit)]
struct Overlaid {
[FieldOffset(0)]
public object AsObject;
[FieldOffset(0)]
public IntPtr AsPointer;
}
But you can exploit it by giving the classes fields. Nothing really bad happens as long as you are just reading the field values, you can get the value of the tracking handle that way for example.
Writing those fields however leads to an ExecutionEngineException. I think however that it is an exploit if you can guess the value of a tracking handle correctly. Practical use is sufficiently close to zero though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With