Serializing Permissions Across CLR Versions

An interesting question came up in the newsgroups today.  If you serialize a permissions set (either by calling ToXml().ToString() directly on the permission, or by converting to an XML Element), you’ll get permissions that look like this:

<IPermission class=”System.Security.Permissions.SecurityPermission, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=x86″ version=”1″ Flags=”Assertion, UnmanagedCode, Execution, ControlThread, ControlEvidence, ControlPolicy, SerializationFormatter, ControlDomainPolicy, ControlPrincipal, ControlAppDomain, RemotingConfiguration, Infrastructure, BindingRedirects” />

The first attribute of the permission contains the assembly qualified name of the permission, including the version number of mscorlib that the permission is located in.  Why doesn’t this cause problems when a permission is serialized in one version of the CLR, and restored in another?

The reason is called unification.  The CLR itself is tightly bound to the version of mscorlib that it was built with.  This is understandable since mscorlib contains many special types, including System.Object, System.String, etc.  In fact, the entire framework stack is pretty tightly bound together.  For instance, we don’t test the v1.0 System.Security.dll against the v1.1 runtime.

In order to allow this to work, starting with v1.1 of the CLR, even if an assembly is built against a different version of the framework, the CLR will load the version of the framework stack that it was built with.  So, if you have a v1.0 WinForms app running on the v1.1 runtime, you’ll actually get the v1.1 WinForms.  Alan Shi gives a good explanation of this in his Unification Policy blog entry.

Given the unification policy, you can easily see what happens here.  If I try to deserialize the above v1.0 SecurityPermission on v1.1 or v2.0 of the CLR, when the security system tells the loader to go load mscorlib, it will be unified to the current mscorlib, and the current version of the permission will be loaded.

If you want to prove to yourself that unification is the reason that this works, and there’s not some kind of special security system voodoo going on, try it with a custom permission you write.  Create a permission, serialize it, and then change the version number.  When you try to deserialize that, you’ll get an exception since your custom application will not be unified.

Comments (4)

  1. what the says:

    Woah. What you call "unification" is what I call "dll hell". I thought one of the great advantages to .net CLR was that code built with runtime x.y would always run with runtime x.y. If you start switching runtimes underneath me, I’m going to start having compatibility problems. Right?

  2. Shawn says:

    You’re getting side-by-side and unification confused. Side-by-side versions of the framework mean that if you build against v1.0 of the CLR, and your user has both v1.0 and v1.1 installed, your app will run against v1.0, including all of the v1.0 assemblies.

    Unification occurs within an application after the CLR is loaded. For instance, lets say that I build a library against v1.0 of the runtime. You’re building an app against v1.1 which uses my library. When you call into my library, I won’t get the v1.0 versions of the type library, instead I’ll be unified to the v1.1 versions. In this instance it’s actually a very good thing for you, since the v1.1 System.String is a completely different type (from the CLR’s perspective) than the v1.0 System.String.

    To summarize — side-by-side lets you choose the framework version to run on before your app starts. Unification keeps all the framework libraries consistent once your app is already running.


  3. Big Dawg says:

    Thanks for the clarification. However, it still seems to me that dll hell is lurking. If my library uses some feature in v1.0 that was modified in v1.1 due to a bugfix, security change, etc., it will break in your app but run fine in v1.0 applications. Right? So, it seems like there is a problem here.

  4. Shawn says:

    While what you say is true, remember that in order for that situation to occur, you have to provide a v1.0 library, and then I have to (knowing that your library targets v1.0) build my app for v1.1. Only then will your library be forced to run on the v1.1 framework. If I’m intentionally forcing your code to run on a different framework than it was built for, it’s my responsibility to test that it works there.

    Another way to think of it is like this. If I build a v1.0 app, and you want to run it but have only v1.1 installed, would you prefer that the application (which in all likelyhood will work fine on v1.1 since we try very hard to keep compatibility between versions) refuse to run until you download an older version of the framework?