
今天,我们正在测试另一个Microsoft高质量项目,其中我们仍然尝试使用PVS-Studio英勇地搜索错误。 SARIF代表静态分析结果交换格式,是一种标准(文件格式),用于与其他工具进行交互和交换静态分析器的结果:IDE,全面的代码验证和分析工具(例如SonarQube),持续集成系统等 SARIF SDK分别包含用于支持SARIF和支持文件的.NET开发人员工具。
SARIF起源于微软,现在是由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 { ....
默认字段初始化为尚未收到值的另一个字段的值。 结果,
Default将收到
字符串类型的默认值-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 ,将尝试从??运算符的右侧返回该值,这将导致通过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; .... }
在条件语句的两个并行分支中::它们使用潜在的null
格式字符串引用使用不安全的访问选项。
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 (....) {
在此,分析器立即通过
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(); }
这是一个错字。 显然,第二个
if块的条件必须为
_jsonTextWriter!= Null 。 这段代码的危险是它很可能不会崩溃,因为
_jsonTextWriter并非完全为
null 。 但与此同时,流程保持开放。
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的危险使用甚至无法达到目的。 仅
当将非零
v1StackFrame值
传递给CreateStackFrame方法时,此代码才
有效 。 我怀疑这在调用代码中受到了某种控制。
CreateStackFrame调用如下所示:
Frames = v1Stack.Frames?.Select(CreateStackFrame).ToList()
CreateStackFrame用作选择器。 没有验证传递的
空相等引用。 也许在填充
Frames集合时某处(记录零引用)是受控的,但我没有那么深入。 结论已经很明显了-该代码需要作者的注意。
结论
这篇文章的篇幅很小,但没有人会无聊:)我提醒您,您可以随时
下载我们的分析器,以独立查找您自己或他人项目中的错误。
最后,一个小公告:我的下一篇文章将介绍我和我的同事在2019年的C#项目中发现的最有趣的错误。 关注我们的
博客 。 待会见!
要了解新的博客文章,您可以订阅以下频道:

如果您想与讲英语的读者分享这篇文章,请使用以下链接:Sergey Khrenov。
SARIF SDK及其错误 。