public bool Rewrite(Instruction instruction, MemberRewriterContext context) { var il = context.IL; var method = ((MethodReference)instruction.Operand).Resolve(); if (method.ReturnType.FullName != typeof(string).FullName) { return(false); } var getLength = il.Body.Method.Module.ImportReference( method.ReturnType.Resolve().GetProperty(nameof(string.Length)).GetMethod ); var pop = il.Create(OpCodes.Pop); context.IL.InsertAfter(instruction, il.Create(OpCodes.Dup), il.Create(OpCodes.Dup), il.Create(OpCodes.Brfalse, pop), il.CreateCall(getLength), il.CreateLdlocBest(context.RuntimeGuardVariable), il.CreateCall(context.RuntimeGuardReferences.FlowThroughGuardCountIntPtrMethod), pop ); return(true); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) { var method = (MethodReference)instruction.Operand; var il = context.IL; if (method.Parameters.Count == 1) { var enumerableType = method.Parameters[0].ParameterType.ResolveGenericParameters(method as GenericInstanceMethod, method.DeclaringType as GenericInstanceType); if (!IsIEnumerable(enumerableType)) { return(false); } InsertEnumerableGuard(instruction, context, enumerableType); return(true); } var parameterTypes = new TypeReference[method.Parameters.Count]; var hasIEnumerable = false; for (var i = 0; i < method.Parameters.Count; i++) { var parameterType = method.Parameters[i].ParameterType.ResolveGenericParameters(method as GenericInstanceMethod, method.DeclaringType as GenericInstanceType); if (IsIEnumerable(parameterType)) { hasIEnumerable = true; } parameterTypes[i] = parameterType; } if (!hasIEnumerable) { return(false); } // we need to eat the stack and put it somewhere (vars), so that we can // process the enumerables without messing up other values var variables = new VariableDefinition[method.Parameters.Count]; for (var i = method.Parameters.Count - 1; i >= 0; i--) { var variable = new VariableDefinition(parameterTypes[i]); variables[i] = variable; il.Body.Variables.Add(variable); il.InsertBeforeAndRetargetJumps(instruction, il.CreateStlocBest(variable)); // technically we don't have to eat last parameter if it's not enumerable, // but trying to keep things simple } foreach (var variable in variables) { il.InsertBefore(instruction, il.CreateLdlocBest(variable)); if (IsIEnumerable(variable.VariableType)) { InsertEnumerableGuard(instruction, context, variable.VariableType); } } return(true); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) { var writeWarning = context.IL.Body.Method.Module.ImportReference(WriteWarningMethod); var ldstr = context.IL.Create(OpCodes.Ldstr, _warning); context.IL.InsertAfter(instruction, ldstr); context.IL.InsertAfter(ldstr, context.IL.CreateCall(writeWarning)); return(true); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) { var il = context.IL; il.InsertAfter(instruction, il.CreateLdlocBest(context.RuntimeGuardVariable), il.Create(OpCodes.Ldc_I8, 1L), il.CreateCall(context.RuntimeGuardReferences.GuardCountMethod) ); return(true); }
private void InsertEnumerableGuard(Instruction instruction, MemberRewriterContext context, TypeReference enumerableType) { var il = context.IL; var elementType = ((GenericInstanceType)enumerableType).GenericArguments[0]; var guardMethodInstance = new GenericInstanceMethod(context.RuntimeGuardReferences.FlowThroughGuardEnumerableCollectedMethod) { GenericArguments = { elementType } }; il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(context.RuntimeGuardVariable)); il.InsertBefore(instruction, il.CreateCall(guardMethodInstance)); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) { var il = context.IL; var method = ((MethodReference)instruction.Operand).Resolve(); var collectedType = (method.IsConstructor) ? method.DeclaringType : method.ReturnType; var collect = new GenericInstanceMethod(context.RuntimeGuardReferences.FlowThroughCollectDisposableMethod) { GenericArguments = { il.Body.Method.Module.ImportReference(collectedType) } }; il.InsertAfter(instruction, il.CreateLdlocBest(context.RuntimeGuardVariable), il.CreateCall(collect) ); return(true); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) { var method = ((MethodReference)instruction.Operand).Resolve(); if (!MethodDetailsByMetadataToken.Value.TryGetValue(method.MetadataToken.ToInt32(), out var details)) { throw new AssemblyGuardException($"Regex method {method.Name} is not allowed."); } if (details.ReasonIfNotAllowed != null) { throw new AssemblyGuardException(details.ReasonIfNotAllowed); } if (details.HasRegexOptionsAsLastParameter) { var guardRegexOptions = context.Module.ImportReference(KnownMethods.GuardRegexOptions); context.IL.InsertBeforeAndRetargetJumps(instruction, context.IL.CreateCall(guardRegexOptions)); } if (!method.IsStatic && !method.IsConstructor) { return(true); } if (details.MethodWithTimeout == null) { throw new AssemblyGuardException($"Could not find a timeout variant for this overload of Regex.{method.Name}. Please try another overload."); } if (details.MethodWithTimeoutNeedsRegexOptions) { // 0 = RegexOptions.None context.IL.InsertBeforeAndRetargetJumps(instruction, context.IL.Create(OpCodes.Ldc_I4_0)); } var getTimeUntilLimit = context.Module.ImportReference(KnownMethods.GetTimeUntilLimit); context.IL.InsertBeforeAndRetargetJumps(instruction, context.IL.CreateLdlocBest(context.RuntimeGuardVariable)); context.IL.InsertBefore(instruction, context.IL.CreateCall(getTimeUntilLimit)); var importedTimeoutMethod = context.Module.ImportReference(details.MethodWithTimeout); instruction.Operand = importedTimeoutMethod; return(true); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) { var method = ((MethodReference)instruction.Operand).Resolve(); var countParameter = GetCountParameter(method); if (countParameter == null) { return(false); } if (countParameter.Index < method.Parameters.Count - 1) { throw new NotSupportedException($"{nameof(CountArgumentRewriter)} does not support method {method} because count is not the last argument."); } var il = context.IL; il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(context.RuntimeGuardVariable)); il.InsertBefore(instruction, il.CreateCall(context.RuntimeGuardReferences.FlowThroughGuardCountInt32Method)); return(true); }
public bool Rewrite(Instruction instruction, MemberRewriterContext context) { var il = context.IL; var method = ((MethodReference)instruction.Operand).Resolve(); if (!method.ReturnType.IsArray) { return(false); } var dup = il.Create(OpCodes.Dup); var ldlen = il.Create(OpCodes.Ldlen); var ldloc = il.CreateLdlocBest(context.RuntimeGuardVariable); var call = il.CreateCall(context.RuntimeGuardReferences.FlowThroughGuardCountIntPtrMethod); var pop = il.Create(OpCodes.Pop); context.IL.InsertAfter(instruction, dup); context.IL.InsertAfter(dup, ldlen); context.IL.InsertAfter(ldlen, ldloc); context.IL.InsertAfter(ldloc, call); context.IL.InsertAfter(call, pop); return(true); }
bool IMemberRewriterInternal.Rewrite(Instruction instruction, MemberRewriterContext context) => true;
private static void ValidateAndRewriteMethod(MethodDefinition method, RuntimeGuardReferences guard, AssemblyValidator validator, AssemblyGuardSettings settings) { if (method.DeclaringType == guard.InstanceField.DeclaringType) { return; } validator.ValidateDefinition(method); if (!method.HasBody) { return; } if (method.Body.Instructions.Count == 0) { return; // weird, but happens with 'extern' } var isStaticConstructor = method.Name == ".cctor" && method.IsStatic && method.IsRuntimeSpecialName; var il = method.Body.GetILProcessor(); var guardVariable = new VariableDefinition(guard.InstanceField.FieldType); il.Body.Variables.Add(guardVariable); var instructions = il.Body.Instructions; var start = instructions[0]; var skipFirst = 4; il.InsertBefore(start, il.Create(OpCodes.Ldsfld, guard.InstanceField)); il.InsertBefore(start, il.Create(OpCodes.Dup)); il.InsertBefore(start, il.CreateStlocBest(guardVariable)); il.InsertBefore(start, il.Create(OpCodes.Call, isStaticConstructor ? guard.GuardEnterStaticConstructorMethod : guard.GuardEnterMethod)); for (var i = skipFirst; i < instructions.Count; i++) { var instruction = instructions[i]; var memberRule = validator.ValidateInstructionAndGetPolicy(instruction, method); var code = instruction.OpCode.Code; if (code == Code.Newarr) { il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable)); il.InsertBefore(instruction, il.CreateCall(guard.FlowThroughGuardCountIntPtrMethod)); i += 2; continue; } if (memberRule != null && memberRule.Rewriters.Count > 0) { var instructionCountBefore = instructions.Count; var rewritten = false; var context = new MemberRewriterContext(il, guardVariable, guard); foreach (var rewriter in memberRule.InternalRewriters) { rewritten = rewriter.Rewrite(instruction, context) || rewritten; } if (rewritten) { i += instructions.Count - instructionCountBefore; continue; } } if (isStaticConstructor && instruction.OpCode.Code == Code.Ret) { il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable)); il.InsertBefore(instruction, il.CreateCall(guard.GuardExitStaticConstructorMethod)); i += 2; continue; } if (!ShouldInsertJumpGuardBefore(instruction)) { continue; } il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable)); il.InsertBefore(instruction, il.Create(OpCodes.Call, guard.GuardJumpMethod)); i += 2; } il.CorrectAllAfterChanges(); }
private static void ValidateAndRewriteMethod(MethodDefinition method, RuntimeGuardReferences guard, AssemblyValidator validator, AssemblyGuardSettings settings) { if (method.DeclaringType == guard.InstanceField.DeclaringType) { return; } validator.ValidateDefinition(method); if (!method.HasBody) { return; } if (method.Body.Instructions.Count == 0) { return; // weird, but happens with 'extern' } var isStaticConstructor = method.Name == ".cctor" && method.IsStatic && method.IsRuntimeSpecialName; var il = method.Body.GetILProcessor(); // IAsyncStateMachine.MoveNext has special rules, but to be safe, only apply them when its type is compiler-generated // which is checked by verifying that it has unspeakable name var isStateMachineMoveNext = method.Name == "MoveNext" && method.Overrides.Count == 1 && method.Overrides[0].FullName == "System.Void System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext()" && method.DeclaringType.Name.Contains("<") && il.Body.ExceptionHandlers.Count >= 1; var guardVariable = new VariableDefinition(guard.InstanceField.FieldType); il.Body.Variables.Add(guardVariable); var instructions = il.Body.Instructions; var start = instructions[0]; var skipStart = 0; var skipCount = 4; ExceptionHandler?stateMachineExceptionHandler = null; if (isStateMachineMoveNext) { // in IAsyncStateMachine.MoveNext, the GuardEnter call has to be inside the try block, so that the exception is propagated to the Task stateMachineExceptionHandler = il.Body.ExceptionHandlers.OrderBy(eh => eh.TryStart.Offset).First(); start = stateMachineExceptionHandler.TryStart; skipStart = instructions.IndexOf(start); } il.InsertBeforeAndRetargetJumps(start, il.Create(OpCodes.Ldsfld, guard.InstanceField)); il.InsertBefore(start, il.Create(OpCodes.Dup)); il.InsertBefore(start, il.CreateStlocBest(guardVariable)); il.InsertBefore(start, il.Create(OpCodes.Call, isStaticConstructor ? guard.GuardEnterStaticConstructorMethod : guard.GuardEnterMethod)); for (var i = 0; i < instructions.Count; i++) { if (i >= skipStart && i < skipStart + skipCount) { continue; } // in IAsyncStateMachine.MoveNext, guard calls are only allowed inside the try block if (stateMachineExceptionHandler != null && (i < instructions.IndexOf(stateMachineExceptionHandler.TryStart) || i > instructions.IndexOf(stateMachineExceptionHandler.TryEnd))) { continue; } var instruction = instructions[i]; var memberRule = validator.ValidateInstructionAndGetPolicy(instruction, method); var code = instruction.OpCode.Code; if (code == Code.Newarr) { il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable)); il.InsertBefore(instruction, il.CreateCall(guard.FlowThroughGuardCountIntPtrMethod)); i += 2; continue; } if (memberRule != null && memberRule.Rewriters.Count > 0) { var instructionCountBefore = instructions.Count; var rewritten = false; var context = new MemberRewriterContext(il, guardVariable, guard); foreach (var rewriter in memberRule.InternalRewriters) { rewritten = rewriter.Rewrite(instruction, context) || rewritten; } if (rewritten) { i += instructions.Count - instructionCountBefore; continue; } } if (isStaticConstructor && instruction.OpCode.Code == Code.Ret) { il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable)); il.InsertBefore(instruction, il.CreateCall(guard.GuardExitStaticConstructorMethod)); i += 2; continue; } if (!ShouldInsertJumpGuardBefore(instruction)) { continue; } il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable)); il.InsertBefore(instruction, il.Create(OpCodes.Call, guard.GuardJumpMethod)); i += 2; } il.CorrectAllAfterChanges(); }