SSJX.CO.UK
Content

Memory Safety - How D Can Prevent Use After Free Errors

Introduction

A 'Use After Free' error is where after giving back (freeing) some memory, you carry on using it. The problem is that this memory may be being used by some other task, reading and/or writing to it may expose/corrupt information or provide a means for bad parties to launch viruses and exploits.

Using the previous box example, this time we ask to use 10 boxes, put some information in them, then give them back. However we forget we gave them back and continue to put things in them and read from them, despite the boxes now being used for something else and may have other stuff in them!

Example Of Use After Free

The example below (in C) shows how a Use After Free error can occur.

#include <stdio.h>

void main(){
	// Allocate 10 boxes
	char *box=malloc(10);
		
	// Put the number 10 in the first box
	box[0]=10;
	
	// Finished, so we free the memory (give it back)
	free(box);
	
	// Not realising the above we carry on putting numbers in boxes!
	box[1]=20;
}

This program compiles and runs without giving any errors despite being incorrect, this can lead to program instabilities and the security problems mentioned in the introduction.

This example uses a fixed number of boxes, malloc() is often used when the number of boxes is not known at compile time.

The next section details how Use After Free errors can be avoided using D Programming Language.

Using The D Programming Language

From the D website:

D is a general-purpose programming language with static typing, systems-level access, and C-like syntax. With the D Programming Language, write fast, read fast, and run fast.

For further reading about D's Memory Safety, click here.

One way D solves the problem of freeing memory (and other memory issues) is by the use of a 'Garbage Collector'. When memory is no longer used or is out of scope, the system automatically marks it free and makes it available again. Below is the same program using D's Dynamic Arrays:

import std.stdio;

void main(){
	// Create a Dynamic Array 10 boxes
	char[] box;
	box.length=10;
	
	// Put the number 10 in the first box
	box[0]=10;
	
	// We carry on putting numbers in boxes
	box[1]=20;
	
	// Finished, so the system frees the memory (gives it back)
}

That looks good but what if the 'box[1]=20' was an error in the C version? What if it was meant to be 'pot[1]=20'? That would explain how it was after the 'free()' in the C version? There are two ways to catch that, we can either reduce the number of boxes after we are done:

import std.stdio;

void main(){
	// Create a Dynamic Array of 10 boxes
	char[] box;
	box.length=10;
	
	// Put the number 10 in the first box
	box[0]=10;
	
	// Shrink our number of boxes to 0
	box.length=0;
	
	// We accidently carry on putting numbers in boxes
	box[1]=20;
	
}

The above will cause an Out Of Bounds error when run:

core.exception.ArrayIndexError@uaf_d.d(14): index [1] is out of bounds for array of length 0

It would be better though if the problem was detected when the program is compiled, we can do that by limiting the scope of our box memory:

import std.stdio;

void main(){

	// We create a scope so the box Dynamic Array only exists within the curly brackets
	{
		char[] box;
		box.length=10;
		
		// Put the number 10 in the first box
		box[0]=10;
	}
	// Finished, so the system can free the memory (take it back)
	
	// This will fail as box[1] does not exist outside of the curly brackets
	box[1]=20;
}

And as expected, the above gives this message at compile time:

uaf_d.d(16): Error: undefined identifier `box`

Extra Points

In the initial C example, if we forgot to put a 'free()' we would have created a memory leak, the memory would not have been given back. Thanks to the Garbage Collector (GC) in D, this would not happen as the GC would take care of the memory as soon as it went out of scope.

Conclusion

According to the Chromium Project Memory Safety page, 36% of it's memory safety bugs are use after free related. Using D can eliminate this entire class of error and the bounds checking previously mentioned would probably make a big dent in the remaining 64% of memory related errors.

Last Updated 20/05/2024
Created 19/02/23