Where Does the Stack Walk Start or: Why Do Demands from Main Always Succeed?

When starting to play with CAS a lot of people come up with toy programs that simply do a Demand for some permission or another, then copy that program to various locations that will cause it to be granted different permission sets.  It's generally a surprise when those demands pass, even though caspol confirms that their assemblies aren't being granted the permission that is being demanded.

The reason for this has to do with which stack frame the CLR starts at when doing the stack walk: instead of starting with the method that calls Demand(), we start with that method's caller.  This means that doing a Demand in the Main method of an exe will never cause a SecurityException, even if that program wasn't granted the permission that was demanded since Main() doesn't have a caller on the call stack.  (For instance, if you tried to pull the IsFullTrust property into Main in this post, you would always think that you were running FullTrust since the Demand will never throw).

For instance, lets look at two Main methods, both in exes on a file share:

public static void Main()
{
    try
    {
        new FileIOPermission(FileIOPermissionAccess.Read, @"c:\boot.ini").Demand();
        Console.WriteLine("No exception");
    }
    catch(SecurityException)
    {
        Console.WriteLine("Got a SecurityException");
    }
}

public static void Main()
{
    try
    {
        File.OpenText(@"c:\boot.ini");
        Console.WriteLine("No exception");
    }
    catch(SecurityException)
    {
        Console.WriteLine("Got a SecurityException");
    }
}

In this case, the call stacks at the time of the Demand essentially look like:

Demand() Demand()
Main() File::Read()
Main()

Applying the rule where the caller of the method which calls Demand() is the first on the stack walk, the first program begins its stack walk at Main's caller, which doesn't exist.  Therefore, since no frames on the stack walk were not granted FileIOPermission, the demand succeeds and no exception is thrown.  In the second case, the rule tells us that Main is the first frame to check on the stack walk ... and since Main is not granted FileIOPermission to c:\boot.ini a SecurityException is thrown.  And in fact, this is exactly the behavior we see when running the two programs.

At first glance this might appear to be a security issue, after all we're skipping over a method that clearly does not have the permissions being requested.  However, since the method we're skipping over is the one which contains the code to call Demand(), if that demand did cause a SecurityException, then the person attempting to do whatever it is that they're being denied access to could simply omit calling Demand() in the first place.  In fact, most malicious code isn't security conscious enough to ensure that it's allowed permission to do whatever evil action it wants to anyway ... "Oh, the user doesn't want me to upload their Microsoft Money data to my server.  Well, in that case I guess I'll just move on." :-)