none
LINQ lazy loading requires MemberAccess rather than RestrictedMemberAccess permission?! RRS feed

  • Question

  • Hi all,

     

    I understand that LINQ-to-SQL is supposed to require the RestrictedMemberAccess permission, rather than the full MemberAccess permission.

     

    However, in my tests, it is always requiring full member access.

     

    For instance, this code fails:

     

    Code Snippet

    using (var dc = new NorthwindDataContext())

    {

      var product = dc.Products.Where(p => p.ProductID == 1).Single();

     

      // reduce permissions to the supposed minimum for LINQ's lazy loading to work

      var permissionSet = new PermissionSet(PermissionState.None);

      permissionSet.AddPermission(new SqlClientPermission(PermissionState.Unrestricted));

      permissionSet.AddPermission(new ReflectionPermission

                                (ReflectionPermissionFlag.RestrictedMemberAccess));

     

      permissionSet.PermitOnly();

     

      // lazy load the product's supplier

      var supplier = product.Supplier; // <-- fails

    }

     

    If I change the second permission to ReflectionPermissionFlag.MemberAccess instead, then it works.

     

    Also, in another version of this test code, if drop the PermitOnly call, and simply Deny MemberAccess, then it fails.  I.e. with all rights except for MemberAccess, it fails.

     

    Am I missing something?  Is this a known bug?

     

    We are on .NET 3.5, and have not yet moved to the service pack.

     

    The call stack looks like this:

     

       at System.Runtime.CompilerServices.RuntimeHelpers._CompileMethod(IntPtr method)
       at System.Reflection.Emit.DynamicMethod.CreateDelegate(Type delegateType)
       at System.Data.Linq.Mapping.FieldAccessor.Create(Type objectType, FieldInfo fi)
       at System.Data.Linq.Mapping.AttributedMetaDataMember.MakeMemberAccessor(Type accessorType, MemberInfo mi, MetaAccessor storage)
       at System.Data.Linq.Mapping.AttributedMetaDataMember.InitAccessors()
       at System.Data.Linq.Mapping.AttributedMetaDataMember.get_StorageAccessor()
       at System.Data.Linq.CommonDataServices.DeferredSourceFactory`1.Execute(Object instance)
       at System.Data.Linq.CommonDataServices.DeferredSourceFactory`1.DeferredSource.GetEnumerator()
       at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
       at System.Data.Linq.EntityRef`1.get_Entity()
       at LinqSecTest.Product.get_Supplier() in C:\Work\Internal\LinqSecurityTest\LinqSecTest\DataClasses1.designer.cs:line 711
       at LinqSecTest.Form1.RunTest() in C:\Work\Internal\LinqSecurityTest\LinqSecTest\Form1.cs:line 41

     

    John


     

     

    Monday, September 1, 2008 4:24 AM

Answers

  • The problem is the permission set of the assembly that contains the code you are testing.  RestrictedMemberAccess permission permits code from one assembly to access internal/private members of code in another assembly if the permissions of the assembly being accessed are equivalent or weaker than the permissions currently available. (This keeps naughty user code from accessing internals of system libraries on host servers.) In your example, the Product and Supplier objects themselves are loaded into the default domain with full trust. When you reduce permissions to less than that and try to access these fields, then RestrictedMemberAccess fails.

     

    To try this out you'll need to package up your test code in a separate library, create an app domain with RestrictedMemberAccess permission and load the library into it.

     

    Tuesday, September 2, 2008 4:21 PM
    Moderator

All replies

  • P.S. The error is reproducable in a simple windows forms application running under FullTrust, so its nothing to do with running under ASP.NET.

    Monday, September 1, 2008 8:24 PM
  • How are you setting up the data context itself? Possibly change how linq accesses the db could be the key. Maybe pass in a connection string dynamically.
    Monday, September 1, 2008 9:09 PM
    Moderator
  • I've tried both.  Didn't help, sorry.

     

    John

    Tuesday, September 2, 2008 1:26 AM
  • The problem is the permission set of the assembly that contains the code you are testing.  RestrictedMemberAccess permission permits code from one assembly to access internal/private members of code in another assembly if the permissions of the assembly being accessed are equivalent or weaker than the permissions currently available. (This keeps naughty user code from accessing internals of system libraries on host servers.) In your example, the Product and Supplier objects themselves are loaded into the default domain with full trust. When you reduce permissions to less than that and try to access these fields, then RestrictedMemberAccess fails.

     

    To try this out you'll need to package up your test code in a separate library, create an app domain with RestrictedMemberAccess permission and load the library into it.

     

    Tuesday, September 2, 2008 4:21 PM
    Moderator
  • Thanks Matt. 

     

    You're saying that it works if the permissions of the assembly being accessed are less than or equal to the current permissions (according to the current callstack).  That explains it. 

     

    I had been confused by the documentation on RestrictedMemberAccess, which says "the grant set of the assembly that contains the non-public member that is being invoked must be equal to, or a subset of, the grant set of the invoking assembly".  I took that to mean that it only considered permissions granted to assemblies, and given that all my test code was in the same assembly, I expected it to work.  However, it doesn't just look at the permissions associated with the assemblies, right?  It also looks at any restrictions that are applied to the current call stack.

     

    Just out of interest, is the documentation here incorrect, or did I just misunderstand it? http://msdn.microsoft.com/en-us/library/system.security.permissions.reflectionpermissionattribute.restrictedmemberaccess.aspx 

     

    John

    Tuesday, September 2, 2008 7:25 PM
  • This simple code, in the same assembly, fails too:
    using System;  
    using System.Reflection;  
    using System.Security.Permissions;  
     
    namespace TestReflection35  
    {  
        static class Program  
        {  
            //Just to test if that "unrestricted" in the exception really means that full trust is required for MemberAccess.  
            //Test result: It doesn't: You can deny ReflectionEmit and allow MemberAccess, it works.  
            [ReflectionPermission(SecurityAction.Deny, ReflectionEmit=true)]  
            static void Main(string[] args)  
            {  
                Test();  
            }  
     
            [ReflectionPermission(SecurityAction.Deny, MemberAccess=true, RestrictedMemberAccess=false)]  
            [ReflectionPermission(SecurityAction.Demand, RestrictedMemberAccess=true)]  
            static void Test()  
            {  
                Thing obj = new Thing();  
                FieldInfo fi = typeof(Thing).GetField("someField", BindingFlags.Instance|BindingFlags.NonPublic);  
                Object o = fi.GetValue(obj);  
                int field = (int)o;  
                Console.WriteLine("Field value : " + field);  
            }  
        }  
     
        class Thing  
        {  
            private int someField;  
     
            public Thing()  
            {  
                someField = 42;  
            }  
        }  
    }  
     

    It doesn't work either when I remove the ReflectionEmit and MemberAccess rights at the assembly level rather than the function level.

    But when exporting the Thing class to another assembly whose rights I limit, it works.
    Thursday, January 22, 2009 2:46 PM
  • Well, it used  or seemed to work. Now I have two assemblies both targeting the .Net Framework 3.5, with the very same assembly-level restrictions, no method-level restriction at all, and it fails. What am I doing wrong?

     

    Thursday, January 29, 2009 2:26 PM