In an answer to his own controversial question, Mash has illustrated that you don't need the "unsafe" keyword to read and write directly to the bytes of any .NET object instance. You can declare the following types:
[StructLayout(LayoutKind.Explicit)]
struct MemoryAccess
{
[FieldOffset(0)]
public object Object;
[FieldOffset(0)]
public TopBytes Bytes;
}
class TopBytes
{
public byte b0;
public byte b1;
public byte b2;
public byte b3;
public byte b4;
public byte b5;
public byte b6;
public byte b7;
public byte b8;
public byte b9;
public byte b10;
public byte b11;
public byte b12;
public byte b13;
public byte b14;
public byte b15;
}
And then you can do things like change an "immutable" string. The following code prints "bar" on my machine:
string foo = "foo";
MemoryAccess mem = new MemoryAccess();
mem.Object = foo;
mem.Bytes.b8 = (byte)'b';
mem.Bytes.b10 = (byte)'a';
mem.Bytes.b12 = (byte)'r';
Console.WriteLine(foo);
You can also trigger an AccessViolationException by corrupting object references with the same technique.
Question: I thought that (in pure managed C# code) the unsafe keyword was necessary to do things like this. Why is it not necessary here? Does this mean that pure managed "safe" code is not really safe at all?
You are still opting out of the 'managed' bit. There is the underlying assumption that if you can do that then you know what you're doing.
Whoops, I've muddled
unsafe
withfixed
. Here's a corrected version:The reason that the sample code does not require tagging with the
unsafe
keyword is that it does not contain pointers (see below quote for why this is regarded as unsafe). You are quite correct: "safe" might better be termed "run-time friendly". For more information on this topic I refer you to Don Box and Chris Sells Essential .NETTo quote MSDN,
The difference between fixed and unsafe is that fixed stops the CLR from moving things around in memory, so that things outside the run-time can safely access them, whereas unsafe is about exactly the opposite problem: while the CLR can guarantee correct resolution for a dotnet reference, it cannot do so for a pointer. You may recall various Microsofties going on about how a reference is not a pointer, and this is why they make such a fuss about a subtle distinction.
OK, that is nasty... the dangers of using a union. That may work, but isn't a very good idea - I guess I'd compare it to reflection (where you can do most things). I'd be interested to see if this works in a constrained access environment - if so, it may represent a bigger problem...
I've just tested it without the "Full Trust" flag, and the runtime rejects it:
And to have this flag, you already need high trust - so you can already do more nasty things. Strings are a slightly different case, because they aren't normal .NET objects - but there are other examples of ways to mutate them - the "union" approach is an interesting one, though. For another hacky way (with enough trust):