As a software engineer my greatest ambition is to produce code that will have a lasting impact on my fellow humans. I want to make good things happen to both good and bad people. The last thing I want to do is destabilize the runtime. Whether it be my runtime or your runtime, if you cant run and there is no time…hmmm…sounds kinda like death which of course has its own baggage and negative set of connotations.
So, you can now understand the horror I must have experienced yesterday morning when I got this:
Couple things worth noting here: “[No relevant source lines]”. Oh that’s helpful. Is the source so potentially harmful that the runtime has deemed the offending code “irrelevant.” Yeah? Well I think YOU are irrelevent .NET! I’m gonna put your worker threads in a warehouse and shut down your I/O completion ports. Minutes pass. Absolute silence. .Net remains unmoved by my threats and I realize that I must use what little intelligence I have to slog through this and figure it out for myself. The rest of this post is a narrative account of just that process. So sit back, reach for that glass of pinot and enjoy this tale of Verification Exceptions in Medium Trust on the .Net 2.0 runtime. Mmmm. You can tell already its gonna be good.
What is a Verification Exception
According to the official MSDN Library documentation, a VerificationException is “The exception that is thrown when the security policy requires code to be type safe and the verification process is unable to verify that the code is type safe.” Good enough for me. But for the obtuse, we will explore further.
Well unfortunately it doesn’t take much exploring to discover that there is not a lot of detailed explanation on this error and that it can also be raised by a large variety of very different scenarios. From what I can gather from all the fragments of blogs and forums I found that touched on various permutations of my scenario.
So what is my Scenario?
Occurs in .net 2.0 runtime under Medium Trust on x64
I have an assembly that I want to be able to run in .net 3.5 and up and in hosting environments that are restricted to Medium Trust. Here is the line that triggers this exception:
AuthorizedUserList = config == null || string.IsNullOrEmpty(config.AuthorizedUserList) ? Anonymous : config.AuthorizedUserList.Split(',').Length == 0 ? Anonymous : config.AuthorizedUserList.Split(',');
Its note worthy that variables AuthorizedUserList and Anonymous are both typed IEnumerable<string>.
This exception is only thrown when running this line on .net 3.5 in Medium trust. What I find particularly odd and don’t have an explanation for is that it worked fine in .net 3.5 Medium trust on my 32 bit machine but throws this exception on my 64 bit work laptop. I’m not convinced that it is the bitness level and not some other environment issue that makes the difference here. Sometimes things are just more fun when they remain a mystery. Especially in software don’t you think?
How to find the root of the problem
So looking at the above line of code my first reaction was that there was something wrong with the debug symbol mapping to the assembly I was using. I mean how does this line look harmful. Wrap it in fur and stuff it with cotton and you’d want to do no less than squeeze it close to your bosom and sing soft lullabies to it. So I proceeded to play with compiling configurations and changing references which proved entirely futile.
The golden nugget that I was missing was a tool that ships with the .net SDK called PEVerify. I made this connection reading this StackOverflow answer. One key thing to be aware of is that each version of the .NET runtime has its own version of PEVerify so make sure to use the one that ships with the version of the runtime you are getting this exception with. In my case I needed the .Net 2.0 SDK which you can find here.
PEVerify is a Command line utility that verifies that the IL in an assembly is type safe in a particular runtime environment. Why the .net 2 compiler cant report these as warnings, I do not know. So entering:
C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin>peverify "C:\RequestReduce\RequestReduce\bin\v3.5\debug\RequestReduce.dll" /verbose
I got this output:
Microsoft (R) .NET Framework PE Verifier. Version 2.0.50727.42Copyright (c) Microsoft Corporation. All rights reserved. [IL]: Error: [C:\RequestReduce\RequestReduce\bin\v3.5\debug\RequestReduce.dll :RequestReduce.Configuration.RRConfiguration::.ctor][mdToken=0x60000ce][offset 0x000000BB][found ref 'System.Collections.IEnumerable'][expected ref 'System.Collections.Generic.IEnumerable`1[System.String]'] Unexpected type on the stack.1 Error Verifying C:\RequestReduce\RequestReduce\bin\v3.5\debug\RequestReduce.dll
Ahhh. Its all making perfect sense now. Well if you think about it (a practice that I’m quite rusty with but sometimes still capable of), it does help. As I mentioned above and is illustrated here, AuthorizedUserList expects an IEnumerable<string>. The PEVerify output complains that it is getting a plain old IEnumerable (not its generic cousin). This does make sense since the config.AuthorizedUsers.Split(‘,’) returns a string.
Obviously under most circumstances, there will be no problem implicitly casting the string to an IEnumerable<string>. The code does run in .Net 2 and 4 in Full Trust and has never caused a problem. So .Net 2.0 must think that this conversion could potentially be un type safe and if the user is running in partial trust, the fact that its runtime type checking verification fails, causes this exception to be thrown.
Forcing a cast using ToList() fixes the problem
So adding nine characters to the end of the line fixes everything:
AuthorizedUserList = config == null || string.IsNullOrEmpty(config.AuthorizedUserList) ? Anonymous : config.AuthorizedUserList.Split(',').Length == 0 ? Anonymous : config.AuthorizedUserList.Split(',').ToList();
And now the output of PEVerify is:
C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin>peverify "C:\RequestReduce\RequestReduce\bin\v3.5\debug\RequestReduce.dll" /verbose Microsoft (R) .NET Framework PE Verifier. Version 2.0.50727.42Copyright (c) Microsoft Corporation. All rights reserved. All Classes and Methods in C:\RequestReduce\RequestReduce\bin\v3.5\debug\RequestReduce.dll Verified.
So that’s it. Hopefully someone will find this useful but from what I can tell from others who have experienced this same error, the causes can vary widely.
Looking back on this title, I’m wondering if the bark really is worse than the bite. I suppose the good news is that in the end, no runtimes were destabilized in the making of this blog post or the events that led up to it.