Type Transparency in .NET 4 – #11

May 29th 2009 05:42 am

Up to this point I have focused on transparency with regards to .NET methods, but you can utilize the transparency attributes on types as well. They basically imply the same layering as they do when applied to methods, but there are some interesting invariants that the CLR will enforce with regards to type transparency.

There are two attributes of interest, the System.Security.SecuritySafeCriticalAttribute and the System.Security.SecurityCriticalAttribute. If you remember from the last tip, transparent code can only call critical code through safe critical code. So what does it mean for a type to safe critical or critical?

In most cases, it means that every member—this includes methods, fields, property getters and setters, nested classes, and delegates—inherits the annotation. Have a look at the class below.

[SecurityCritical]

public class Foo

{

    public static int Bar;

 

    public static class Bar

    {

        public static void Exec() { }

    }

 

    public Foo()

    {

    }

 

    public void Baz()

    {

    }

}

The Foo class is marked SecurityCritical, which means that transparent code cannot do the following:

  • Instantiate a new Foo.
  • Access the static Bar field.
  • Call the Exec method on the nested Bar class.
  • Call the Baz method.
  • Use reflection to call any of the above.

So even though the fields, methods, and nested classes aren’t explicitly marked security critical, the attribute on the class forces the critical behavior to flow down to all its members.

When you start mixing transparency and inheritance, it gets a bit tricky. There are some simple rules you can learn to help.

1. Derived types must be at least as restrictive as their base types.

If I decide to extend Foo with a FooBar class, then it must be marked with the SecurityCriticalAttribute if you want to use the class. Otherwise, when the JIT compiler encounters code that instantiates or uses FooBar, it will throw a TypeLoadException. In other words, Main will not even execute here:

public class FooBar : Foo

{

}

 

static void Main(string[] args)

{

    new FooBar();

}

Here is a list of the allowed combinations of base types and derived types.

Base Type Derived Type
Transparent Transparent
Transparent Safe Critical
Transparent Critical
Safe Critical Safe Critical
Safe Critical Critical
Critical Critical

 

2. Overridden methods must be as restrictive as the base method.

This means that when you override a Critical method, your method must also be marked Critical. However, Transparent and Safe Critical are considered as the same restriction from this rule’s point-of-view, so I can have a Transparent override of a Safe Critical method, and vice versa, without problems.

What, then, is the problem with this code?

[SecurityCritical]

public class RemotableObject : MarshalByRefObject

{

    public override object InitializeLifetimeService()

    {

        return base.InitializeLifetimeService();

    }

}

In .NET 4 the MarshalByRefObject.InitializeLifetimeService method is Critical, but we also established earlier in this post that if you mark a type as Critical, then every member inside of it is also Critical, right?

Well, I said "in most cases." This is the exception to the rule. From there we come to the last rule.

3. Overridden methods are always Transparent by default.

The problem above, then, can be remedied by marking InitializeLifeTimeService with the SecurityCriticalAttribute explicitly.

And that’s it for type transparency!

Posted by David DeWinter under .NET4/VS2010 & Security Tips | No Comments »

Trackback URI | Comments RSS

Leave a Reply