in src/BinSkim.Rules/PERules/BA2006.BuildWithSecureTools.cs [84:300]
public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
{
PEBinary target = context.PEBinary();
Pdb pdb = target.Pdb;
/*
* This is disabled for now. It's not clear that we can
* actually detect when a binary is compiled with a version
* of csc.exe that is too stale. The reasons are twofold:
* 1) Older versions of csc.exe did not version the version
* data that is persisted to the PE. i.e., '48.0' was
* emitted for numerous versions of the compiler.
* 2) Our PDB reader does not current appear able to crack
* PDBs generated by the older C# compilers. Needs to
* be investigated.
if (target.PE.IsManaged && !target.PE.IsMixedMode)
{
AnalyzeManagedPE(context);
}
*/
Version minCompilerVersion;
var goodCompilers = new HashSet<string>();
var badModules = new List<ObjectModuleDetails>();
StringToVersionMap allowedLibraries = context.Policy.GetProperty(AllowedLibraries);
var languageToBadModules = new Dictionary<Language, List<ObjectModuleDetails>>();
foreach (DisposableEnumerableView<Symbol> omView in pdb.CreateObjectModuleIterator())
{
Symbol om = omView.Value;
ObjectModuleDetails omDetails = om.GetObjectModuleDetails();
if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftC &&
omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftCxx)
{
// TODO: MikeFan (1/6/2022)
// We need to take a step back and comprehensively review our compiler/language support.
// https://github.com/Microsoft/binskim/issues/114
continue;
}
switch (omDetails.Language)
{
case Language.LINK:
{
continue;
}
case Language.C:
case Language.Cxx:
{
minCompilerVersion = (target.PE?.IsXBox == true)
? context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_COMPILER_VER]
: context.Policy.GetProperty(MinimumToolVersions)[nameof(Language.C)];
break;
}
/*
TODO: MikeFan (1/6/2022)
We need to take a step back and comprehensively review our compiler/language support.
https://github.com/Microsoft/binskim/issues/114
case Language.MASM:
{
minCompilerVersion =
context.Policy.GetProperty(MinimumToolVersions)[nameof(Language.MASM)];
break;
}
case Language.CVTRES:
{
minCompilerVersion =
context.Policy.GetProperty(MinimumToolVersions)[nameof(Language.CVTRES)];
break;
}
case Language.CSharp:
{
minCompilerVersion =
context.Policy.GetProperty(MinimumToolVersions)[nameof(Language.CSharp)];
break;
}
Language data is not always included if it is only compiled with SymTagCompiland without SymTagCompilandDetails
https://docs.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/compilanddetails?view=vs-2022
Compiland information is split between symbols with a SymTagCompiland tag (low detail)
and a SymTagCompilandDetails tag (high detail).
case Language.Unknown:
{
minCompilerVersion =
context.Policy.GetProperty(MinimumToolVersions)[nameof(Language.Unknown)];
break;
}
*/
default:
{
continue;
}
}
// See if the item is in our skip list
if (!string.IsNullOrEmpty(om.Lib))
{
string libFileName = string.Concat(System.IO.Path.GetFileName(om.Lib), ",", omDetails.Language.ToString()).ToLowerInvariant();
if (allowedLibraries.TryGetValue(libFileName, out Version minAllowedVersion) &&
omDetails.CompilerBackEndVersion >= minAllowedVersion)
{
continue;
}
}
Version actualVersion;
Version minimumVersion = minCompilerVersion;
Language omLanguage = omDetails.Language;
switch (omLanguage)
{
case Language.C:
case Language.Cxx:
{
actualVersion = Minimum(omDetails.CompilerBackEndVersion, omDetails.CompilerFrontEndVersion);
break;
}
case Language.LINK:
case Language.MASM:
case Language.CVTRES:
{
actualVersion = omDetails.CompilerBackEndVersion;
break;
}
default:
continue;
}
bool foundIssue = actualVersion < minimumVersion;
AdvancedMitigations advancedMitigations = context.Policy.GetProperty(AdvancedMitigationsEnforced);
if (!foundIssue &&
target.PE != null &&
(advancedMitigations & AdvancedMitigations.Spectre) == AdvancedMitigations.Spectre)
{
var machineType = (ExtendedMachine)target.PE.Machine;
// Current toolchain is within the version range to validate.
// Now we'll retrieve relevant compiler mitigation details to
// ensure this object module's build and revision meet
// expectations.
CompilerMitigations newMitigationData =
EnableSpectreMitigations.GetAvailableMitigations(context, machineType, actualVersion);
// Current compiler version does not support Spectre mitigations.
foundIssue = !newMitigationData.HasFlag(CompilerMitigations.D2GuardSpecLoadAvailable)
&& !newMitigationData.HasFlag(CompilerMitigations.QSpectreAvailable);
if (foundIssue)
{
// Get the closest compiler version that has mitigations--i.e. if the user is using a 19.0 (VS2015) compiler, we should be recommending an upgrade to the
// 19.0 version that has the mitigations, not an upgrade to a 19.10+ (VS2017) compiler.
// Limitation--if there are multiple 'upgrade to' versions to recommend, this just going to give users the last one we see in the error.
minCompilerVersion = EnableSpectreMitigations.GetClosestCompilerVersionWithSpectreMitigations(context, machineType, actualVersion);
// Indicates Spectre mitigations are not supported on this platform. We won't flag this case.
if (minCompilerVersion == null)
{
foundIssue = false;
}
}
}
if (foundIssue)
{
badModules.Add(omDetails);
}
else
{
goodCompilers.Add(BuildCompilerIdentifier(omDetails));
}
}
if (badModules.Count != 0)
{
string badModulesText = badModules.CreateOutputCoalescedByCompiler();
string minimumRequiredCompilers = BuildMinimumCompilersList(context, languageToBadModules);
// '{0}' was compiled with one or more modules which were not built using
// minimum required tool versions ({1}). More recent toolchains
// contain mitigations that make it more difficult for an attacker to exploit
// vulnerabilities in programs they produce. To resolve this issue, compile
// and /or link your binary with more recent tools. If you are servicing a
// product where the tool chain cannot be modified (e.g. producing a hotfix
// for an already shipped version) ignore this warning. Modules built outside
// of policy: {2}
context.Logger.Log(this,
RuleUtilities.BuildResult(FailureLevel.Error, context, null,
nameof(RuleResources.BA2006_Error),
context.TargetUri.GetFileName(),
minimumRequiredCompilers,
badModulesText));
return;
}
string[] sorted = goodCompilers.ToArray();
Array.Sort(sorted);
// All linked modules of '{0}' satisfy configured policy (observed compilers: {1}).
context.Logger.Log(this,
RuleUtilities.BuildResult(ResultKind.Pass, context, null,
nameof(RuleResources.BA2006_Pass),
context.TargetUri.GetFileName(),
string.Join(", ", sorted)));
}