Dela via


LinkDemands and InheritenceDemands Occur at JIT Time

We previously saw that the SkipVerification demand for calling a method with unverifiable code occurs at JIT time rather than at runtime.  Two other types of demands also occur at JIT time, LinkDemands and InheritenceDemands.  An InheritenceDemand will occur when the method of the derived class is being JITed, while a LinkDemand occurs when the method which calls the method containing the LinkDemand is being JITed.  For instance, if method A calls method B, which has a LinkDemand for FullTrust, the demand occurs when A is JITed.  If method C overrides a method in class D which is protected with an InheritenceDemand, the demand occurs when C is JITed.

This has some interesting consequences.  Since the basic unit of JIT compilation is the method, you cannot have a single method which checks the permission set its running with, and then conditionally calls a method protected by a LinkDemand if if would satisfy that demand.  Instead, it would have to check it's permissions and if it had enough call another method which simply turned around and called the method with the LinkDemand.  Similarly, you cannot do a try ... catch(SecurityException) around a method protected by a LinkDemand.

I recently was asked a question about a program that stopped working when it was moved to a file share.  The author of the application attempted handle this gracefully by displaying an error message to his users indicating that they need to increase the trust of the application.  However, even that code didn't help -- it seemed as though the application's Main method never even got to execute.

It turns out that Main created a Mutex object, and the Mutex constructor contains a LinkDemand for UnmanagedCode.  Now that we know that this demand will occur at JIT time, the observed behavior seems understandable.  When the JIT attempted to compile Main, it found a call to a method that had a LinkDemand for UnmanagedCode, and so checked the permission set of the assembly containing Main.  It then found that this assembly did not have UnmanagedCode Permission, so it threw a SecurityException rather than compiling the method.  This prevented the author's attempt to fail gracefully from ever running, and the application died with an unhandled exception.  In fact, since this occurred while JITing Main, there actually wasn't even a call stack yet, so the exception message wasn't even particularly helpful in tracking down the problem.

One way to fix this is to have Main first do the check for FullTrust, then call another method which does the work that used to occur in the original Main method.  This way, the application's entry point is allowed to JIT, and it can provide a useful diagnostic message to the users in the case that they need to provide extra trust for the code.

Comments

  • Anonymous
    January 12, 2006
    With your suggested fix, surely the second method will fail to compile before Main is run and so the app will fail in the same way?

  • Anonymous
    January 12, 2006
    A method is only compiled when it is called for the first time. In the suggested fix, Main does it's check before the method call is done, and therefore the JIT never causes the SecurityException to be thrown.

    -Shawn

  • Anonymous
    January 04, 2007
    Great tip. The follow code makes my program pop a dialog when it's run from a shared drive. You have to set the app's entry point to ProgramWrapper.Main instead of default as it's usually set is the only catch. <code> static class ProgramWrapper { [STAThread] static void Main(string[] args) { try { Program.Main(args); } catch (System.Security.SecurityException) { MessageBox.Show("This application must be run from a secure location, such as a local disk."); } } } static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] public static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new BrowserSelectorForm(args)); } } </code>

  • Anonymous
    January 13, 2010
    The comment has been removed

  • Anonymous
    February 24, 2010
    "That assembly does not allow partially trusted callers" means that your assembly containing the Backup method needs to be marked with the AllowPartiallyTrustedCallers attribute - that should solve the problem for you. -Shawn