SARIF SDK及其错误

图片2


今天,我们正在测试另一个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 { .... /// <summary>The MIME type to use when no better MIME type is known.</summary> public static readonly string Default = Binary; .... /// <summary>The MIME type for binaries.</summary> public static readonly string Binary = "application/octet-stream"; .... } 

默认字段初始化为尚未收到值的另一个字段的值。 结果, 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 (....) { // Replace the token with an embedded hyperlink. messageText = messageText.Replace(....); } else { // Replace the token with plain text. messageText = messageText.Replace(....); } .... } 

在此,分析器立即通​​过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 ,然后忘记进行检查。 但是有一个重要的警告: v1StackFramestackFrame变量在逻辑上是相关的。 请参阅,如果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及其错误

Source: https://habr.com/ru/post/zh-CN479482/


All Articles