Exemple #1
0
        // FIXME: could probably rewrite this to be more reliable using
        // OpCode.StackBehaviourPop and StackBehaviourPush
        //
        // FIXME: This code is simply too naive to work well in the real world.
        // For example, code that compares a local to null will not work
        // correctly (we want the local to be null along one branch and non-
        // null along the other). But this is tricky to do with the textbook
        // algorithm (note that meet must be commutative). One fix is to
        // splice in synthetic blocks and fix the code so that it can handle
        // zero length blocks.
        public void Transfer([NonNull] Node node, [NonNull] object inFact,
                             [NonNull] object outFact, bool warn)
        {
            BasicBlock bb = (BasicBlock)node;

            /* Exit and exception nodes don't cover any real instructions. */
            if (bb.isExit || bb.isException)
            {
                return;
            }

            //NullDerefFrame inFrame = (NullDerefFrame)inFact;
            NullDerefFrame outFrame           = (NullDerefFrame)outFact;
            VariableDefinitionCollection vars = method.Body.Variables;

            if (Verbose)
            {
                Trace.WriteLine(string.Empty);
                Trace.WriteLine(string.Format("Basic block {0}", bb.ToString()));
                Trace.WriteLine("Input frame:");
                Trace.Write(outFrame.ToString());
            }

            for (int i = bb.first; i <= bb.last; i++)
            {
                Instruction insn   = bb.Instructions[i];
                OpCode      opcode = insn.OpCode;

                if (Verbose)
                {
                    Trace.Write(string.Format("   {0}", opcode.Name));
                    if (insn.Operand != null && !(insn.Operand is Instruction))
                    {
                        Trace.WriteLine(string.Format(" {0}", insn.Operand.ToString()));
                    }
                    else if (insn.Operand is Instruction)
                    {
                        Trace.WriteLine(string.Format(" {0}",
                                                      ((Instruction)insn.Operand).Offset.ToString("X4")));
                    }
                    else
                    {
                        Trace.WriteLine(string.Empty);
                    }
                }

                switch (opcode.Code)
                {
                /* Load argument */

                /* Stored nullities are set to declared values on method
                 * entry. Starg and kin can change this over time. */
                case Code.Ldarg_0:
                    outFrame.PushStack(outFrame.GetArgNullity(0));
                    break;

                case Code.Ldarg_1:
                    outFrame.PushStack(outFrame.GetArgNullity(1));
                    break;

                case Code.Ldarg_2:
                    outFrame.PushStack(outFrame.GetArgNullity(2));
                    break;

                case Code.Ldarg_3:
                    outFrame.PushStack(outFrame.GetArgNullity(3));
                    break;

                case Code.Ldarg:
                    if (insn.Operand is int)
                    {
                        outFrame.PushStack(outFrame.GetArgNullity((int)insn.Operand));
                    }
                    else if (insn.Operand is ParameterDefinition)
                    {
                        ParameterDefinition pd = (insn.Operand as ParameterDefinition);
                        outFrame.PushStack((pd.HasConstant && pd.Constant == null) ?
                                           Nullity.Null : Nullity.NonNull);
                    }
                    else
                    {
                        outFrame.PushStack(Nullity.NonNull);
                    }
                    break;

                case Code.Ldarg_S: {
                    ParameterDefinition param =
                        (ParameterDefinition)insn.Operand;
                    outFrame.PushStack(
                        outFrame.GetArgNullity(param.GetSequence() - 1));
                    break;
                }

                case Code.Ldarga:
                case Code.Ldarga_S:
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Store argument */
                case Code.Starg:
                    outFrame.SetArgNullity((int)insn.Operand,
                                           outFrame.PopStack());
                    break;

                case Code.Starg_S: {
                    ParameterDefinition param =
                        (ParameterDefinition)insn.Operand;
                    outFrame.SetArgNullity(param.GetSequence() - 1,
                                           outFrame.PopStack());
                    break;
                }

                /* Load local */
                case Code.Ldloc_0:
                    outFrame.PushStack(outFrame.GetLocNullity(0));
                    break;

                case Code.Ldloc_1:
                    outFrame.PushStack(outFrame.GetLocNullity(1));
                    break;

                case Code.Ldloc_2:
                    outFrame.PushStack(outFrame.GetLocNullity(2));
                    break;

                case Code.Ldloc_3:
                    outFrame.PushStack(outFrame.GetLocNullity(3));
                    break;

                case Code.Ldloc:
                case Code.Ldloc_S:
                    outFrame.PushStack(outFrame.GetLocNullity(
                                           vars.IndexOf((VariableDefinition)insn.Operand)));
                    break;

                case Code.Ldloca:
                case Code.Ldloca_S:
                    outFrame.SetLocNullity(
                        vars.IndexOf((VariableDefinition)insn.Operand),
                        Nullity.Unknown);
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Store local */
                case Code.Stloc_0:
                    outFrame.SetLocNullity(0, outFrame.PopStack());
                    break;

                case Code.Stloc_1:
                    outFrame.SetLocNullity(1, outFrame.PopStack());
                    break;

                case Code.Stloc_2:
                    outFrame.SetLocNullity(2, outFrame.PopStack());
                    break;

                case Code.Stloc_3:
                    outFrame.SetLocNullity(3, outFrame.PopStack());
                    break;

                case Code.Stloc:
                case Code.Stloc_S:
                    outFrame.SetLocNullity(
                        vars.IndexOf((VariableDefinition)insn.Operand),
                        outFrame.PopStack());
                    break;

                /* Load other things */
                case Code.Ldftn:
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Ldvirtftn:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Ldstr:
                case Code.Ldnull:
                    outFrame.PushStack(Nullity.Null);
                    break;

                case Code.Ldlen:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Ldtoken:
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Object operations */
                case Code.Cpobj:
                    outFrame.PopStack(2);
                    break;

                case Code.Newobj:
                    outFrame.PopStack(
                        ((MethodReference)insn.Operand).Parameters.Count);
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Ldobj:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Stobj:
                    outFrame.PopStack(2);
                    break;

                case Code.Initobj:
                    outFrame.PopStack();
                    break;

                /* Load field */
                case Code.Ldfld: {
                    Check(insn, warn, outFrame.PopStack(), "field");
                    FieldReference field = (FieldReference)insn.Operand;
                    if (nnaCollector.HasNonNullAttribute(field))
                    {
                        outFrame.PushStack(Nullity.NonNull);
                    }
                    else
                    {
                        outFrame.PushStack(Nullity.Unknown);
                    }
                    break;
                }

                case Code.Ldflda:
                    Check(insn, warn, outFrame.PopStack(), "field");
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Ldsfld: {
                    FieldReference field = (FieldReference)insn.Operand;
                    if (nnaCollector.HasNonNullAttribute(field))
                    {
                        outFrame.PushStack(Nullity.NonNull);
                    }
                    else
                    {
                        outFrame.PushStack(Nullity.Unknown);
                    }
                    break;
                }

                case Code.Ldsflda:
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Store field */
                case Code.Stfld: {
                    /* FIXME: warn if writing null to non-null field */
                    Nullity n = outFrame.PopStack();
                    Check(insn, warn, outFrame.PopStack(), "field");
                    FieldReference field = (FieldReference)insn.Operand;
                    if (warn && nnaCollector.HasNonNullAttribute(field))
                    {
                        if (Verbose)
                        {
                            Trace.WriteLine(string.Format("FAILURE1: null deref at {0:X2}", insn.Offset));
                        }
                        if (n == Nullity.Unknown)
                        {
                            runner.Report(method, insn, Severity.High, Confidence.Low, "storing possibly null value in field declared non-null");
                        }
                        else if (n == Nullity.Null)
                        {
                            runner.Report(method, insn, Severity.High, Confidence.Low, "storing null value in field declared non-null");
                        }
                    }
                    break;
                }

                case Code.Stsfld: {
                    Nullity        n     = outFrame.PopStack();
                    FieldReference field = (FieldReference)insn.Operand;
                    if (warn && nnaCollector.HasNonNullAttribute(field))
                    {
                        if (Verbose)
                        {
                            Trace.WriteLine(string.Format("FAILURE2: null deref at {0:X2}", insn.Offset));
                        }
                        if (n == Nullity.Unknown)
                        {
                            runner.Report(method, insn, Severity.High, Confidence.Low, "storing possibly null value in field declared non-null");
                        }
                        else if (n == Nullity.Null)
                        {
                            runner.Report(method, insn, Severity.High, Confidence.Low, "storing null value in field declared non-null");
                        }
                    }
                    break;
                }

                /* Stack operations */
                case Code.Dup:
                    outFrame.PushStack(outFrame.PeekStack());
                    break;

                case Code.Pop:
                    outFrame.PopStack();
                    break;

                /* Method call and return */
                case Code.Calli:
                    ProcessCall(insn, warn, true, outFrame);
                    break;

                case Code.Call:
                case Code.Callvirt:
                    ProcessCall(insn, warn, false, outFrame);
                    break;

                case Code.Ret:
                    if (!IsVoid(method.ReturnType))
                    {
                        Nullity n = outFrame.PopStack();
                        if (nnaCollector.HasNonNullAttribute(method) && warn)
                        {
                            if (Verbose)
                            {
                                Trace.WriteLine(string.Format("FAILURE3: null deref at {0:X2}", insn.Offset));
                            }
                            if (n == Nullity.Null)
                            {
                                runner.Report(method, insn, Severity.High, Confidence.Low, "returning null value from method declared non-null");
                            }
                            else
                            {
                                runner.Report(method, insn, Severity.High, Confidence.Low, "returning possibly null value from method declared non-null");
                            }
                        }
                    }
                    break;

                /* Indirect load */
                case Code.Ldind_I1:
                case Code.Ldind_U1:
                case Code.Ldind_I2:
                case Code.Ldind_U2:
                case Code.Ldind_I4:
                case Code.Ldind_U4:
                case Code.Ldind_I8:
                case Code.Ldind_I:
                case Code.Ldind_R4:
                case Code.Ldind_R8:
                case Code.Ldind_Ref:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.Unknown);
                    break;

                /* Indirect store */
                case Code.Stind_Ref:
                case Code.Stind_I:
                case Code.Stind_I1:
                case Code.Stind_I2:
                case Code.Stind_I4:
                case Code.Stind_I8:
                case Code.Stind_R4:
                case Code.Stind_R8:
                    outFrame.PopStack(2);
                    break;

                /* Class-related operations */
                case Code.Box:
                case Code.Unbox:
                case Code.Unbox_Any:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Castclass:
                case Code.Isinst:
                    break;

                /* Exception handling */
                case Code.Rethrow:
                case Code.Endfinally:
                    break;

                case Code.Throw:
                case Code.Endfilter:
                    outFrame.PopStack();
                    break;

                case Code.Leave:
                case Code.Leave_S:
                    outFrame.EmptyStack();
                    break;

                /* Array operations */
                case Code.Newarr:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Load element */
                case Code.Ldelema:
                case Code.Ldelem_I1:
                case Code.Ldelem_U1:
                case Code.Ldelem_I2:
                case Code.Ldelem_U2:
                case Code.Ldelem_I4:
                case Code.Ldelem_U4:
                case Code.Ldelem_I8:
                case Code.Ldelem_I:
                case Code.Ldelem_R4:
                case Code.Ldelem_R8:
                    outFrame.PopStack(2);
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Ldelem_Ref:
                case Code.Ldelem_Any:         /* This may or may not be a reference. */
                    outFrame.PopStack(2);
                    outFrame.PushStack(Nullity.Unknown);
                    break;

                /* Store element */
                /* Pop 3 */
                case Code.Stelem_I:
                case Code.Stelem_I1:
                case Code.Stelem_I2:
                case Code.Stelem_I4:
                case Code.Stelem_I8:
                case Code.Stelem_R4:
                case Code.Stelem_R8:
                case Code.Stelem_Ref:
                case Code.Stelem_Any:
                    outFrame.PopStack(3);
                    break;

                case Code.Arglist:
                case Code.Sizeof:
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                case Code.Mkrefany:
                case Code.Refanyval:
                case Code.Refanytype:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Prefixes */
                case Code.Unaligned:
                case Code.Volatile:
                case Code.Tail:
                    break;

                /* Effect-free instructions */
                case Code.Nop:
                case Code.Break:
                    break;

                /* Load constant */
                /* Push non-ref. */
                case Code.Ldc_I4_M1:
                case Code.Ldc_I4_0:
                case Code.Ldc_I4_1:
                case Code.Ldc_I4_2:
                case Code.Ldc_I4_3:
                case Code.Ldc_I4_4:
                case Code.Ldc_I4_5:
                case Code.Ldc_I4_6:
                case Code.Ldc_I4_7:
                case Code.Ldc_I4_8:
                case Code.Ldc_I4_S:
                case Code.Ldc_I4:
                case Code.Ldc_I8:
                case Code.Ldc_R4:
                case Code.Ldc_R8:
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Unconditional control flow */
                /* Do nothing */
                case Code.Br:
                case Code.Br_S:
                    break;

                /* Conditional branches */
                /* Pop 1 */
                case Code.Brfalse:
                case Code.Brtrue:
                case Code.Brfalse_S:
                case Code.Brtrue_S:
                    outFrame.PopStack();
                    break;

                /* Comparison branches */
                /* Pop 2. */
                case Code.Beq:
                case Code.Bge:
                case Code.Bgt:
                case Code.Ble:
                case Code.Blt:
                case Code.Bne_Un:
                case Code.Bge_Un:
                case Code.Bgt_Un:
                case Code.Ble_Un:
                case Code.Blt_Un:
                case Code.Beq_S:
                case Code.Bge_S:
                case Code.Bgt_S:
                case Code.Ble_S:
                case Code.Blt_S:
                case Code.Bne_Un_S:
                case Code.Bge_Un_S:
                case Code.Bgt_Un_S:
                case Code.Ble_Un_S:
                case Code.Blt_Un_S:
                    outFrame.PopStack(2);
                    break;

                case Code.Switch:
                    outFrame.PopStack();
                    break;

                /* Comparisons */
                /* Pop 2, push non-ref */
                case Code.Ceq:
                case Code.Cgt:
                case Code.Cgt_Un:
                case Code.Clt:
                case Code.Clt_Un:
                    outFrame.PopStack(2);
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Arithmetic and logical binary operators */
                /* Pop 2, push non-ref */
                case Code.Add:
                case Code.Sub:
                case Code.Mul:
                case Code.Div:
                case Code.Div_Un:
                case Code.Rem:
                case Code.Rem_Un:
                case Code.And:
                case Code.Or:
                case Code.Xor:
                case Code.Shl:
                case Code.Shr:
                case Code.Shr_Un:
                case Code.Add_Ovf:
                case Code.Add_Ovf_Un:
                case Code.Mul_Ovf:
                case Code.Mul_Ovf_Un:
                case Code.Sub_Ovf:
                case Code.Sub_Ovf_Un:
                    outFrame.PopStack(2);
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Arithmetic and logical unary operators */
                /* Pop 1, push non-ref */
                case Code.Neg:
                case Code.Not:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                /* Conversions. */
                /* Do nothing. */
                case Code.Conv_I1:
                case Code.Conv_I2:
                case Code.Conv_I4:
                case Code.Conv_I8:
                case Code.Conv_R4:
                case Code.Conv_R8:
                case Code.Conv_U4:
                case Code.Conv_U8:
                case Code.Conv_U:
                case Code.Conv_R_Un:
                case Code.Conv_Ovf_I1_Un:
                case Code.Conv_Ovf_I2_Un:
                case Code.Conv_Ovf_I4_Un:
                case Code.Conv_Ovf_I8_Un:
                case Code.Conv_Ovf_U1_Un:
                case Code.Conv_Ovf_U2_Un:
                case Code.Conv_Ovf_U4_Un:
                case Code.Conv_Ovf_U8_Un:
                case Code.Conv_Ovf_I_Un:
                case Code.Conv_Ovf_U_Un:
                case Code.Conv_Ovf_I1:
                case Code.Conv_Ovf_U1:
                case Code.Conv_Ovf_I2:
                case Code.Conv_Ovf_U2:
                case Code.Conv_Ovf_I4:
                case Code.Conv_Ovf_U4:
                case Code.Conv_Ovf_I8:
                case Code.Conv_Ovf_U8:
                case Code.Conv_U2:
                case Code.Conv_U1:
                case Code.Conv_I:
                case Code.Conv_Ovf_I:
                case Code.Conv_Ovf_U:
                case Code.Ckfinite:
                    break;

                /* Unverifiable instructions. */
                case Code.Jmp:
                    break;

                case Code.Cpblk:
                    outFrame.PopStack(3);
                    break;

                case Code.Initblk:
                    outFrame.PopStack(3);
                    break;

                case Code.Localloc:
                    outFrame.PopStack();
                    outFrame.PushStack(Nullity.NonNull);
                    break;

                default:
                    Trace.WriteLine(string.Format("Unknown instruction: {0} {1}",
                                                  opcode.Name, opcode.Value.ToString("X4")));
                    break;
                }         /* switch */
            }             /* for */

            if (Verbose)
            {
                Trace.WriteLine("Output frame:");
                Trace.Write(outFrame.ToString());
            }
        }         /* Transfer */