Skip to content

Instantly share code, notes, and snippets.

@nguerrera
Last active October 15, 2019 13:38
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nguerrera/625546d47dd3bd553193 to your computer and use it in GitHub Desktop.
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.
/*
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