Tags: , , , , | Categories: .NET Posted by oleksii on 4/13/2011 4:53 PM | Comments (0)

.NET Common Language Runtime provides several valuable concepts and cool features. Application domain is one of them. Even though the theory of app domains is usually hidden and, as such, typically appears as a kind-of-test-level-question during an interview, it can be pretty handy during software development. For detailed core explanation see the book by J. Richter CLR via C# 3rd Edition.

So briefly, what actually are the distinguishing qualities of the app domain?

  1. Isolation
  2. Sandbox
    Perhaps, the most important characteristic of the CLR. It provides a process-like isolation to the each app domain. This is really dandy, as it keeps OS away from the pain of creating a new process, whilst still allowing the security and the other associated benefits of a separate process. Consider, for example, production IIS server that hosts multiple web-sites (assuming for simplicity it is a managed application). If we are to create a separate process for each web site, we are going to heavily stress the system without much actual value. In contrast, by keeping all the web-sites in the same process, we will greatly compromise security. If one web-site crash, this will results in all other production web-sites failure. Such approach doesn’t sound righteous and this is why CLR provides the additional isolation level within the process. In this example there can be one IIS process and many app domains, one for each web site. (This is a bit naïve, but will suffice the demonstration purpose.)
    • Process (IIS)
      • AppDomain 1 (Web-site 1)
        • Thread 1
        • Thread 2
        • ...
      • AppDomain 2 (Web-site 2)
        • Thread 1
        • Thread 2
        • ...
      • ...
  3. Architecture
  4. If the application is giant and controlling execution flow becomes a pain, we can separate some features into different domain, which will substantially ease maintenance and error handling.

  5. Exception handling
  6. Most errors in one domain do not have impact onto the other domain’s state. Thus most exceptions will not crash the whole application, but rather will stay isolated within the domain. Even uncaught exceptions like NullRefferenceException, ArgumentException or InvalidOperaionException will not result in the application crash. If the error, however, corrupts a process’s state, e.g. ThreadAbortedException or StackOverflowException, CLR by default is forced to close the whole application and unload all domains of the process.

  7. Custom security configuration
  8. Code downloaded from the Internet or freesome application's plug-ins are the examples of code that must be treated with extra caution. What if some funky guy left a bit of code to format your hard drive or update system registry? From the other hand, we still want people to contribute to the application development. This is where we can configure which operations are allowed and which permissions are assigned to the domain where the code runs.

  9. Memory management
  10. It is widely known that assembly cannot be separately unloaded. It means that once CLR loads the assembly to the memory, it stays there until the whole app domain unloaded. If we keep all assemblies in the same domain, me can have a problem – assembly memory leakage. Consider for a moment situation, where application security is managed by Domain Specific Language. For this purpose the system has got a number of Boo scripts – authorisation rules. Script are compiled into a separate assembly. So with the time passing by, we want to change or update rules/scripts, which are automatically picked up by the system. This is where we end up with many recompiled assemblies sitting in the memory and we may need to do the highly undesirable application restart to release them. With domains, this can be done frictionless by keeping the DSL scripts in a separate app domain.

  11. Communication
  12. Any communication between processes is usually very expensive, but getting data between app domains is relatively faster and cheaper. Such traffic (marshaling) still needs to be avoided and used only if absolutely needed.

Feww... I think that’s enough me talking, let’s see some code. I have designed a simple class with a Main entry. First, I configured a second app domain, that is called untrustedDomain. This domain has got very limited permissions. Then, I created an instance of the class and ran a method of the object. Keep in mind the code is executed in a separate domain.


class
Program { static void Main(string[] args) { //Configure permissions for highly untrusted code var evidence = new Evidence(); var zone = new Zone(SecurityZone.Internet); evidence.AddHostEvidence(zone); var permissionSet = SecurityManager.GetStandardSandbox(evidence); //Create a sandboxed AppDomain var setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; var untrustedDomain = AppDomain .CreateDomain("Untrusted code", evidence, setup, permissionSet); //Call the method that is potentially harmful try { var evilObj = untrustedDomain .CreateInstanceAndUnwrap( Assembly.GetExecutingAssembly().FullName, "EvilClass"); ((EvilClass)evilObj).MaliciousMethod(); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { //Unload appdomain //This is the only way to unload assemblies AppDomain.Unload(untrustedDomain); } Console.WriteLine("Main application is still working."); } }

And here is my evil class with malicious code or unhandled exceptions.

public class EvilClass : MarshalByRefObject
{
    public void MaliciousMethod()
    {
        //Harmful meta code - erase "C" drive
        //Operation denied and security exception thrown
        Directory.Delete(@"C:\", true);
        
        //This will not crash the whole application
        throw new NullReferenceException();

        //Exceptions that corrupt application state
        //Will force CLR to shut down the whole application

        //Crashes the whole application 
        //ThreadAbortedException is rised
        Thread.CurrentThread.Abort();

        //Kills the entire application
        throw new StackOverflowException();
    }
}

Let’s see the output, shall we? If I execute Directory.Delete, security exception will be thrown by the host CLR and the operation won’t be executed. Main app domain will still continue to function.

System.Security.SecurityException: Request for the permission of type
'System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, 
Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.

Main application is still working.

Next, if exception is thrown and it is uncaught, the application will not shut down, as domains are fully isolated.

Object reference not set to an instance of an object.

Main application is still working.

Finally, if critical error occurred which resulted in the application's corrupted state, the CLR will crash the whole application with all its internals.

Thread was being aborted.
Process is terminated due StackOverflowException.

Well, this is the matter in a nutshell.

If you enjoyed this post, make sure you subscribe to my RSS feed!

blog comments powered by Disqus