Maintaining LinkDemands across inheritance boundaries

Maintaining LinkDemands across inheritance boundaries

  • Comments 6

As you already know, LinkDemands can be dangerous. But if you do use them, you need to know how to use them correctly. One problem that happens is when people add (or fail to maintain) a LinkDemand to a derived class when one did (or did not) exist on the base class.

Let's say you have the following code:

    public class BaseClass

    {

        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]

        public void DoSomething()

        {

            // Do something here

        }

    }

    public class DerivedClass : BaseClass

    {

        public override void DoSomething()

        {

            // Do something else here...

 

            // Now call base class' implementation

            base.DoSomething();

        }

    }

Hopefully it is pretty clear that this represents a security risk: BaseClass has protected its method with a LinkDemand so that untrusted code cannot call it. Unfortunately, DerivedClass goes ahead and calls the method as part of its implementation of DoSomething, and in doing so will likely satisfy the LinkDemand. To fix this problem the author of DerivedClass needs to add the LinkDemand attribute to DerivedClass.DoSomething.

That one was easy. But what's wrong with this?

    public class BaseClass

    {

        public void DoSomething()

        {

            // Do something safe here

        }

    }

    public class DerivedClass : BaseClass

    {

        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]

        public override void DoSomething()

        {

            // Do something dangerous here

        }

    }

Surely it's OK since the DerivedClass is being more secure than the BaseClass?

The problem here is that LinkDemand checks are done as the calling method is being JITted. If you have a method in an untrusted piece of code that accepts a parameter of type BaseClass then it will be JITted without any problems since no LinkDemand will be done. The method can then be passed a DerivedClass instance (which wants the check to occur) but the check will never occur.

Taking advantage of this is easy:

    void DoSomethingHelper()

    {

        CallDoSomething(new DerivedClass());

    }

 

    void CallDoSomething(BaseClass bc)

    {

        bc.DoSomething();

    }

In this case, when the JITter compiles CallDoSomething all it knows is that you are calling BaseClass.DoSomething. It doesn't know that you are later going to pass in a DerivedClass instance as the parameter. In fact, in a more complicated scenario the JITter may not even know that DerivedClass exists -- it could be in an assembly that is dynamically loaded at some future point in time.

Obviously if you want to add a LinkDemand to your DerivedClass but you are not in control of BaseClass (and so can't add the LinkDemand to it) you are in a bit of a bind. Two reasonable approaches:

1)       Create a new method! If the base class' implementation of DoSomething was safe, you're probably abusing it by making the override dangerous

2)       Do a full demand; it will always work, and it's probably what you want anyway

And don't call me Shirley.

  • I don't understand how Full Demands work. In a future blog can you give you a run-down about how they work? In particular, I'm having trouble understanding the "deny" part of Full Demands. Can I demand that my assembly run without full trust, but that the callers code can run however it would like? How do I do that?
  • The best way to run without FullTrust is to add RequestRefuse declarative attributes to your assembly. See:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconrefusingpermissions.asp

    for more information.

    A Deny is used to temporarily terminate a stackwalk at the current method.
  • Thanks for the excellent explanation. Any chance you could tell us how you got that beautiful colorized text in for the code? I have been frustrated with that part of blogging.
  • I write my blogs in Word and then use a WordML -> HTML transform and a VSTO project to post to the blog web service.

    I really need to upload the final version some day... ;-)
  • Peter: Wouldn't making an optional request for non-unrestricted permissions (i.e.: [assembly: PermissionSet(SecurityAction.RequestOptional, Unrestricted=false)]) as per http://msdn.microsoft.com/library/en-us/cpguide/html/cpconpermissionrequests.asp?frame=true be a much simpler means of running without full trust?

    As with your earlier comment, most MS recommendations I've seen for refusing full trust go the RequestRefuse route. However, the setup for this is both more tedious and more error-prone, and it doesn't accomodate possible extensions to the available permission sets, so I have to wonder why it seems to be the favoured approach.

    Is the RequestRefuse route somehow safer or more reliable? Is there any documentation available comparing the two approaches?
  • Thanks for the great comment, Nicole.

    Please see my follow-up post at http://weblogs.asp.net/ptorr/archive/2004/07/01/170998.aspx
Page 1 of 1 (6 items)