Custom memory allocation in C# Part 6 — Memory errors

This is the sixth part of the Custom memory allocation series. For your convenience you can find other parts in the table of contents in Part 1 — Allocating object on a stack

We already know how to allocate object in any place in memory. We can invoke constructor for already created object. We know how to hijack new operator and dispatch allocations to our code. We can parse lambda and have compile time support for unsafe allocations. Unfortunately, we have another thing: problems.

Table of Contents

The code

I will describe some problems with our mechanisms using the following code:

The code is long by design. I wanted to test passing multiple parameters with different types and verify different allocators. And here are the conclusions.

Simple allocation on a heap

Ordinary allocation on a heap works pretty stable. As you can see, I was able to allocate object with multiple parameters, I could use finalizer, and inherit from other class. However, this was the only one stable method.

Problems with hijacking new

When we examined the new operator we hijacked only one of its implementations. Unfortunately, there are others which we should patch as well. Let’s see the problematic code:

We patch operator to direct it to our custom allocator. Next, we create an object with multiple parameters, and restores operator. This works as designed, however, if we replace VerySimple with Dummy it breaks in the Free method because allocator says that it didn’t allocate this object. Why? Because Dummy inherits from something different then System.Object. Because of different allocation mechanisms, allocator only handles call to base class allocation, not to the derived class.

Problems with allocating on a stack

Allocation on a stack looks pretty easy. We have the following code:

Observe, that we are passing null to reference type. If we now replace it with something else (string for instance), we get the following error:

Memory exception for Dummy type

It looks pretty strange. If you debug the code you will see that the exception is thrown in this line in constructor:

Suspicious line in constructor

Well, looks bad. But if we try to read or write values using Immediate Window we will have no errors. Well, what can be wrong? Let’s debug the machine code:

Even more suspicious machine code

What is going on here? This is the call to call clr!JIT_WriteBarrierEAX method. I am not going to describe here what are memory barriers, but there is one important thing inside their implementation in .NET. Inside a write barrier, a check is made to see whether the object being assigned to has an address lower that the start address of generation 1 (i.e., generation 1 or 2). In that case .NET updates some addresses to obtain lock on an address. Since lock must be made on a specific segment address, .NET simply performs some bitwise operations on the address of written object and modify memory there — but this memory might not be mapped to our process. Unfortunately, when you try to access memory on a stack, you must be careful. 1 out of 20 executions had no error because of address randomization.

Patching new once again

When we were implementing new hijacking, we were manually attaching debugger to our application and we couldn’t start it using F5. This is another reason to not do that — if we cannot debug application, we are screwed. Of course, we could try to come up with different patching mechanism and detected in runtime whether we have debugger connected or not, but then we would probably hit other errors.

Summary

Messing with memory is funny. We were able to implement multiple different scenarios and handle pretty nice cases. However, memory issues are hard to debug and this is why .NET relies on GC implementation. If we need to perform these dirty hacks maybe we should just take different language?