private ValueNodeList PopCallArguments(
            Stack <StackSlot> currentStack,
            MethodReference methodCalled,
            MethodBody containingMethodBody,
            bool isNewObj, int ilOffset,
            out ValueNode newObjValue)
        {
            newObjValue = null;

            int countToPop = 0;

            if (!isNewObj && methodCalled.HasThis && !methodCalled.ExplicitThis)
            {
                countToPop++;
            }
            countToPop += methodCalled.Parameters.Count;

            ValueNodeList methodParams = new ValueNodeList(countToPop);

            for (int iParam = 0; iParam < countToPop; ++iParam)
            {
                StackSlot slot = PopUnknown(currentStack, 1, containingMethodBody, ilOffset);
                methodParams.Add(slot.Value);
            }

            if (isNewObj)
            {
                newObjValue = UnknownValue.Instance;
                methodParams.Add(newObjValue);
            }
            methodParams.Reverse();
            return(methodParams);
        }
        private void HandleCall(
            MethodBody callingMethodBody,
            Instruction operation,
            Stack <StackSlot> currentStack,
            int curBasicBlock)
        {
            MethodReference calledMethod = (MethodReference)operation.Operand;

            bool isNewObj = operation.OpCode.Code == Code.Newobj;

            ValueNode     newObjValue;
            ValueNodeList methodParams = PopCallArguments(currentStack, calledMethod, callingMethodBody, isNewObj,
                                                          operation.Offset, out newObjValue);

            ValueNode methodReturnValue;
            bool      handledFunction = HandleCall(
                callingMethodBody,
                calledMethod,
                operation,
                methodParams,
                out methodReturnValue);

            // Handle the return value or newobj result
            if (!handledFunction)
            {
                if (isNewObj)
                {
                    if (newObjValue == null)
                    {
                        PushUnknown(currentStack);
                    }
                    else
                    {
                        methodReturnValue = newObjValue;
                    }
                }
                else
                {
                    if (calledMethod.ReturnType.MetadataType != MetadataType.Void)
                    {
                        methodReturnValue = UnknownValue.Instance;
                    }
                }
            }

            if (methodReturnValue != null)
            {
                currentStack.Push(new StackSlot(methodReturnValue, calledMethod.ReturnType.IsByRefOrPointer()));
            }

            foreach (var param in methodParams)
            {
                if (param is ArrayValue arr)
                {
                    MarkArrayValuesAsUnknown(arr, curBasicBlock);
                }
            }
        }
Exemple #3
0
        public override bool Equals(object other)
        {
            ValueNodeList otherList = other as ValueNodeList;

            if (otherList == null)
            {
                return(false);
            }

            if (otherList.Count != Count)
            {
                return(false);
            }

            for (int i = 0; i < Count; i++)
            {
                if (!otherList[i].Equals(this[i]))
                {
                    return(false);
                }
            }
            return(true);
        }
 public abstract bool HandleCall(
     MethodBody callingMethodBody,
     MethodReference calledMethod,
     Instruction operation,
     ValueNodeList methodParams,
     out ValueNode methodReturnValue);
        public override bool HandleCall(MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out MultiValue methodReturnValue)
        {
            methodReturnValue = new ();

            var reflectionProcessed = _markStep.ProcessReflectionDependency(callingMethodBody, operation);

            if (reflectionProcessed)
            {
                return(false);
            }

            Debug.Assert(callingMethodBody.Method == _origin.Provider);
            var calledMethodDefinition = _context.TryResolve(calledMethod);

            if (calledMethodDefinition == null)
            {
                return(false);
            }

            _origin = _origin.WithInstructionOffset(operation.Offset);

            MultiValue instanceValue;
            ImmutableArray <MultiValue> arguments;

            if (calledMethodDefinition.HasImplicitThis())
            {
                instanceValue = methodParams[0];
                arguments     = methodParams.Skip(1).ToImmutableArray();
            }
            else
            {
                instanceValue = MultiValueLattice.Top;
                arguments     = methodParams.ToImmutableArray();
            }

            TrimAnalysisPatterns.Add(new TrimAnalysisMethodCallPattern(
                                         operation,
                                         calledMethod,
                                         instanceValue,
                                         arguments,
                                         _origin
                                         ));

            var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: false, _context);

            return(HandleCall(
                       operation,
                       calledMethod,
                       instanceValue,
                       arguments,
                       diagnosticContext,
                       _reflectionMarker,
                       _context,
                       _markStep,
                       out methodReturnValue));
        }
Exemple #6
0
        public override bool HandleCall(MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out MultiValue methodReturnValue)
        {
            methodReturnValue = new ();
            MultiValue?maybeMethodReturnValue = null;

            var reflectionProcessed = _markStep.ProcessReflectionDependency(callingMethodBody, operation);

            if (reflectionProcessed)
            {
                return(false);
            }

            var callingMethodDefinition = callingMethodBody.Method;
            var calledMethodDefinition  = _context.TryResolve(calledMethod);

            if (calledMethodDefinition == null)
            {
                return(false);
            }

            bool requiresDataFlowAnalysis = _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis(calledMethodDefinition);
            DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ?
                                                                                       _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation(calledMethodDefinition) : 0;

            _origin = _origin.WithInstructionOffset(operation.Offset);
            bool diagnosticsEnabled = ShouldEnableReflectionPatternReporting(_origin.Provider);
            var  diagnosticContext  = new DiagnosticContext(_origin, diagnosticsEnabled, _context);
            var  handleCallAction   = new HandleCallAction(_context, _reflectionMarker, diagnosticContext, callingMethodDefinition);

            switch (Intrinsics.GetIntrinsicIdForMethod(calledMethodDefinition))
            {
            case IntrinsicId.IntrospectionExtensions_GetTypeInfo:
            case IntrinsicId.TypeInfo_AsType:
            case IntrinsicId.Type_get_UnderlyingSystemType:
            case IntrinsicId.Type_GetTypeFromHandle:
            case IntrinsicId.Type_get_TypeHandle:
            case IntrinsicId.Type_GetInterface:
            case IntrinsicId.Type_get_AssemblyQualifiedName:
            case IntrinsicId.RuntimeHelpers_RunClassConstructor:
            case var callType when(callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields ||
                                   callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) &&
                calledMethod.DeclaringType.IsTypeOf(WellKnownType.System_Type) &&
                calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" &&
                calledMethod.HasThis:
            case var fieldPropertyOrEvent when(fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) &&
                calledMethod.DeclaringType.IsTypeOf(WellKnownType.System_Type) &&
                calledMethod.Parameters[0].ParameterType.IsTypeOf(WellKnownType.System_String) &&
                calledMethod.HasThis:
            case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent ||
                getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField ||
                getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod ||
                getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty:
            case IntrinsicId.Type_GetMember:
            case IntrinsicId.Type_GetMethod:
            case IntrinsicId.Type_GetNestedType:
            case IntrinsicId.Nullable_GetUnderlyingType:
            case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType(1, "System.Reflection.MethodInfo"):
            case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property:
            case IntrinsicId.Type_get_BaseType:
            case IntrinsicId.Type_GetConstructor:
            case IntrinsicId.MethodBase_GetMethodFromHandle:
            case IntrinsicId.MethodBase_get_MethodHandle:
            case IntrinsicId.Type_MakeGenericType:
            case IntrinsicId.MethodInfo_MakeGenericMethod:
            case IntrinsicId.Expression_Call:
            case IntrinsicId.Expression_New:
            case IntrinsicId.Type_GetType:
            case IntrinsicId.Activator_CreateInstance_Type:
            case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName:
            case IntrinsicId.Activator_CreateInstanceFrom:
            case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance ||
                appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap ||
                appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom ||
                appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap:
            case IntrinsicId.Assembly_CreateInstance: {
                var instanceValue = MultiValueLattice.Top;
                IReadOnlyList <MultiValue> parameterValues = methodParams;
                if (calledMethodDefinition.HasImplicitThis())
                {
                    instanceValue   = methodParams[0];
                    parameterValues = parameterValues.Skip(1).ToImmutableList();
                }
                return(handleCallAction.Invoke(calledMethodDefinition, instanceValue, parameterValues, out methodReturnValue, out _));
            }

            case IntrinsicId.None: {
                if (calledMethodDefinition.IsPInvokeImpl)
                {
                    // Is the PInvoke dangerous?
                    bool comDangerousMethod = IsComInterop(calledMethodDefinition.MethodReturnType, calledMethodDefinition.ReturnType);
                    foreach (ParameterDefinition pd in calledMethodDefinition.Parameters)
                    {
                        comDangerousMethod |= IsComInterop(pd, pd.ParameterType);
                    }

                    if (comDangerousMethod)
                    {
                        diagnosticContext.AddDiagnostic(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethodDefinition.GetDisplayName());
                    }
                }
                _markStep.CheckAndReportRequiresUnreferencedCode(calledMethodDefinition, _origin);

                var instanceValue = MultiValueLattice.Top;
                IReadOnlyList <MultiValue> parameterValues = methodParams;
                if (calledMethodDefinition.HasImplicitThis())
                {
                    instanceValue   = methodParams[0];
                    parameterValues = parameterValues.Skip(1).ToImmutableList();
                }
                return(handleCallAction.Invoke(calledMethodDefinition, instanceValue, parameterValues, out methodReturnValue, out _));
            }

            case IntrinsicId.TypeDelegator_Ctor: {
                // This is an identity function for analysis purposes
                if (operation.OpCode == OpCodes.Newobj)
                {
                    AddReturnValue(methodParams[1]);
                }
            }
            break;

            case IntrinsicId.Array_Empty: {
                AddReturnValue(ArrayValue.Create(0, ((GenericInstanceMethod)calledMethod).GenericArguments[0]));
            }
            break;

            //
            // System.Object
            //
            // GetType()
            //
            case IntrinsicId.Object_GetType: {
                foreach (var valueNode in methodParams[0])
                {
                    // Note that valueNode can be statically typed in IL as some generic argument type.
                    // For example:
                    //   void Method<T>(T instance) { instance.GetType().... }
                    // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type.
                    // But it could be that T is annotated with for example PublicMethods:
                    //   void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); }
                    // In this case it's in theory possible to handle it, by treating the T basically as a base class
                    // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking
                    // has to happen on the callsite, which doesn't know that GetType() will be used...).
                    // For now we're intentionally ignoring this case - it will produce a warning.
                    // The counter example is:
                    //   Method<Base>(new Derived);
                    // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which
                    // currently it won't do.

                    TypeDefinition?staticType = (valueNode as IValueWithStaticType)?.StaticType;
                    if (staticType is null)
                    {
                        // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations
                        AddReturnValue(_annotations.GetMethodReturnValue(calledMethodDefinition));
                    }
                    else if (staticType.IsSealed || staticType.IsTypeOf("System", "Delegate"))
                    {
                        // We can treat this one the same as if it was a typeof() expression

                        // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods
                        // on delegates anyway so reflection on something this approximation would miss is actually safe.

                        // We ignore the fact that the type can be annotated (see below for handling of annotated types)
                        // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge
                        // of the type. So for example even if the type is annotated with PublicMethods
                        // but the code calls GetProperties on it - it will work - mark properties, don't mark methods
                        // since we ignored the fact that it's annotated.
                        // This can be seen a little bit as a violation of the annotation, but we already have similar cases
                        // where a parameter is annotated and if something in the method sets a specific known type to it
                        // we will also make it just work, even if the annotation doesn't match the usage.
                        AddReturnValue(new SystemTypeValue(staticType));
                    }
                    else
                    {
                        // Make sure the type is marked (this will mark it as used via reflection, which is sort of true)
                        // This should already be true for most cases (method params, fields, ...), but just in case
                        _reflectionMarker.MarkType(_origin, staticType);

                        var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy
                                         .ApplyDynamicallyAccessedMembersToTypeHierarchy(_reflectionMarker, staticType);

                        // Return a value which is "unknown type" with annotation. For now we'll use the return value node
                        // for the method, which means we're loosing the information about which staticType this
                        // started with. For now we don't need it, but we can add it later on.
                        AddReturnValue(_annotations.GetMethodReturnValue(calledMethodDefinition, annotation));
                    }
                }
            }
            break;

            // Note about Activator.CreateInstance<T>
            // There are 2 interesting cases:
            //  - The generic argument for T is either specific type or annotated - in that case generic instantiation will handle this
            //    since from .NET 6+ the T is annotated with PublicParameterlessConstructor annotation, so the linker would apply this as for any other method.
            //  - The generic argument for T is unannotated type - the generic instantiantion handling has a special case for handling PublicParameterlessConstructor requirement
            //    in such that if the generic argument type has the "new" constraint it will not warn (as it is effectively the same thing semantically).
            //    For all other cases, the linker would have already produced a warning.

            default:
                throw new NotImplementedException("Unhandled instrinsic");
            }

            // If we get here, we handled this as an intrinsic.  As a convenience, if the code above
            // didn't set the return value (and the method has a return value), we will set it to be an
            // unknown value with the return type of the method.
            bool returnsVoid = calledMethod.ReturnsVoid();

            methodReturnValue = maybeMethodReturnValue ?? (returnsVoid ?
                                                           MultiValueLattice.Top :
                                                           _annotations.GetMethodReturnValue(calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes));

            // Validate that the return value has the correct annotations as per the method return value annotations
            if (returnValueDynamicallyAccessedMemberTypes != 0)
            {
                foreach (var uniqueValue in methodReturnValue)
                {
                    if (uniqueValue is ValueWithDynamicallyAccessedMembers methodReturnValueWithMemberTypes)
                    {
                        if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag(returnValueDynamicallyAccessedMemberTypes))
                        {
                            throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds.");
                        }
                    }
                    else if (uniqueValue is SystemTypeValue)
                    {
                        // SystemTypeValue can fullfill any requirement, so it's always valid
                        // The requirements will be applied at the point where it's consumed (passed as a method parameter, set as field value, returned from the method)
                    }
                    else
                    {
                        throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds.");
                    }
                }
            }

            return(true);

            void AddReturnValue(MultiValue value)
            {
                maybeMethodReturnValue = (maybeMethodReturnValue is null) ? value : MultiValueLattice.Meet((MultiValue)maybeMethodReturnValue, value);
            }
        }