Beispiel #1
0
 private static void ValidateAndRewriteType(TypeDefinition type, RuntimeGuardReferences guard, AssemblyValidator validator, AssemblyGuardSettings settings) {
     validator.ValidateDefinition(type);
     foreach (var nested in type.NestedTypes) {
         ValidateAndRewriteType(nested, guard, validator, settings);
     }
     foreach (var method in type.Methods) {
         ValidateAndRewriteMethod(method, guard, validator, settings);
     }
 }
Beispiel #2
0
        internal static RuntimeGuardToken Rewrite(AssemblyDefinition assembly, AssemblyGuardSettings? settings = null) {
            var id = Guid.NewGuid();
            settings = settings ?? AssemblyGuardSettings.Default;
            var validator = new AssemblyValidator(settings, assembly, new StackSizeValidator(settings), new PointerOperationValidator(settings));
            foreach (var module in assembly.Modules) {
                var guardInstanceField = EmitGuardInstance(module, id);
                var guard = new RuntimeGuardReferences(guardInstanceField, module);

                validator.ValidateDefinition(module);
                foreach (var type in module.Types) {
                    ValidateAndRewriteType(type, guard, validator, settings);
                }
            }
            return new RuntimeGuardToken(id);
        }
Beispiel #3
0
        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();
        }
Beispiel #4
0
        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();
        }