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