1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.Runtime
6 {
7     using System.Diagnostics;
8     using System.Runtime.Diagnostics;
9     using System.Security;
10     using System.Threading;
11 
12     abstract class ActionItem
13     {
14 #if FEATURE_COMPRESSEDSTACK
15         [Fx.Tag.SecurityNote(Critical = "Stores the security context, used later in binding back into")]
16         [SecurityCritical]
17         SecurityContext context;
18 #endif
19         bool isScheduled;
20 
21         bool lowPriority;
22 
ActionItem()23         protected ActionItem()
24         {
25         }
26 
27         public bool LowPriority
28         {
29             get
30             {
31                 return this.lowPriority;
32             }
33             protected set
34             {
35                 this.lowPriority = value;
36             }
37         }
38 
Schedule(Action<object> callback, object state)39         public static void Schedule(Action<object> callback, object state)
40         {
41             Schedule(callback, state, false);
42         }
43         [Fx.Tag.SecurityNote(Critical = "Calls into critical method ScheduleCallback",
44             Safe = "Schedule invoke of the given delegate under the current context")]
45         [SecuritySafeCritical]
Schedule(Action<object> callback, object state, bool lowPriority)46         public static void Schedule(Action<object> callback, object state, bool lowPriority)
47         {
48             Fx.Assert(callback != null, "A null callback was passed for Schedule!");
49 
50             if (PartialTrustHelpers.ShouldFlowSecurityContext ||
51                 WaitCallbackActionItem.ShouldUseActivity ||
52                 Fx.Trace.IsEnd2EndActivityTracingEnabled)
53             {
54                 new DefaultActionItem(callback, state, lowPriority).Schedule();
55             }
56             else
57             {
58                 ScheduleCallback(callback, state, lowPriority);
59             }
60         }
61 
62         [Fx.Tag.SecurityNote(Critical = "Called after applying the user context on the stack or (potentially) " +
63             "without any user context on the stack")]
64         [SecurityCritical]
Invoke()65         protected abstract void Invoke();
66 
67         [Fx.Tag.SecurityNote(Critical = "Access critical field context and critical property " +
68             "CallbackHelper.InvokeWithContextCallback, calls into critical method " +
69             "PartialTrustHelpers.CaptureSecurityContextNoIdentityFlow, calls into critical method ScheduleCallback; " +
70             "since the invoked method and the capturing of the security contex are de-coupled, can't " +
71             "be treated as safe")]
72         [SecurityCritical]
Schedule()73         protected void Schedule()
74         {
75             if (isScheduled)
76             {
77                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled));
78             }
79 
80             this.isScheduled = true;
81 #if FEATURE_COMPRESSEDSTACK
82             if (PartialTrustHelpers.ShouldFlowSecurityContext)
83             {
84                 this.context = PartialTrustHelpers.CaptureSecurityContextNoIdentityFlow();
85             }
86             if (this.context != null)
87             {
88                 ScheduleCallback(CallbackHelper.InvokeWithContextCallback);
89             }
90             else
91 #endif
92             {
93                 ScheduleCallback(CallbackHelper.InvokeWithoutContextCallback);
94             }
95         }
96 #if FEATURE_COMPRESSEDSTACK
97         [Fx.Tag.SecurityNote(Critical = "Access critical field context and critical property " +
98             "CallbackHelper.InvokeWithContextCallback, calls into critical method ScheduleCallback; " +
99             "since nothing is known about the given context, can't be treated as safe")]
100         [SecurityCritical]
ScheduleWithContext(SecurityContext context)101         protected void ScheduleWithContext(SecurityContext context)
102         {
103             if (context == null)
104             {
105                 throw Fx.Exception.ArgumentNull("context");
106             }
107             if (isScheduled)
108             {
109                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled));
110             }
111 
112             this.isScheduled = true;
113             this.context = context.CreateCopy();
114             ScheduleCallback(CallbackHelper.InvokeWithContextCallback);
115         }
116 #endif
117 
118         [Fx.Tag.SecurityNote(Critical = "Access critical property CallbackHelper.InvokeWithoutContextCallback, " +
119             "Calls into critical method ScheduleCallback; not bound to a security context")]
120         [SecurityCritical]
ScheduleWithoutContext()121         protected void ScheduleWithoutContext()
122         {
123             if (isScheduled)
124             {
125                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled));
126             }
127 
128             this.isScheduled = true;
129             ScheduleCallback(CallbackHelper.InvokeWithoutContextCallback);
130         }
131 
132         [Fx.Tag.SecurityNote(Critical = "Calls into critical methods IOThreadScheduler.ScheduleCallbackNoFlow, " +
133             "IOThreadScheduler.ScheduleCallbackLowPriNoFlow")]
134         [SecurityCritical]
ScheduleCallback(Action<object> callback, object state, bool lowPriority)135         static void ScheduleCallback(Action<object> callback, object state, bool lowPriority)
136         {
137             Fx.Assert(callback != null, "Cannot schedule a null callback");
138             if (lowPriority)
139             {
140                 IOThreadScheduler.ScheduleCallbackLowPriNoFlow(callback, state);
141             }
142             else
143             {
144                 IOThreadScheduler.ScheduleCallbackNoFlow(callback, state);
145             }
146         }
147 #if FEATURE_COMPRESSEDSTACK
148         [Fx.Tag.SecurityNote(Critical = "Extract the security context stored and reset the critical field")]
149         [SecurityCritical]
ExtractContext()150         SecurityContext ExtractContext()
151         {
152             Fx.Assert(this.context != null, "Cannot bind to a null context; context should have been set by now");
153             Fx.Assert(this.isScheduled, "Context is extracted only while the object is scheduled");
154             SecurityContext result = this.context;
155             this.context = null;
156             return result;
157         }
158 #endif
159         [Fx.Tag.SecurityNote(Critical = "Calls into critical static method ScheduleCallback")]
160         [SecurityCritical]
ScheduleCallback(Action<object> callback)161         void ScheduleCallback(Action<object> callback)
162         {
163             ScheduleCallback(callback, this, this.lowPriority);
164         }
165 
166         [SecurityCritical]
167         static class CallbackHelper
168         {
169 #if FEATURE_COMPRESSEDSTACK
170             [Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
171             static Action<object> invokeWithContextCallback;
172 #endif
173             [Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
174             static Action<object> invokeWithoutContextCallback;
175             [Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
176             static ContextCallback onContextAppliedCallback;
177 
178 #if FEATURE_COMPRESSEDSTACK
179             [Fx.Tag.SecurityNote(Critical = "Provides access to a critical field; Initialize it with " +
180                 "a delegate to a critical method")]
181             public static Action<object> InvokeWithContextCallback
182             {
183                 get
184                 {
185                     if (invokeWithContextCallback == null)
186                     {
187                         invokeWithContextCallback = new Action<object>(InvokeWithContext);
188                     }
189                     return invokeWithContextCallback;
190                 }
191             }
192 #endif
193             [Fx.Tag.SecurityNote(Critical = "Provides access to a critical field; Initialize it with " +
194                 "a delegate to a critical method")]
195             public static Action<object> InvokeWithoutContextCallback
196             {
197                 get
198                 {
199                     if (invokeWithoutContextCallback == null)
200                     {
201                         invokeWithoutContextCallback = new Action<object>(InvokeWithoutContext);
202                     }
203                     return invokeWithoutContextCallback;
204                 }
205             }
206             [Fx.Tag.SecurityNote(Critical = "Provides access to a critical field; Initialize it with " +
207                 "a delegate to a critical method")]
208             public static ContextCallback OnContextAppliedCallback
209             {
210                 get
211                 {
212                     if (onContextAppliedCallback == null)
213                     {
214                         onContextAppliedCallback = new ContextCallback(OnContextApplied);
215                     }
216                     return onContextAppliedCallback;
217                 }
218             }
219 #if FEATURE_COMPRESSEDSTACK
220             [Fx.Tag.SecurityNote(Critical = "Called by the scheduler without any user context on the stack")]
InvokeWithContext(object state)221             static void InvokeWithContext(object state)
222             {
223                 SecurityContext context = ((ActionItem)state).ExtractContext();
224                 SecurityContext.Run(context, OnContextAppliedCallback, state);
225             }
226 #endif
227             [Fx.Tag.SecurityNote(Critical = "Called by the scheduler without any user context on the stack")]
InvokeWithoutContext(object state)228             static void InvokeWithoutContext(object state)
229             {
230                 ((ActionItem)state).Invoke();
231                 ((ActionItem)state).isScheduled = false;
232             }
233             [Fx.Tag.SecurityNote(Critical = "Called after applying the user context on the stack")]
OnContextApplied(object o)234             static void OnContextApplied(object o)
235             {
236                 ((ActionItem)o).Invoke();
237                 ((ActionItem)o).isScheduled = false;
238             }
239         }
240 
241         class DefaultActionItem : ActionItem
242         {
243             [Fx.Tag.SecurityNote(Critical = "Stores a delegate that will be called later, at a particular context")]
244             [SecurityCritical]
245             Action<object> callback;
246             [Fx.Tag.SecurityNote(Critical = "Stores an object that will be passed to the delegate that will be " +
247                 "called later, at a particular context")]
248             [SecurityCritical]
249             object state;
250 
251             bool flowLegacyActivityId;
252             Guid activityId;
253             EventTraceActivity eventTraceActivity;
254 
255             [Fx.Tag.SecurityNote(Critical = "Access critical fields callback and state",
256                 Safe = "Doesn't leak information or resources")]
257             [SecuritySafeCritical]
DefaultActionItem(Action<object> callback, object state, bool isLowPriority)258             public DefaultActionItem(Action<object> callback, object state, bool isLowPriority)
259             {
260                 Fx.Assert(callback != null, "Shouldn't instantiate an object to wrap a null callback");
261                 base.LowPriority = isLowPriority;
262                 this.callback = callback;
263                 this.state = state;
264                 if (WaitCallbackActionItem.ShouldUseActivity)
265                 {
266                     this.flowLegacyActivityId = true;
267                     this.activityId = EtwDiagnosticTrace.ActivityId;
268                 }
269                 if (Fx.Trace.IsEnd2EndActivityTracingEnabled)
270                 {
271                     this.eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate();
272                     if (TraceCore.ActionItemScheduledIsEnabled(Fx.Trace))
273                     {
274                         TraceCore.ActionItemScheduled(Fx.Trace, this.eventTraceActivity);
275                     }
276                 }
277 
278             }
279 
280             [Fx.Tag.SecurityNote(Critical = "Implements a the critical abstract ActionItem.Invoke method, " +
281                 "Access critical fields callback and state")]
282             [SecurityCritical]
Invoke()283             protected override void Invoke()
284             {
285                 if (this.flowLegacyActivityId || Fx.Trace.IsEnd2EndActivityTracingEnabled)
286                 {
287                     TraceAndInvoke();
288                 }
289                 else
290                 {
291                     this.callback(this.state);
292                 }
293             }
294 
295             [Fx.Tag.SecurityNote(Critical = "Implements a the critical abstract Trace method, " +
296                     "Access critical fields callback and state")]
297             [SecurityCritical]
TraceAndInvoke()298             void TraceAndInvoke()
299             {
300                 //
301                 if (this.flowLegacyActivityId)
302                 {
303                     Guid currentActivityId = EtwDiagnosticTrace.ActivityId;
304                     try
305                     {
306                         EtwDiagnosticTrace.ActivityId = this.activityId;
307                         this.callback(this.state);
308                     }
309                     finally
310                     {
311                         EtwDiagnosticTrace.ActivityId = currentActivityId;
312                     }
313                 }
314                 else
315                 {
316                     Guid previous = Guid.Empty;
317                     bool restoreActivityId = false;
318                     try
319                     {
320                         if (this.eventTraceActivity != null)
321                         {
322                             previous = Trace.CorrelationManager.ActivityId;
323                             restoreActivityId = true;
324                             Trace.CorrelationManager.ActivityId = this.eventTraceActivity.ActivityId;
325                             if (TraceCore.ActionItemCallbackInvokedIsEnabled(Fx.Trace))
326                             {
327                                 TraceCore.ActionItemCallbackInvoked(Fx.Trace, this.eventTraceActivity);
328                             }
329                         }
330                         this.callback(this.state);
331                     }
332                     finally
333                     {
334                         if (restoreActivityId)
335                         {
336                             Trace.CorrelationManager.ActivityId = previous;
337                         }
338                     }
339                 }
340             }
341         }
342     }
343 }
344