Freigeben über


Declarative Security and Reflection

If you’re using the CustomAttributeData APIs to examine declarative security permission, you might notice that the returned information looks a little strange.  The CustomAttributeData object that you’ll see for the declarative security attribute will have a ConstructorInfo that is of the correct type, but may not be the same constructor that was actually used in the source code.  Similarly, the ConstructorArguments and NamedArguments property will say that the attribute has neither, even though if you look at your source code you may see one or both of these types of arguments.

So, what’s going on?

This behavior is a side-effect of the fact that security permission attributes aren’t “real” custom attributes in the sense that they’re not stored in the CustomAttribute metadata table, but rather in their own table – the DeclSecurity table.  Reflection tries to abstract this away from the callers of the CustomAttributeData APIs by including both real custom attributes and pseudo-custom attributes like declarative security in its return value.  However, it cannot abstract this detail away perfectly, which is why the constructor information that it normally provides for custom attributes is not provided for declarative security.

Specifically, the metadata format for declarative security blobs is structured in such a way that it is easy for the security system to quickly and lazily determine if a permission blob is needed during execution, and if so very quickly rehydrate the permission in question.  For instance, the format of the declarative security table allows us to avoid doing any work at all when a method has a declarative assert until the point that a demand actually hits the stack frame with that assert.  This optimization means that in full trust, you will never have to waste time building up permission objects that might never be used.  Similarly, the blog format is optimized to make it quick for us to build up that asserted permission set once a demand does hit the method in question – meaning that we don’t slow down stack walks any more than necessary.

Since the metadata format is optimized for this scenario, it’s not setup in such a way that you can trivially map it back to the exact constructor that might have been used in code in the first place.  In fact, in the general case, it’s actually impossible to do so.  If you really did want to reverse-engineer what the source declaration might have been for declarative security you could do so by reading out of the declarative security table, and having special knowledge about how each different type of permission can build up its internal state.

(Interestingly, you can also see this separation of declarative security from real custom attributes in the IL syntax.  Rather than using a .custom declaration for declarative security, as you would for a standard custom attribute, you use either a .permission or a .permissionset declaration.  ILDASM will show this if you look at the IL output of an assembly that uses declarative security).

Comments

  • Anonymous
    April 22, 2010
    In the general case it's actually not fully possible to get back to source representation.  For instance, HostProtectionAttributePermission doesn't encode the SecurityAction you give it in source - it always uses LinkDemand.  Of course, in that specific case, you probably wouldn't care as much. You could certainly imagine the reflection team adding a feature where they knew how to decode the permissions that ship in the runtime.  However, that wouldn't cover custom permissions which can be added by anybody, so you'd still have to deal with this case in general. I will, however, mention to them that this decoding feature has been requested. -Shawn

  • Anonymous
    May 02, 2010
    Hi shawnfa! I migrated an APTCA assembly to .NET 4 and found it impossible to apply level 2 security on it. Could you take a look at the problem I documented on http://stackoverflow.com/questions/2690291 ? Kind regards

  • Anonymous
    June 07, 2010
    hi shawnfa, Recently I was implementing a plug-in framework, based on security considerations, each plug-in were placed in a separate AppDomain, so that once a plug-in  occurs exception will not cause application crashes, but it's not works perfectly, my code as follow. public class AppDomainStartuper : MarshalByRefObject    {        public static void Start()        {            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);            AppDomain domain = AppDomain.CreateDomain("test");            domain.DoCallBack(new CrossAppDomainDelegate(() =>            {                Assembly asm = Assembly.LoadFrom(@"E:WorkspaceVisual Studio 2010ProjectsFirstApplicationFirstApplicationbinDebugFirstApplication.exe");                Type type = asm.GetType("FirstApplication.MainWindow");                Window win = (Window)Activator.CreateInstance(type);                win.Show();                AppDomain.CurrentDomain.UnhandledException += (sender, e) =>                {                    MessageBox.Show("UnhandledException");                };                AppDomain.CurrentDomain.FirstChanceException += (sender, e) =>                {                    MessageBox.Show("FirstChanceException");                };            }));        }        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)        {            MessageBox.Show("main error");        }    } if the firstapplication.exe in the new appdomain throws a exception, it alert "FirstChanceException" first, and then alert "main error", then, the application crashed. the UnhandledException in sub appdomain is not fired, but the main appdomain fired. I expect the UnhandledException is catched and it do not affect the main appdomain, and the application continue to run.

  • Anonymous
    March 07, 2012
    The comment has been removed