public override void AnalyzePortableExecutableAndPdb()

in src/BinSkim.Rules/PERules/BA2007.EnableCriticalCompilerWarnings.cs [82:230]


        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb di = target.Pdb;

            var warningTooLowModules = new TruncatedCompilandRecordList();
            var disabledWarningModules = new TruncatedCompilandRecordList();
            var unknownLanguageModules = new TruncatedCompilandRecordList();
            var allWarningLevelLowModules = new TruncatedCompilandRecordList();

            string exampleTooLowWarningCommandLine = null;
            int overallMinimumWarningLevel = int.MaxValue;
            string exampleDisabledWarningCommandLine = null;
            var overallDisabledWarnings = new List<int>();

            foreach (DisposableEnumerableView<Symbol> omView in di.CreateObjectModuleIterator())
            {
                Symbol om = omView.Value;
                ObjectModuleDetails omDetails = om.GetObjectModuleDetails();

                // Detection applies to C/C++ produced by MS compiler only
                if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftC &&
                    omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftCxx)
                {
                    continue;
                }

                if (omDetails.Language == Language.Unknown)
                {
                    // See if this module contributed to an executable section. If not, we can ignore the module.
                    if (di.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId))
                    {
                        unknownLanguageModules.Add(om.CreateCompilandRecord());
                    }

                    continue;
                }

                if (!om.CreateChildIterator(SymTagEnum.SymTagFunction).Any())
                {
                    // uninteresting...
                    continue;
                }

                int warningLevel = omDetails.WarningLevel;
                var requiredDisabledWarnings = omDetails.ExplicitlyDisabledWarnings
                    .Where(context.Policy.GetProperty(RequiredCompilerWarnings).Contains).ToList();

                overallMinimumWarningLevel = Math.Min(overallMinimumWarningLevel, warningLevel);

                if (warningLevel >= 3 && requiredDisabledWarnings.Count == 0)
                {
                    // We duplicate this condition to bail out early and avoid writing the
                    // module description or newline into sbBadWarningModules if everything
                    // in the module is OK.
                    continue;
                }

                var suffix = new List<string>(2);

                if (warningLevel < 3)
                {
                    exampleTooLowWarningCommandLine ??= omDetails.RawCommandLine;

                    string msg = "[warning level: " + warningLevel.ToString(CultureInfo.InvariantCulture) + "]";
                    warningTooLowModules.Add(om.CreateCompilandRecordWithSuffix(msg));
                    suffix.Add(msg);
                }

                if (requiredDisabledWarnings.Count != 0)
                {
                    MergeInto(overallDisabledWarnings, requiredDisabledWarnings);
                    exampleDisabledWarningCommandLine ??= omDetails.RawCommandLine;

                    string msg = "[Explicitly disabled warnings: " + CreateTextWarningList(requiredDisabledWarnings) + "]";
                    disabledWarningModules.Add(om.CreateCompilandRecordWithSuffix(msg));
                    suffix.Add(msg);
                }

                allWarningLevelLowModules.Add(om.CreateCompilandRecordWithSuffix(string.Join(" ", suffix)));
            }

            if (unknownLanguageModules.Empty &&
                exampleTooLowWarningCommandLine == null &&
                exampleDisabledWarningCommandLine == null)
            {
                // '{0}' was compiled at a secure warning level ({1}) and does not 
                // include any modules that disable specific warnings which are 
                // required by policy. As a result, there is a greater likelihood 
                // that memory corruption, information disclosure, double-free and 
                // other security-related vulnerabilities do not exist in code.
                context.Logger.Log(this,
                    RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                        nameof(RuleResources.BA2007_Pass),
                        context.TargetUri.GetFileName(),
                        overallMinimumWarningLevel.ToString()));
                return;
            }

            if (!unknownLanguageModules.Empty)
            {
                // '{0}' contains code from an unknown language, preventing a 
                // comprehensive analysis of the compiler warning settings. 
                // The language could not be identified for the following modules: {1}
                context.Logger.Log(this,
                    RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                        nameof(RuleResources.BA2007_Error_UnknownModuleLanguage),
                        context.TargetUri.GetFileName(),
                        unknownLanguageModules.CreateSortedObjectList()));
            }

            if (!string.IsNullOrEmpty(exampleTooLowWarningCommandLine))
            {
                // '{0}' was compiled at too low a warning level. Warning level 3 enables 
                // important static analysis in the compiler to flag bugs that can lead 
                // to memory corruption, information disclosure, or double-free 
                // vulnerabilities.To resolve this issue, compile at warning level 3 or 
                // higher by supplying / W3, / W4, or / Wall to the compiler, and resolve 
                // the warnings emitted.
                // An example compiler command line triggering this check: {1}
                // Modules triggering this check: {2}
                context.Logger.Log(this,
                    RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                        nameof(RuleResources.BA2007_Error_InsufficientWarningLevel),
                        context.TargetUri.GetFileName(),
                        overallMinimumWarningLevel.ToString(),
                        exampleTooLowWarningCommandLine,
                        warningTooLowModules.CreateTruncatedObjectList()));
            }

            if (exampleDisabledWarningCommandLine != null)
            {
                // '{0}' disables compiler warning(s) which are required by policy. A 
                // compiler warning is typically required if it has a high likelihood of 
                // flagging memory corruption, information disclosure, or double-free 
                // vulnerabilities. To resolve this issue, enable the indicated warning(s) 
                // by removing /Wxxxx switches (where xxxx is a warning id indicated here) 
                // from your command line, and resolve any warnings subsequently raised 
                // during compilation.
                // An example compiler command line triggering this check was: {1}
                // Modules triggering this check were: {2}
                context.Logger.Log(this,
                    RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                        nameof(RuleResources.BA2007_Error_WarningsDisabled),
                        context.TargetUri.GetFileName(),
                        exampleDisabledWarningCommandLine,
                        disabledWarningModules.CreateTruncatedObjectList()));
            }
        }