
今天,我们要检查另一个高质量的Microsoft项目,我们将英勇地钻研该项目,以发现PVS-Studio中的错误。 SARIF,是静态分析交换格式的缩写,它是一种标准(文件格式),旨在与其他工具进行交互并共享静态分析器的结果,这些工具包括:IDE,复杂的代码验证和分析工具(例如SonarQube),持续集成系统,等 SARIF SDK分别包含.NET开发人员工具以支持SARIF以及其他文件。
SARIF起源于Microsoft,现在是OASIS(处理开放标准的非营利性联盟)开发的标准。 SARIF不仅要传递分析器的结果,而且还要传递有关该工具的元数据,以及有关其启动方式,时间标签等的数据。 有关更多信息,请访问
OASIS网站。 可以从
GiHub上的存储库下载SARIF SDK的源代码。 通过
链接可访问该项目的主页。
关于项目
SARIF SDK项目原来很小:799个.cs文件(大约98,000个非空代码行)。 该项目包含我总是从检查中排除的测试。 因此,我们感兴趣的部分代码是642个.cs文件(大约79,000个非空代码行)。 当然还不够。 从好的方面来说,从那时到之后,检查和分析都很简单快捷,我一开始就试图在图片上反映出来。 尽管如此,我还是设法找到了一些不可思议的案件。 让我们看看它们。
失误
V3070 [CWE-457]初始化“默认”变量时,使用未初始化的变量“二进制”。 MimeType.cs 90
public static class MimeType { ....
该字段由另一个尚未接收到值的字段初始化。 结果,
默认情况下,
默认将为
字符串类型接收null值。 由于“
默认”字段未在任何地方使用,因此错误很可能没有引起注意。 但是事情可能会改变,然后开发人员将面临不适当的结果或程序崩溃。
V3061参数'logicalLocationToIndexMap'始终在使用前在方法主体中重写。 PrereleaseCompatibilityTransformer.cs 1963
private static JArray ConvertLogicalLocationsDictionaryToArray( .... Dictionary<LogicalLocation, int> logicalLocationToIndexMap, ....) { .... logicalLocationToIndexMap = new Dictionary<LogicalLocation, int>(LogicalLocation.ValueComparer); .... }
代码作者不以任何方式使用
logicalLocationToIndexMap参数,而是在其中写入一个不同的值。 奇怪的是,先前的值与在调用者代码中创建的空字典完全相同:
private static bool ApplyChangesFromTC25ThroughTC30(....) { .... Dictionary<LogicalLocation, int> logicalLocationToIndexMap = null; .... logicalLocationToIndexMap = new Dictionary<LogicalLocation, int>(LogicalLocation.ValueComparer); run["logicalLocations"] = ConvertLogicalLocationsDictionaryToArray( ...., logicalLocationToIndexMap, ....); }
奇怪而可疑的代码。
V3008 [CWE-563]连续两次为'run.Tool'变量分配值。 也许这是一个错误。 检查行:116,114。ExportRulesMetadataCommandBase.cs 116
public partial class Run { .... public Tool Tool { get; set; } .... } public partial class Tool : .... { .... public Tool() { } .... } private void OutputSarifRulesMetada(....) { .... var run = new Run(); run.Tool = new Tool(); run.Tool = Tool.CreateFromAssemblyData(....);
向
run.Tool属性分配了两次值。 在创建
Tool对象和在
Tool属性中写入值时,都不需要其他工作。 因此,重新分配闻起来很腥。
V3042 [CWE-476]可能为NullReferenceException。 '?。' 和“。” 运算符用于访问“ loc”对象的成员WhereComparer.cs 152
private static Uri ArtifactUri(ArtifactLocation loc, Run run) { return loc?.Uri ?? loc.Resolve(run)?.Uri; }
如果
loc变量的值为
null ,则将尝试从??的右侧返回该值。 运算符,导致通过空引用进行访问。
V3042 [CWE-476]可能为NullReferenceException。 '?。' 和“。” 运算符用于访问'formatString'对象的成员InsertOptionalDataVisitor.cs 194
public override Message VisitMessage(Message node) { .... node.Text = node.Arguments?.Count > 0 ? string.Format(...., formatString.Text, ....) : formatString?.Text; .... }
开发人员通过在条件?:运算符的两个并行分支中可能为空的
formatString引用来使用不安全和安全的访问选项。
V3042 [CWE-476]可能为NullReferenceException。 '?。' 和“。” 运算符用于访问“ messageText”对象FortifyFprConverter.cs 1210的成员
V3042 [CWE-476]可能为NullReferenceException。 '?。' 和“。” 运算符用于访问“ messageText”对象FortifyFprConverter.cs 1216的成员
private void AddMessagesToResult(Result result) { .... string messageText = (rule.ShortDescription ?? rule.FullDescription)?.Text; .... if (....) {
在此,分析器已经发出了两个有关通过null
messageText引用进行可能访问的警告。 它看起来没有威胁,但仍然是一个错误。
V3080 [CWE-476]可能的空解除引用。 考虑检查“ fileDataVersionOne.Uri”。 SarifCurrentToVersionOneVisitor.cs 1030
private IDictionary<string, FileDataVersionOne> CreateFileDataVersionOneDictionary() { .... FileDataVersionOne fileDataVersionOne = CreateFileDataVersionOne(v2File); if (fileDataVersionOne.Uri.OriginalString.Equals(key)) { .... } .... }
分析器怀疑使用
fileDataVersionOne.Uri引用时可能会出现
NullReferenceException 。 让我们看看该变量来自何处,并找出分析仪是否正确。 为此,让我们仔细看一下
CreateFileDataVersionOne方法的主体:
private FileDataVersionOne CreateFileDataVersionOne(Artifact v2FileData) { FileDataVersionOne fileData = null; if (v2FileData != null) { .... fileData = new FileDataVersionOne { .... Uri = v2FileData.Location?.Uri, .... }; .... } return fileData; } public partial class FileDataVersionOne { .... public Uri Uri { get; set; } .... }
确实,当创建
FileDataVersionOne类的对象时,
Uri属性可能会收到
null值。 这是数据流分析和过程间分析机制协同工作的一个很好的例子。
V3080 [CWE-476]可能的空解除引用。 考虑检查“ _jsonTextWriter”。 SarifLogger.cs 242
public virtual void Dispose() { .... if (_closeWriterOnDispose) { if (_textWriter != null) { _textWriter.Dispose(); } if (_jsonTextWriter == null) { _jsonTextWriter.Close(); }
这个片段中有一个错字。 显然
_jsonTextWriter!= Null必须处于第二个块的条件下。 由于
_jsonTextWriter为
nonnull ,因此
这段代码很可能因为没有崩溃而
处于危险之中 。 此外,流保持开放。
V3083 [CWE-367]事件'RuleRead'的不安全调用,可能会发生NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 FxCopConverter.cs 897
private void ReadRule(....) { .... if (RuleRead != null) { RuleRead(....); } .... }
事件处理不安全。 这是一个不重要的错误,可以通过例如Visual Studio技巧轻松修复。 这是IDE建议的替代品:
private void ReadRule(....) { .... RuleRead?.Invoke(....); .... }
只需几秒钟即可修复它,但分析器将不再抱怨它,并且IDE不会突出显示该代码。 另一个类似的错误。
- V3083 [CWE-367]事件'ResultRead'的不安全调用,可能会发生NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 FxCopConverter.cs 813
V3095 [CWE-476]在验证是否为null之前使用了“ v1Location”对象。 检查行:333、335。SarifVersionOneToCurrentVisitor.cs 333
internal Location CreateLocation(LocationVersionOne v1Location) { .... string key = v1Location.LogicalLocationKey ?? v1Location.FullyQualifiedLogicalName; if (v1Location != null) { .... } .... }
作者认为
v1Location引用可能为null,并添加了适当的检查。 鉴于以上内容,我们可以看到此引用未经任何检查即可处理。 细心的重构? 好吧,你永远不会知道。
V3125 [CWE-476]在针对null验证了'v1StackFrame'对象之后,使用了该对象。 检查行:1182,1171。SarifVersionOneToCurrentVisitor.cs 1182
internal StackFrame CreateStackFrame(StackFrameVersionOne v1StackFrame) { StackFrame stackFrame = null; if (v1StackFrame != null) { stackFrame = new StackFrame { .... }; } stackFrame.Location = CreateLocation(v1StackFrame.FullyQualifiedLogicalName, v1StackFrame.LogicalLocationKey, ....); return stackFrame; }
与往常一样,情况相反。 首先,检查
v1StackFrame引用是否为
null ,然后将检查误入歧途。 但是这种情况有一个重要的警告:
v1StackFrame和
stackFrame变量在逻辑上是相关的。 请参见,如果
v1StackFrame为
null ,则不会创建
StackFrame对象,而
stackFrame将保持为
null。 随后由于调用
stackFrame.Location而导致程序崩溃,因为此处没有检查。 因此,甚至不会出现分析器指出的危险的
v1StackFrame使用情况。 仅当您将
nonnull v1StackFrame值传递给
CreateStackFrame方法时,此代码才有效。 我怀疑调用者代码以某种方式控制了它。
CreateStackFrame调用看起来像这样:
Frames = v1Stack.Frames?.Select(CreateStackFrame).ToList()
CreateStackFrame用作选择器。 此处不检查传递的引用是否为
null 。 也许,在填充
Frames集合时,它(写空引用)受到控制,但是我并没有深入研究。 结论已经很明显了-该代码需要作者的注意。
结论
如您所见,这篇文章并不长,但是我希望您喜欢这篇简短的文章:)以防万一,您可以随时
下载我们的分析器来搜索您自己或某人的项目中的错误。
最后,一个小公告:我的下一篇文章将介绍我和我的同事在2019年的项目中发现的最有趣的错误。 再见!
要了解有关新博客文章的更多信息,欢迎您订阅以下频道: