
An often overlooked aspect of software security is the proper allocation and release of resources. Scarce resources like database connections and file handles, if not properly disposed of after use, can result in memory leaks, resource pool consumption and other conditions that may lead to denial of service vulnerabilities. The C# 2.0 using construct can help to eliminate these conditions.
Although the .NET runtime implements a garbage collector to free memory that's been allocated to resources that are no longer being used, it is still up to the developer to promptly dispose of resources once they have reached the end of their usage so the garbage collector can do its job.
Let's look at a fairly common scenario involving disposable resources:
In the above code, a Stream object and a StreamReader object are instantiated for the purpose of reading a text file. Once that processing is complete, both objects are disposed of as they are no longer needed. However, what happens if the processing code causes an exception to be thrown? There is now a risk of the Dispose() method not being called for both objects which results in those allocated resources not being freed. Clearly this is not the desired behavior.
To remedy this issue in version 1.1 of the .NET framework (C# 1.0), the above code would be refactored in the following manner:
try
{
textFile = new FileStream(@"C:\TEST.TXT", FileMode.Open, FileAccess.Read);
textFileReader = new StreamReader(textFile);
{Code that reads from the text file and does something with the data}
}
catch (Exception ex)
{
{Exception handling code}
}
finally
{
textFileReader.Dispose();
textFile.Dispose();
}
Version 2.0 of the .NET framework (C# 2.0) introduced the using construct - a shorthand for allocating and disposing of any type that implements the IDisposable interface. The above code, refactored with a using construct, appears below:
{Code that reads from the text file and does something with the data}
}
The code in example three affords identical functionality and significantly simplifies the code. The key to the using construct is its ability to ensure that the Dispose() method of any object used in the construct is always called (which explains the construct's requirement that any object used must be of a type that implements IDisposable). So, in this case, both objects will be disposed of properly whether the code within the construct results in an exception or executes successfully.
I've referred to the using construct as "shorthand" throughout; the reason for this is simple: the code in example three, when compiled, will produce nearly identical intermediate language as the code in example two. Hence, the using construct is just a simpler, more efficient way of coding. Simplifying code leads to less bugs and improves the quality of code; quality code is the key to secure software!