File: CallbackWrapper.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (339 lines) | stat: -rw-r--r-- 13,603 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace System.Activities.Runtime
{
    using System;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
    using System.Security;
    using System.Security.Permissions;
    using System.Threading;

    [DataContract]
    class CallbackWrapper
    {
        static BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;

        static PermissionSet ReflectionMemberAccessPermissionSet = null;

        string callbackName;

        string declaringAssemblyName;

        string declaringTypeName;

        Delegate callback;

        ActivityInstance activityInstance;

        public CallbackWrapper(Delegate callback, ActivityInstance owningInstance)
        {
            this.ActivityInstance = owningInstance;
            this.callback = callback;
        }
        
        public ActivityInstance ActivityInstance
        {
            get
            {
                return this.activityInstance;
            }
            private set
            {
                this.activityInstance = value;
            }
        }

        protected bool IsCallbackNull
        {
            get
            {
                return this.callback == null && this.callbackName == null;
            }
        }

        protected Delegate Callback
        {
            get
            {
                return this.callback;
            }
        }

        [DataMember(Name = "callbackName")]
        internal string SerializedCallbackName
        {
            get { return this.callbackName; }
            set { this.callbackName = value; }
        }

        [DataMember(EmitDefaultValue = false, Name = "declaringAssemblyName")]
        internal string SerializedDeclaringAssemblyName
        {
            get { return this.declaringAssemblyName; }
            set { this.declaringAssemblyName = value; }
        }

        [DataMember(EmitDefaultValue = false, Name = "declaringTypeName")]
        internal string SerializedDeclaringTypeName
        {
            get { return this.declaringTypeName; }
            set { this.declaringTypeName = value; }
        }

        [DataMember(Name = "ActivityInstance")]
        internal ActivityInstance SerializedActivityInstance
        {
            get { return this.ActivityInstance; }
            set { this.ActivityInstance = value; }
        }

        public static bool IsValidCallback(Delegate callback, ActivityInstance owningInstance)
        {
            Fx.Assert(callback != null, "This should only be called with non-null callbacks");

            object target = callback.Target;

            // if the target is null, it is static 
            if (target == null)
            {
                Fx.Assert(callback.Method.IsStatic, "This method should be static when target is null");
                return true;
            }

            // its owner's activity
            if (object.ReferenceEquals(target, owningInstance.Activity))
            {
                return true;
            }

            return false;
        }

        // Special note about establishing callbacks:
        //
        // When establising a callback, we need to Assert ReflectionPermission(MemberAccess) because the callback
        // method will typically be a private method within a class that derives from Activity. Activity authors need
        // to be aware that their callback may be invoked with different permissions than were present when the Activity
        // was originally executed and perform appropriate security checks.
        //
        // We ensure that the declaring type of the callback method derives from Activity. This check is made in RecreateCallback.
        //
        // The classes that derive from CallbackWrapper and call EnsureCallback do an explicit cast of the returned delegate
        // to the delegate type that they expect before calling thru to the delegate. This cast is done in SecuritySafeCritical code.
        //
        // These checks are both made in Security[Safe]Critical code.

        [Fx.Tag.SecurityNote(Critical = "Because we are calling GenerateCallback, which are SecurityCritical.")]
        [SecurityCritical]
        protected void EnsureCallback(Type delegateType, Type[] parameterTypes, Type genericParameter)
        {
            // We were unloaded and have some work to do to rebuild the callback
            if (this.callback == null)
            {
                this.callback = GenerateCallback(delegateType, parameterTypes, genericParameter);
                Fx.Assert(this.callback != null, "GenerateCallback should have been able to produce a non-null callback.");
            }
        }

        [Fx.Tag.SecurityNote(Critical = "Because we are calling GenerateCallback, which are SecurityCritical.",
            Safe = "Because the delegate is not leaked out of this routine. It is only validated.")]
        [SecuritySafeCritical]
        protected void ValidateCallbackResolution(Type delegateType, Type[] parameterTypes, Type genericParameter)
        {
            Fx.Assert(this.callback != null && this.callbackName != null, "We must have a callback and a callback name");
            
            if (!this.callback.Equals(GenerateCallback(delegateType, parameterTypes, genericParameter)))
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidExecutionCallback(this.callback.Method, null)));
            }
        }

        MethodInfo FindMatchingGenericMethod(Type declaringType, Type[] parameterTypes, Type genericParameter)
        {
            MethodInfo[] potentialMatches = declaringType.GetMethods(bindingFlags);
            for (int i = 0; i < potentialMatches.Length; i++)
            {
                MethodInfo potentialMatch = potentialMatches[i];

                if (potentialMatch.IsGenericMethod && potentialMatch.Name == this.callbackName)
                {
                    Fx.Assert(potentialMatch.IsGenericMethodDefinition, "We should be getting the generic method definition here.");

                    Type[] genericArguments = potentialMatch.GetGenericArguments();

                    if (genericArguments.Length == 1)
                    {
                        potentialMatch = potentialMatch.MakeGenericMethod(genericParameter);

                        ParameterInfo[] parameters = potentialMatch.GetParameters();

                        bool match = true;
                        for (int parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++)
                        {
                            ParameterInfo parameter = parameters[parameterIndex];

                            if (parameter.IsOut || parameter.IsOptional || parameter.ParameterType != parameterTypes[parameterIndex])
                            {
                                match = false;
                                break;
                            }
                        }

                        if (match)
                        {
                            return potentialMatch;
                        }
                    }
                }
            }
            return null;
        }

        [Fx.Tag.SecurityNote(Critical = "Because we are calling RecreateCallback, which is SecurityCritical.")]
        [SecurityCritical]
        Delegate GenerateCallback(Type delegateType, Type[] parameterTypes, Type genericParameter)
        {
            Type declaringType;
            MethodInfo methodInfo = GetMatchingMethod(parameterTypes, out declaringType);

            if (methodInfo == null)
            {
                Fx.Assert(declaringType != null, "We must have found the declaring type.");
                methodInfo = FindMatchingGenericMethod(declaringType, parameterTypes, genericParameter);
            }

            if (methodInfo == null)
            {
                return null;
            }
            
            return RecreateCallback(delegateType, methodInfo);
        }

        [Fx.Tag.SecurityNote(Critical = "Because we are calling RecreateCallback, which is SecurityCritical.")]
        [SecurityCritical]
        protected void EnsureCallback(Type delegateType, Type[] parameters)
        {
            // We were unloaded and have some work to do to rebuild the callback
            if (this.callback == null)
            {
                Type unusedDeclaringType;
                MethodInfo methodInfo = GetMatchingMethod(parameters, out unusedDeclaringType);

                Fx.Assert(methodInfo != null, "We must have a method info by now");

                this.callback = RecreateCallback(delegateType, methodInfo);
            }
        }

        MethodInfo GetMatchingMethod(Type[] parameters, out Type declaringType)
        {
            Fx.Assert(this.callbackName != null, "This should only be called when there is actually a callback to run.");

            object targetInstance = this.ActivityInstance.Activity;

            if (this.declaringTypeName == null)
            {
                declaringType = targetInstance.GetType();
            }
            else
            {
                // make a MethodInfo since it's not hanging directly off of our activity type
                Assembly callbackAssembly;
                if (this.declaringAssemblyName != null)
                {
                    callbackAssembly = Assembly.Load(this.declaringAssemblyName);
                }
                else
                {
                    callbackAssembly = targetInstance.GetType().Assembly;
                }

                declaringType = callbackAssembly.GetType(this.declaringTypeName);
            }

            Fx.Assert(declaringType != null, "declaring type should be re-constructable from our serialized components");

            return declaringType.GetMethod(this.callbackName, bindingFlags, null, parameters, null);
        }

        // The MethodInfo passed to this method must be derived
        [Fx.Tag.SecurityNote(Critical = "Because we are Asserting ReflectionPermission(MemberAccess) in order to get at private callback methods.")]
        [SecurityCritical]
        Delegate RecreateCallback(Type delegateType, MethodInfo callbackMethod)
        {
            object targetInstance = null;

            // If the declaring type does not derive from Activity, somebody has manipulated the callback in the persistece store.
            if (!typeof(Activity).IsAssignableFrom(callbackMethod.DeclaringType))
            {
                return null;
            }

            if (!callbackMethod.IsStatic)
            {
                targetInstance = this.ActivityInstance.Activity;
            }

            // Asserting ReflectionPermission.MemberAccess because the callback method is most likely internal or private
            if (ReflectionMemberAccessPermissionSet == null)
            {
                PermissionSet myPermissionSet = new PermissionSet(PermissionState.None);
                myPermissionSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
                Interlocked.CompareExchange(ref ReflectionMemberAccessPermissionSet, myPermissionSet, null);
            }
            ReflectionMemberAccessPermissionSet.Assert();
            try
            {
                return Delegate.CreateDelegate(delegateType, targetInstance, callbackMethod);
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
        }

        [OnSerializing]
        [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters)]
        [SuppressMessage(FxCop.Category.Usage, "CA2238:ImplementSerializationMethodsCorrectly",
            Justification = "Needs to be internal for serialization in partial trust. We have set InternalsVisibleTo(System.Runtime.Serialization) to allow this.")]
        internal void OnSerializing(StreamingContext context)
        {
            if (this.callbackName == null && !this.IsCallbackNull)
            {
                MethodInfo method = this.callback.Method;
                this.callbackName = method.Name;
                Type declaringType = method.DeclaringType;
                Type activityType = this.ActivityInstance.Activity.GetType();

                if (declaringType != activityType)
                {
                    // If we're not directly off of the Activity type being used,
                    // then we need to store the declaringType's name.
                    this.declaringTypeName = declaringType.FullName;

                    if (declaringType.Assembly != activityType.Assembly)
                    {
                        this.declaringAssemblyName = declaringType.Assembly.FullName;
                    }
                }
                
                if (method.IsGenericMethod)
                {
                    OnSerializingGenericCallback();
                }
            }
        }
        
        protected virtual void OnSerializingGenericCallback()
        {
            // Generics are invalid by default
            throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidExecutionCallback(this.callback.Method, null)));
        }
    }
}