Writing unsafe .NET code without the unsafe keyword

 
 
  • Gérald Barré

Someone asked me why I set AllowUnsafeBlocks to true in the Meziantou.DotNet.CodingStandard package (source). In this post, I'll explain what this property enables, and why it is not more unsafe to set it to true.

#The AllowUnsafeBlocks property

The AllowUnsafeBlocks compiler option allows code that uses unsafe features to compile. The default value for this option is false, meaning unsafe code is not allowed. To enable unsafe code, you must add the AllowUnsafeBlocks compiler option to your project file:

csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
</Project>

Enabling unsafe code allows 2 things in C#:

  • Allow the usage of the unsafe keyword. This keyword allows the usage of pointers, fixed-size buffers, and the fixed keyword.

    C#
    // The unsafable keyword is required to use pointers (int*).
    unsafe static void Square(int* p)
    {
        *p = *p * *p;
    }
  • The use of the [SkipLocalsInit] attribute which instructs the compiler to skip the initialization of local variables. I've already written about this attribute in a previous article.

    C#
    [System.Runtime.CompilerServices.SkipLocalsInit]
    static unsafe void DemoZeroing()
    {
        int i;
        Console.WriteLine(*&i); // Unpredictable output as i is not initialized
    }

#Writing unsafe code without the unsafe keyword

The System.Runtime.CompilerServices.Unsafe contains many methods that allow writing unsafe code, without the unsafe keyword. So, you don't need to set AllowUnsafeBlocks to true to use this class. This class was introduced with .NET Core 2.0 and allow to do really unsafe code.

You can use it to get pointer-like features. For instance, you can do pointer arithmetic from references:

C#
{
    int foo = 0;
    int bar = 0;

    // Get a reference to foo using a ref to bar
    ref var value = ref Unsafe.Add(ref bar, 1);

    // Edit foo
    value = 42;

    // Print "foo=42"
    Console.WriteLine($"foo={foo}");
}

You can get AccessViolationException when accessing unallocated pages:

C#
{
   int a = 0;
   ref var value = ref Unsafe.Add(ref a, 4096);

   // System.AccessViolationException: Attempted to read or write protected memory.
   // This is often an indication that other memory is corrupt.
   value = 42;
}

You can change the type of an instance:

C#
var a = new Vehicule() { Name = "test" };

var b = Unsafe.As<Printable>(a);
b.Print(); // Prints "test"


class Vehicule
{
    public string Name { get; set; }
}

class Printable
{
    public string Name { get; set; }

    public void Print() { Console.WriteLine(Name); }
}

You can use uninitialized variables:

C#
Unsafe.SkipInit(out SampleStruct a);
Console.WriteLine(a.A); // Undefined value
Console.WriteLine(a.B); // Undefined value

struct SampleStruct
{
    public int A;
    public int B;
}

BTW, you can set the value of a variable before declaring it:

C#
var dummy = 0;
ref var value = ref Unsafe.Add(ref dummy, -4);
value = 42; // Set the value of "a" which is declared here-after

Unsafe.SkipInit(out int a);
Console.WriteLine(a); // 42

Another way to do unsafe things without using the unsafe keyword is to use interop to call native methods and execute any unsafe code:

C#
[DllImport("my.dll")]
static extern void MethodDoingUnsafeThingsWithMemory(IntPtr point);

#Conclusion

You need to explicitly use the unsafe keyword to write unsafe code. Also, you can write unsafe code without the unsafe keyword. So, setting AllowUnsafeBlocks to true is not more unsafe than setting it to false. It just allows you to use the unsafe keyword when you want to.

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub