Merge branch 'sgen-disable-gc'
[mono.git] / mcs / tools / tuner / Mono.Tuner / InjectSecurityAttributes.cs
1 //
2 // InjectSecurityAttributes.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2009 Novell, Inc.
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27 //
28
29 using System;
30 using System.Collections;
31 using System.IO;
32 using System.Linq;
33 using System.Text;
34
35 using Mono.Linker;
36 using Mono.Linker.Steps;
37
38 using Mono.Cecil;
39
40 namespace Mono.Tuner {
41
42         public class InjectSecurityAttributes : BaseStep {
43
44                 enum TargetKind {
45                         Type,
46                         Method,
47                 }
48
49                 protected enum AttributeType {
50                         Critical,
51                         SafeCritical,
52                 }
53
54                 const string _safe_critical = "System.Security.SecuritySafeCriticalAttribute";
55                 const string _critical = "System.Security.SecurityCriticalAttribute";
56
57                 const string sec_attr_folder = "secattrs";
58
59                 protected AssemblyDefinition _assembly;
60
61                 MethodDefinition _safe_critical_ctor;
62                 MethodDefinition _critical_ctor;
63
64                 string data_folder;
65
66                 protected override bool ConditionToProcess ()
67                 {
68                         if (!Context.HasParameter (sec_attr_folder)) {
69                                 Console.Error.WriteLine ("Warning: no secattrs folder specified.");
70                                 return false;
71                         }
72
73                         data_folder = Context.GetParameter (sec_attr_folder);
74                         return true;
75                 }
76
77                 protected override void ProcessAssembly (AssemblyDefinition assembly)
78                 {
79                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
80                                 return;
81
82                         string secattr_file = Path.Combine (
83                                 data_folder,
84                                 assembly.Name.Name + ".secattr");
85
86                         if (!File.Exists (secattr_file)) {
87                                 Console.Error.WriteLine ("Warning: file '{0}' not found, skipping.", secattr_file);
88                                 return;
89                         }
90
91                         _assembly = assembly;
92
93                         // remove existing [SecurityCritical] and [SecuritySafeCritical]
94                         RemoveSecurityAttributes ();
95
96                         // add [SecurityCritical] and [SecuritySafeCritical] from the data file
97                         ProcessSecurityAttributeFile (secattr_file);
98                 }
99
100                 protected void RemoveSecurityAttributes ()
101                 {
102                         foreach (TypeDefinition type in _assembly.MainModule.Types) {
103                                 if (RemoveSecurityAttributes (type))
104                                         type.HasSecurity = false;
105
106                                 if (type.HasMethods) {
107                                         foreach (MethodDefinition method in type.Methods) {
108                                                 if (RemoveSecurityAttributes (method))
109                                                         method.HasSecurity = false;
110                                         }
111                                 }
112                         }
113                 }
114
115                 static bool RemoveSecurityDeclarations (ISecurityDeclarationProvider provider)
116                 {
117                         // also remove already existing CAS security declarations
118
119                         if (provider == null)
120                                 return false;
121
122                         if (!provider.HasSecurityDeclarations)
123                                 return false;
124
125                         provider.SecurityDeclarations.Clear ();
126                         return true;
127                 }
128
129                 static bool RemoveSecurityAttributes (ICustomAttributeProvider provider)
130                 {
131                         bool result = RemoveSecurityDeclarations (provider as ISecurityDeclarationProvider);
132
133                         if (!provider.HasCustomAttributes)
134                                 return result;
135
136                         var attributes = provider.CustomAttributes;
137                         for (int i = 0; i < attributes.Count; i++) {
138                                 CustomAttribute attribute = attributes [i];
139                                 switch (attribute.Constructor.DeclaringType.FullName) {
140                                 case _safe_critical:
141                                 case _critical:
142                                         attributes.RemoveAt (i--);
143                                         break;
144                                 }
145                         }
146                         return result;
147                 }
148
149                 void ProcessSecurityAttributeFile (string file)
150                 {
151                         using (StreamReader reader = File.OpenText (file)) {
152                                 string line;
153                                 while ((line = reader.ReadLine ()) != null)
154                                         ProcessLine (line);
155                         }
156                 }
157
158                 void ProcessLine (string line)
159                 {
160                         if (line == null || line.Length < 6)
161                                 return;
162
163                         int sep = line.IndexOf (": ");
164                         if (sep == -1)
165                                 return;
166
167                         string marker = line.Substring (0, sep);
168                         string target = line.Substring (sep + 2);
169
170                         ProcessSecurityAttributeEntry (
171                                 DecomposeAttributeType (marker),
172                                 DecomposeTargetKind (marker),
173                                 target);
174                 }
175
176                 static AttributeType DecomposeAttributeType (string marker)
177                 {
178                         if (marker.StartsWith ("SC"))
179                                 return AttributeType.Critical;
180                         else if (marker.StartsWith ("SSC"))
181                                 return AttributeType.SafeCritical;
182                         else
183                                 throw new ArgumentException ();
184                 }
185
186                 static TargetKind DecomposeTargetKind (string marker)
187                 {
188                         switch (marker [marker.Length - 1]) {
189                         case 'T':
190                                 return TargetKind.Type;
191                         case 'M':
192                                 return TargetKind.Method;
193                         default:
194                                 throw new ArgumentException ();
195                         }
196                 }
197
198                 void ProcessSecurityAttributeEntry (AttributeType type, TargetKind kind, string target)
199                 {
200                         ICustomAttributeProvider provider = GetTarget (kind, target);
201                         if (provider == null)
202                                 return;
203
204                         switch (type) {
205                         case AttributeType.Critical:
206                                 AddCriticalAttribute (provider);
207                                 break;
208                         case AttributeType.SafeCritical:
209                                 AddSafeCriticalAttribute (provider);
210                                 break;
211                         }
212                 }
213
214                 protected void AddCriticalAttribute (ICustomAttributeProvider provider)
215                 {
216                         // a [SecurityCritical] replaces a [SecuritySafeCritical]
217                         if (HasSecurityAttribute (provider, AttributeType.SafeCritical))
218                                 RemoveSecurityAttributes (provider);
219
220                         AddSecurityAttribute (provider, AttributeType.Critical);
221                 }
222
223                 void AddSafeCriticalAttribute (ICustomAttributeProvider provider)
224                 {
225                         // a [SecuritySafeCritical] is ignored if a [SecurityCritical] is present
226                         if (HasSecurityAttribute (provider, AttributeType.Critical))
227                                 return;
228
229                         AddSecurityAttribute (provider, AttributeType.SafeCritical);
230                 }
231
232                 void AddSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
233                 {
234                         if (HasSecurityAttribute (provider, type))
235                                 return;
236
237                         var attributes = provider.CustomAttributes;
238                         switch (type) {
239                         case AttributeType.Critical:
240                                 attributes.Add (CreateCriticalAttribute ());
241                                 break;
242                         case AttributeType.SafeCritical:
243                                 attributes.Add (CreateSafeCriticalAttribute ());
244                                 break;
245                         }
246                 }
247
248                 protected static bool HasSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
249                 {
250                         if (!provider.HasCustomAttributes)
251                                 return false;
252
253                         foreach (CustomAttribute attribute in provider.CustomAttributes) {
254                                 switch (attribute.Constructor.DeclaringType.Name) {
255                                 case _critical:
256                                         if (type == AttributeType.Critical)
257                                                 return true;
258
259                                         break;
260                                 case _safe_critical:
261                                         if (type == AttributeType.SafeCritical)
262                                                 return true;
263
264                                         break;
265                                 }
266                         }
267
268                         return false;
269                 }
270
271                 ICustomAttributeProvider GetTarget (TargetKind kind, string target)
272                 {
273                         switch (kind) {
274                         case TargetKind.Type:
275                                 return GetType (target);
276                         case TargetKind.Method:
277                                 return GetMethod (target);
278                         default:
279                                 throw new ArgumentException ();
280                         }
281                 }
282
283                 TypeDefinition GetType (string fullname)
284                 {
285                         return _assembly.MainModule.GetType (fullname);
286                 }
287
288                 MethodDefinition GetMethod (string signature)
289                 {
290                         int pos = signature.IndexOf (" ");
291                         if (pos == -1)
292                                 throw new ArgumentException ();
293
294                         string tmp = signature.Substring (pos + 1);
295
296                         pos = tmp.IndexOf ("::");
297                         if (pos == -1)
298                                 throw new ArgumentException ();
299
300                         string type_name = tmp.Substring (0, pos);
301
302                         int parpos = tmp.IndexOf ("(");
303                         if (parpos == -1)
304                                 throw new ArgumentException ();
305
306                         string method_name = tmp.Substring (pos + 2, parpos - pos - 2);
307
308                         TypeDefinition type = GetType (type_name);
309                         if (type == null)
310                                 return null;
311
312                         return GetMethod (type.Methods, signature);
313                 }
314
315                 static MethodDefinition GetMethod (IEnumerable methods, string signature)
316                 {
317                         foreach (MethodDefinition method in methods)
318                                 if (GetFullName (method) == signature)
319                                         return method;
320
321                         return null;
322                 }
323
324                 static string GetFullName (MethodReference method)
325                 {
326                         var sentinel = method.Parameters.FirstOrDefault (p => p.ParameterType.IsSentinel);
327                         var sentinel_pos = -1;
328                         if (sentinel != null)
329                                 sentinel_pos = method.Parameters.IndexOf (sentinel);
330
331                         StringBuilder sb = new StringBuilder ();
332                         sb.Append (method.ReturnType.FullName);
333                         sb.Append (" ");
334                         sb.Append (method.DeclaringType.FullName);
335                         sb.Append ("::");
336                         sb.Append (method.Name);
337                         if (method.HasGenericParameters) {
338                                 sb.Append ("<");
339                                 for (int i = 0; i < method.GenericParameters.Count; i++ ) {
340                                         if (i > 0)
341                                                 sb.Append (",");
342                                         sb.Append (method.GenericParameters [i].Name);
343                                 }
344                                 sb.Append (">");
345                         }
346                         sb.Append ("(");
347                         if (method.HasParameters) {
348                                 for (int i = 0; i < method.Parameters.Count; i++) {
349                                         if (i > 0)
350                                                 sb.Append (",");
351
352                                         if (i == sentinel_pos)
353                                                 sb.Append ("...,");
354
355                                         sb.Append (method.Parameters [i].ParameterType.FullName);
356                                 }
357                         }
358                         sb.Append (")");
359                         return sb.ToString ();
360                 }
361
362                 static MethodDefinition GetDefaultConstructor (TypeDefinition type)
363                 {
364                         foreach (MethodDefinition ctor in type.Methods.Where (m => m.IsConstructor))
365                                 if (ctor.Parameters.Count == 0)
366                                         return ctor;
367
368                         return null;
369                 }
370
371                 MethodDefinition GetSafeCriticalCtor ()
372                 {
373                         if (_safe_critical_ctor != null)
374                                 return _safe_critical_ctor;
375
376                         TypeDefinition safe_critical_type = Context.GetType (_safe_critical);
377                         if (safe_critical_type == null)
378                                 throw new InvalidOperationException (String.Format ("{0} type not found", _safe_critical));
379
380                         _safe_critical_ctor = GetDefaultConstructor (safe_critical_type);
381                         return _safe_critical_ctor;
382                 }
383
384                 MethodDefinition GetCriticalCtor ()
385                 {
386                         if (_critical_ctor != null)
387                                 return _critical_ctor;
388
389                         TypeDefinition critical_type = Context.GetType (_critical);
390                         if (critical_type == null)
391                                 throw new InvalidOperationException (String.Format ("{0} type not found", _critical));
392
393                         _critical_ctor = GetDefaultConstructor (critical_type);
394                         return _critical_ctor;
395                 }
396
397                 MethodReference Import (MethodDefinition method)
398                 {
399                         return _assembly.MainModule.Import (method);
400                 }
401
402                 CustomAttribute CreateSafeCriticalAttribute ()
403                 {
404                         return new CustomAttribute (Import (GetSafeCriticalCtor ()));
405                 }
406
407                 CustomAttribute CreateCriticalAttribute ()
408                 {
409                         return new CustomAttribute (Import (GetCriticalCtor ()));
410                 }
411         }
412 }