Yesterday we talked about the CoreCLR security model, and how it is built upon the transparency model introduced in the v2.0 .NET Framework. The quick summary was that all Silverlight application code is transparent, and transparent code may only call other transparent code and safe critical code. With that in mind, lets take a look at figuring out how can tell which platform APIs fall into each category ... allowing us to know which APIs our applications are allowed to call.
In Silverlight, by default all code is transparent. (This is different from the desktop CLR, where all code is critical by default). For application code, that's the end of the story -- as we said yesterday, a fundamental tenant of the CoreCLR security model is that all application code is transparent.
Platform code can be either transparent, safe critical, or critical however. CoreCLR will detect platform code by knowing where the assembly is loaded from (platform assemblies must be loaded from the Silverlight installation directory), and also by checking the assembly's public key. Only assemblies signed with specific Microsoft's public keys will be considered platform assemblies; because of this public key requirement, no Silverlight application code can ever be considered a platform assembly.
We can tell if a method is security critical or transparent by looking at the attributes on the method in question (and on the class that contains the method). To simplify this discussion, I'll talk directly about attributes on methods, however if the class containing the method has one of the security attributes, then this attribute will apply to every method within the class.
If a method in a platform assembly is marked with the SecurityCriticalAttribute, this means that it contains critical code. Even if the method is public, no application code can call it since transparent code may never directly call into critical code. Any attempt to break this rule will lead to a MethodAccessException at runtime.
For example, the constructor for FileStream in the Silverlight 1.1 alpha bits (snipped below) is security critical which we can see from the SecurityCriticalAttribute on line 5:
1 .method public hidebysig specialname rtspecialname
2 instance void .ctor(string path,
3 valuetype System.IO.FileMode mode) cil managed
5 .custom instance void System.Security.SecurityCriticalAttribute::.ctor() = ( 01 00 00 00 )
7 // ...
Similarly, every method in the Marshal class is also critical, since the entire class is marked with the SecurityCriticalAttribute on line 4:
1 .class public abstract auto ansi sealed beforefieldinit System.Runtime.InteropServices.Marshal
2 extends System.Object
4 .custom instance void System.Security.SecurityCriticalAttribute::.ctor() = ( 01 00 00 00 )
6 // ...
7 } // end of class System.Runtime.InteropServices.Marshal
A method in a platform assembly marked with the SecuritySafeCriticalAttribute is, of course, safe critical. Application code may call into these methods, since transparent code may call safe critical code. And since safe critical code is critical, it can then turn around and call critical APIs on behalf of the application.
One example of a safe critical API is the IsolatedStorageFileStream.Write, which we can see on line 6 of its disassembly:
1 .method public hidebysig virtual instance void
2 Write(uint8 buffer,
3 int32 offset,
4 int32 count) cil managed
6 .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 )
8 // ...
An interesting note is that the SecuritySafeCriticalAttribute is really just a combination of the desktop framework's SecurityCriticalAttribute and SecurityTreatAsSafeAttribute. If you're familiar with the desktop CLR's transparency model, then you'll know that the TreatAsSafe attribute is implied for all public critical methods. This is not true in the CoreCLR security model for Silverlight -- instead, safe critical methods must be explicitly marked.
Since there are no more implicit treat as safe methods, the need to explicitly annotate a method with both the SecurityCritical and the SecurityTreatAsSafe attributes became much more common, so SecuritySafeCritical was introduced as a shortcut; it saves time when typing and also reduces metadata size. However, if you are familiar with the desktop CLR transparency model, it's safe to read "SecuritySafeCritical" as "SecurityCritical, SecurityTreatAsSafe" when reasoning about an API set.
If a method has neither the SecurityCritical attribute nor the SecuritySafeCritical attribute applied to it, then the method must be transparent (since we said above that all code is transparent by default). If this method is visible to the application code, that means that the application can call into it because transparent code is always allowed to call other transparent code.
Incidentally, if a Silverlight application tries to be tricky and mark itself with one of the SecurityCritical or SecuritySafeCritical attributes nothing will happen. The CLR knows that application code must be transparent, so it doesn't even bother to look for those two attributes when examining a method from a non-platform assembly. Instead, the method with the disallowed attributes will continue to be treated as transparent, and if they try to do anything that is not allowed from a transparent method, an exception will be thrown.
Finally, it's important to note that these rules do not replace the standard public / private / protected / internal access rules for the runtime; instead they supplement them. So application code may not call an internal method in a platform assembly, even if the method is transparent.
|Security Attribute||Means||Directly callable by application code|
(if the method is visible)
Next time, we'll look at how this security model interacts with inheritance.