2 // InjectSecurityAttributes.cs
5 // Jb Evain (jbevain@novell.com)
7 // (C) 2009 Novell, Inc.
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
36 using Mono.Linker.Steps;
41 namespace Mono.Tuner {
43 public class InjectSecurityAttributes : BaseStep {
50 protected enum AttributeType {
55 const string _safe_critical = "System.Security.SecuritySafeCriticalAttribute";
56 const string _critical = "System.Security.SecurityCriticalAttribute";
57 const string _system_void = "System.Void";
59 const string sec_attr_folder = "secattrs";
61 protected AssemblyDefinition _assembly;
63 MethodDefinition _safe_critical_ctor;
64 MethodDefinition _critical_ctor;
65 TypeDefinition _void_type;
69 protected override bool ConditionToProcess ()
71 if (!Context.HasParameter (sec_attr_folder)) {
72 Console.Error.WriteLine ("Warning: no secattrs folder specified.");
76 data_folder = Context.GetParameter (sec_attr_folder);
80 protected override void ProcessAssembly (AssemblyDefinition assembly)
82 if (Annotations.GetAction (assembly) != AssemblyAction.Link)
85 string secattr_file = Path.Combine (
87 assembly.Name.Name + ".secattr");
89 if (!File.Exists (secattr_file)) {
90 Console.Error.WriteLine ("Warning: file '{0}' not found, skipping.", secattr_file);
96 // remove existing [SecurityCritical] and [SecuritySafeCritical]
97 RemoveSecurityAttributes ();
99 // add [SecurityCritical] and [SecuritySafeCritical] from the data file
100 ProcessSecurityAttributeFile (secattr_file);
103 protected void RemoveSecurityAttributes ()
105 foreach (TypeDefinition type in _assembly.MainModule.Types) {
106 if (RemoveSecurityAttributes (type))
107 type.HasSecurity = false;
109 if (type.HasMethods) {
110 foreach (MethodDefinition method in type.Methods) {
111 if (RemoveSecurityAttributes (method))
112 method.HasSecurity = false;
118 static bool RemoveSecurityDeclarations (ISecurityDeclarationProvider provider)
120 // also remove already existing CAS security declarations
122 if (provider == null)
125 if (!provider.HasSecurityDeclarations)
128 provider.SecurityDeclarations.Clear ();
132 static bool RemoveSecurityAttributes (ICustomAttributeProvider provider)
134 bool result = RemoveSecurityDeclarations (provider as ISecurityDeclarationProvider);
136 if (!provider.HasCustomAttributes)
139 var attributes = provider.CustomAttributes;
140 for (int i = 0; i < attributes.Count; i++) {
141 CustomAttribute attribute = attributes [i];
142 switch (attribute.Constructor.DeclaringType.FullName) {
145 attributes.RemoveAt (i--);
152 void ProcessSecurityAttributeFile (string file)
154 using (StreamReader reader = File.OpenText (file)) {
156 while ((line = reader.ReadLine ()) != null)
161 void ProcessLine (string line)
163 if (line == null || line.Length < 6 || line [0] == '#')
166 int sep = line.IndexOf (": ");
170 string marker = line.Substring (0, sep);
171 string target = line.Substring (sep + 2);
173 ProcessSecurityAttributeEntry (
174 DecomposeAttributeType (marker),
175 DecomposeTargetKind (marker),
179 static AttributeType DecomposeAttributeType (string marker)
181 if (marker.StartsWith ("SC"))
182 return AttributeType.Critical;
183 else if (marker.StartsWith ("SSC"))
184 return AttributeType.SafeCritical;
186 throw new ArgumentException ();
189 static TargetKind DecomposeTargetKind (string marker)
191 switch (marker [marker.Length - 1]) {
193 return TargetKind.Type;
195 return TargetKind.Method;
197 throw new ArgumentException ();
201 public static bool NeedsDefaultConstructor (TypeDefinition type)
203 if (type.IsInterface)
206 TypeReference base_type = type.BaseType;
207 if ((base_type == null) || (base_type.Namespace != "System"))
210 return ((base_type.Name != "Delegate") && (base_type.Name != "MulticastDelegate"));
213 void ProcessSecurityAttributeEntry (AttributeType type, TargetKind kind, string target)
215 ICustomAttributeProvider provider = GetTarget (kind, target);
216 if (provider == null) {
217 Console.Error.WriteLine ("Warning: entry '{0}' could not be found", target);
221 // we need to be smarter when applying the attributes (mostly SC) to types
222 if (kind == TargetKind.Type) {
223 TypeDefinition td = (provider as TypeDefinition);
224 // ensure [SecurityCritical] types (well most) have a default constructor
225 if ((type == AttributeType.Critical) && NeedsDefaultConstructor (td)) {
226 if (GetDefaultConstructor (td) == null) {
227 // Console.Error.WriteLine ("Info: adding default ctor for '{0}'", td);
228 td.Methods.Add (CreateDefaultConstructor ());
232 // it's easier for some tools (e.g. less false positives in fxcop)
233 // and also quicker for the runtime (one less lookup) if all methods gets decorated
234 foreach (MethodDefinition method in td.Methods) {
237 AttributeType mtype = type;
238 // there are cases where an SC cannot be applied to some methods
239 switch (method.Name) {
240 // e.g. everything we override from System.Object (which is transparent)
242 skip = method.Parameters.Count == 1 && method.Parameters [0].ParameterType.FullName == "System.Object";
247 skip = !method.HasParameters;
249 // e.g. some transparent interfaces, like IDisposable (implicit or explicit)
250 // downgrade some SC into SSC to respect the override/inheritance rules
251 case "System.IDisposable.Dispose":
253 skip = !method.HasParameters;
261 case AttributeType.Critical:
262 AddCriticalAttribute (method);
264 case AttributeType.SafeCritical:
265 AddSafeCriticalAttribute (method);
272 case AttributeType.Critical:
273 AddCriticalAttribute (provider);
275 case AttributeType.SafeCritical:
276 AddSafeCriticalAttribute (provider);
281 protected void AddCriticalAttribute (ICustomAttributeProvider provider)
283 // a [SecurityCritical] replaces a [SecuritySafeCritical]
284 if (HasSecurityAttribute (provider, AttributeType.SafeCritical))
285 RemoveSecurityAttributes (provider);
287 AddSecurityAttribute (provider, AttributeType.Critical);
290 void AddSafeCriticalAttribute (ICustomAttributeProvider provider)
292 // a [SecuritySafeCritical] is ignored if a [SecurityCritical] is present
293 if (HasSecurityAttribute (provider, AttributeType.Critical))
296 AddSecurityAttribute (provider, AttributeType.SafeCritical);
299 void AddSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
301 if (HasSecurityAttribute (provider, type))
304 var attributes = provider.CustomAttributes;
306 case AttributeType.Critical:
307 attributes.Add (CreateCriticalAttribute ());
309 case AttributeType.SafeCritical:
310 attributes.Add (CreateSafeCriticalAttribute ());
315 protected static bool HasSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
317 if (!provider.HasCustomAttributes)
320 foreach (CustomAttribute attribute in provider.CustomAttributes) {
321 switch (attribute.Constructor.DeclaringType.Name) {
323 if (type == AttributeType.Critical)
328 if (type == AttributeType.SafeCritical)
338 ICustomAttributeProvider GetTarget (TargetKind kind, string target)
341 case TargetKind.Type:
342 return GetType (target);
343 case TargetKind.Method:
344 return GetMethod (target);
346 throw new ArgumentException ();
350 TypeDefinition GetType (string fullname)
352 return _assembly.MainModule.GetType (fullname);
355 MethodDefinition GetMethod (string signature)
357 int pos = signature.IndexOf (" ");
359 throw new ArgumentException ();
361 string tmp = signature.Substring (pos + 1);
363 pos = tmp.IndexOf ("::");
365 throw new ArgumentException ();
367 string type_name = tmp.Substring (0, pos);
369 int parpos = tmp.IndexOf ("(");
371 throw new ArgumentException ();
373 string method_name = tmp.Substring (pos + 2, parpos - pos - 2);
375 TypeDefinition type = GetType (type_name);
379 return GetMethod (type.Methods, signature);
382 static MethodDefinition GetMethod (IEnumerable methods, string signature)
384 foreach (MethodDefinition method in methods)
385 if (GetFullName (method) == signature)
391 static string GetFullName (MethodReference method)
393 var sentinel = method.Parameters.FirstOrDefault (p => p.ParameterType.IsSentinel);
394 var sentinel_pos = -1;
395 if (sentinel != null)
396 sentinel_pos = method.Parameters.IndexOf (sentinel);
398 StringBuilder sb = new StringBuilder ();
399 sb.Append (method.ReturnType.FullName);
401 sb.Append (method.DeclaringType.FullName);
403 sb.Append (method.Name);
404 if (method.HasGenericParameters) {
406 for (int i = 0; i < method.GenericParameters.Count; i++ ) {
409 sb.Append (method.GenericParameters [i].Name);
414 if (method.HasParameters) {
415 for (int i = 0; i < method.Parameters.Count; i++) {
419 if (i == sentinel_pos)
422 sb.Append (method.Parameters [i].ParameterType.FullName);
426 return sb.ToString ();
429 static MethodDefinition GetDefaultConstructor (TypeDefinition type)
431 foreach (MethodDefinition ctor in type.Methods.Where (m => m.IsConstructor))
432 if (!ctor.IsStatic && !ctor.HasParameters)
438 MethodDefinition GetSafeCriticalCtor ()
440 if (_safe_critical_ctor != null)
441 return _safe_critical_ctor;
443 TypeDefinition safe_critical_type = Context.GetType (_safe_critical);
444 if (safe_critical_type == null)
445 throw new InvalidOperationException (String.Format ("{0} type not found", _safe_critical));
447 _safe_critical_ctor = GetDefaultConstructor (safe_critical_type);
448 return _safe_critical_ctor;
451 MethodDefinition GetCriticalCtor ()
453 if (_critical_ctor != null)
454 return _critical_ctor;
456 TypeDefinition critical_type = Context.GetType (_critical);
457 if (critical_type == null)
458 throw new InvalidOperationException (String.Format ("{0} type not found", _critical));
460 _critical_ctor = GetDefaultConstructor (critical_type);
461 return _critical_ctor;
464 TypeDefinition GetSystemVoid ()
466 if (_void_type != null)
469 _void_type = Context.GetType (_system_void);
470 if (_void_type == null)
471 throw new InvalidOperationException (String.Format ("{0} type not found", _system_void));
476 MethodReference Import (MethodDefinition method)
478 return _assembly.MainModule.Import (method);
481 CustomAttribute CreateSafeCriticalAttribute ()
483 return new CustomAttribute (Import (GetSafeCriticalCtor ()));
486 CustomAttribute CreateCriticalAttribute ()
488 return new CustomAttribute (Import (GetCriticalCtor ()));
491 MethodDefinition CreateDefaultConstructor ()
493 MethodDefinition method = new MethodDefinition (".ctor",
494 MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
496 method.Body.Instructions.Add (Instruction.Create (OpCodes.Ret));