Friday, January 20, 2012

BadImageFormatException when call virtual methods from SPSecurity.RunWithElevatedPrivileges

Recently I faced with very strange problem. Consider the following console application which I made in order to reproduce the problem:

   1: namespace ConsoleApplication
   2: {
   3:     public class FooBase<T>
   4:     {
   5:         public virtual IEnumerable<T> GetAll()
   6:         {
   7:             return new List<T>();
   8:         }
   9:     }
  10:  
  11:     public class Foo : FooBase<int>
  12:     {
  13:         public override IEnumerable<int> GetAll()
  14:         {
  15:             IEnumerable<int> items = null;
  16:             SPSecurity.RunWithElevatedPrivileges(
  17:                 () =>
  18:                     {
  19:                         items = base.GetAll();
  20:                     });
  21:  
  22:             return items;
  23:         }
  24:     }
  25:  
  26:     class Program
  27:     {
  28:         static void Main(string[] args)
  29:         {
  30:             var foo = new Foo();
  31:             var items = foo.GetAll();
  32:         }
  33:     }
  34: }

If run this code BadImageFormatException will be thrown: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B).

At first I thought that I just build x86 application and double checked that Platform target is set to x64. Then goes strange thing: if you will move SPSecurity.RunWithElevatedPrivileges() outside the method Foo.GetAll() and enclose outer method call, exception will disappear and code will work as expected:

   1: namespace ConsoleApplication
   2: {
   3:     public class FooBase<T>
   4:     {
   5:         public virtual IEnumerable<T> GetAll()
   6:         {
   7:             return new List<T>();
   8:         }
   9:     }
  10:  
  11:     public class Foo : FooBase<int>
  12:     {
  13:         public override IEnumerable<int> GetAll()
  14:         {
  15:             IEnumerable<int> items = null;
  16: //            SPSecurity.RunWithElevatedPrivileges(
  17: //                () =>
  18: //                    {
  19:                         items = base.GetAll();
  20: //                    });
  21:  
  22:             return items;
  23:         }
  24:     }
  25:  
  26:     class Program
  27:     {
  28:         static void Main(string[] args)
  29:         {
  30:             var foo = new Foo();
  31:             SPSecurity.RunWithElevatedPrivileges(
  32:                 () =>
  33:                     {
  34:                         var items = foo.GetAll();
  35:                     });
  36:         }
  37:     }
  38: }

This code runs successfully. I checked stack trace of the exception from 1st example:

at ConsoleApplication.Foo.<>c__DisplayClass1.<GetAll>b__0()
at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2()
at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param)
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode)
at ConsoleApplication.Foo.GetAll() in C:\…\ConsoleApplication\ConsoleApplication\Program.cs:line 22
at ConsoleApplication.Program.Main(String[] args) in C:\…\ConsoleApplication\ConsoleApplication\Program.cs:line 40
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

As you can see problem comes from method of generated class <>c__DisplayClass1.<GetAll>b__0(). When I tried to open this method in Reflector, Reflector failed:

image

So compiler generated wrong code for us in first example? Problem seems to be related with vtable, because when I removed “virtual” keyword from base method GetAll(), code became working:

   1: namespace ConsoleApplication
   2: {
   3:     public class FooBase<T>
   4:     {
   5:         public IEnumerable<T> GetAll()
   6:         {
   7:             return new List<T>();
   8:         }
   9:     }
  10:  
  11:     public class Foo : FooBase<int>
  12:     {
  13:         public new IEnumerable<int> GetAll()
  14:         {
  15:             IEnumerable<int> items = null;
  16:             SPSecurity.RunWithElevatedPrivileges(
  17:                 () =>
  18:                     {
  19:                         items = base.GetAll();
  20:                     });
  21:  
  22:             return items;
  23:         }
  24:     }
  25:  
  26:     class Program
  27:     {
  28:         static void Main(string[] args)
  29:         {
  30:             var foo = new Foo();
  31:             var items = foo.GetAll();
  32:         }
  33:     }
  34: }

This problem is strange, looks like a bug. But I’m not currently sure is it caused by Sharepoint code, by compiler or by something else – will update this post once will have additional info.

2 comments:

  1. Hi alex
    Your issue is you try to use base keyword into an anonymous method (the on given to RunWithElevatedPrivilege method).
    The compiler make this into an anonymous class containing your code with base that as non sense into this newly class...
    Because I've a poor english you must look at this article that explain the same issue without Sharepoint into scope : http://blogs.msdn.com/b/ericlippert/archive/2005/11/14/why-are-base-class-calls-from-anonymous-delegates-nonverifiable.aspx
    Here is the code you want do but using an helper method instead of an anonymous method :
    namespace TestSPSecurity
    {
    class Program
    {
    public class FooBase
    {
    public virtual IEnumerable GetAll()
    {
    return new List();
    }
    }

    public class Foo : FooBase
    {
    IEnumerable items = null;

    public IEnumerable Items
    {
    get { return items; }
    set { items = value; }
    }

    public override IEnumerable GetAll()
    {
    SPSecurity.RunWithElevatedPrivileges(new SPSecurity.CodeToRunElevated(InternalGetAll));
    return Items;
    }

    private void InternalGetAll()
    {
    Items = base.GetAll();
    }
    }
    static void Main(string[] args)
    {
    var foo = new Foo();
    var items = foo.GetAll();
    }
    }
    }

    ReplyDelete
    Replies
    1. Hi Lionel,
      thank you for this link, seems like this is compiler problem (not Sharepoint). About helper method - yes I know this workaround, this is how I found the problem. I had 2 methods in the class (GetAll() and GetAll(categoryName)), second method called first method. SPSecurity.RunWithElevatedPrivileges() was in 2nd method and it worked. But when I moved it to the 1st parameterless method, it thrown BadImageFormatException. However it doesn't shows any warnings.

      Also another strange thing: after I changed return value of the virtual method from IEnumerable to int, it became working:
      namespace ConsoleApplication
      {
      public class FooBase
      {
      public virtual int GetAll()
      {
      return 1;
      }
      }

      public class Foo : FooBase
      {
      public override int GetAll()
      {
      int i = 0;
      SPSecurity.RunWithElevatedPrivileges(
      () =>
      {
      i = base.GetAll();
      });

      return i;
      }
      }

      class Program
      {
      static void Main(string[] args)
      {
      var foo = new Foo();
      var i = foo.GetAll();
      }
      }
      }

      I checked it in Reflector - in this case compiler generates helper method:
      public override int GetAll()
      {
      int i = 0;
      SPSecurity.RunWithElevatedPrivileges(delegate {
      i = this.<>n__FabricatedMethod3();
      i += 9;
      });
      return i;
      }


      [CompilerGenerated]
      private int <>n__FabricatedMethod3()
      {
      return base.GetAll();
      }

      And it explains why it works, but doesn't explains why it doesn't generate this helper method when return value is IEnumerable (it also works with object as return value, so the problem is not in using of value types).

      Link which you provided above says that there is no __nonvirtual__keyword in C# which is correct. However there is a workaround which allows to perform non-virtual calls to the base class in C# based on the dynamic methods. In one of my previous posts http://sadomovalex.blogspot.com/2011/12/use-telerik-rad-editor-lite-without.html I used this technique to exclude some of the base virtual methods calls from the calling chain.

      Delete