Last active
October 15, 2019 13:38
-
-
Save nguerrera/625546d47dd3bd553193 to your computer and use it in GitHub Desktop.
Absence of 'unsafe' C# keyword/switch does not guarantee type or memory safety.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The absence of the C# unsafe keyword and switch does not guarantee code | |
is type or memory safe. It merely prevents the use of pointer types and | |
fixed size buffers. | |
The only thing that can guarantee type and memory safety is | |
verification and CAS/transparency enforcement. | |
In particular, the absence of unsafe C# blocks does NOT: | |
a) guarantee that all code in the assembly is verifiable. | |
b) prevent unmanaged interop. | |
c) prevent the use of type or memory unsafe API that do not | |
have pointers in their signature. | |
Let's look at a few examples centered around just one thing that can | |
go wrong in fully trusted C# code that does not use the unsafe keyword | |
or switch -- string mutation. | |
This is meant to be educational. Please DO NOT put any of it to use in | |
real code!!! | |
None of these are security holes as the code must have full trust. | |
*/ | |
using System; | |
using System.Runtime.InteropServices; | |
// a) Unverifiable construct that does not require the unsafe keyword: | |
// Overlapping object references using explicit layout. | |
static class OverlappingObjectReferences | |
{ | |
[StructLayout(LayoutKind.Explicit)] | |
struct Union | |
{ | |
[FieldOffset(0)] | |
public string String; | |
[FieldOffset(0)] | |
public StringPwner Pwner; | |
} | |
class StringPwner | |
{ | |
public int Length; | |
public char FirstChar; | |
public char SecondChar; | |
} | |
public static void Demonstrate() | |
{ | |
Union u = new Union(); | |
u.String = "Hello World"; | |
u.Pwner.FirstChar = 'X'; | |
u.Pwner.SecondChar = 'Y'; | |
// Prints XYllo World | |
Console.WriteLine(u.String); | |
// Also prints XYllo World as we've mutated the interned string literal. | |
Console.WriteLine("Hello World"); | |
} | |
} | |
// b) unmanaged interop | |
static class UnmanagedInterop | |
{ | |
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] | |
static extern IntPtr memcpy(string dest, string src, UIntPtr count); | |
public static void Demonstrate() | |
{ | |
string hello = "hello"; | |
string oops = "oops!"; | |
memcpy(hello, oops, (UIntPtr)(oops.Length * sizeof(char))); | |
Console.WriteLine(hello); | |
} | |
} | |
// c) using dangerous API that do not have pointers in their signature. | |
static class GCHandleAndMarshal | |
{ | |
public static void Demonstrate() | |
{ | |
string s = "Test"; | |
var handle = GCHandle.Alloc(s, GCHandleType.Pinned); | |
Marshal.WriteInt16(handle.AddrOfPinnedObject(), 'X'); | |
handle.Free(); | |
Console.WriteLine(s); | |
} | |
} | |
static class Program | |
{ | |
static void Main() | |
{ | |
OverlappingObjectReferences.Demonstrate(); | |
UnmanagedInterop.Demonstrate(); | |
GCHandleAndMarshal.Demonstrate(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment