静态分析工具正越来越多地集成到软件开发过程中。在过程中保存来自编译器的数据、更改历史记录和错误信息,而不是作为后置代码步骤,可以提高静态分析的效率。
高级静态分析工具在嵌入式系统开发中变得越来越重要。远远超出实际上是编码风格检查器的旧静态分析工具,新工具静态分析源程序的控制和数据流,从而检测错误和漏洞,例如潜在的缓冲区溢出、未初始化变量的使用、通过空指针访问,以及对安全攻击(SQL 注入、跨站点脚本等)的敏感性。
然而,这些先进的工具提出了几个问题。首先,工具需要了解被分析程序的语义——也就是说,它们必须编译程序——以执行所需的控制和数据流分析。为此,它们必须紧密集成到构建环境中,以便在编译时识别和使用所有可能需要的包含文件或其他规范模块。其次,这些高级工具产生的输出量可能令人望而生畏,每条诊断信息都需要仔细审查,以确定它是否反映了一个真正的问题,如果是,如何解决它。
将静态分析工具与软件开发工具链更紧密地集成可以缓解这两个挑战。在第一种情况下,将静态分析工具与编译器紧密集成在很大程度上消除了构建环境问题,并使用户界面变得简单和熟悉。在第二种情况下,可以通过将所有输出存储在历史数据库中来管理大量输出,从而允许程序员专注于已知良好版本和源当前状态之间的增量,而不是在每一步处理所有消息。
与编译器集成
超越简单语法检查的静态分析工具通常需要编译器前端的大部分功能,以便它可以根据程序的语义进行分析。这是因为相同的句法形式通常可以根据其成分的含义有不同的解释。例如,Ada 中的表达式 F(N) 可能(除其他外)是数组引用、函数调用或类型转换。
访问程序的底层语义允许该工具跟随程序中出现的每个名称回到引入该名称的声明,即使存在重载、通用模板或重命名也是如此。该工具将知道每个对象和每个表达式的类型,并将识别任何隐式运行时检查发生的位置。这些隐式运行时检查可能包括对取消引用空指针的检查和对数组边界之外的索引的检查。即使没有隐式运行时检查的语言也可以将某些运行时操作定义为具有未指定的语义,例如整数算术溢出或超出范围的数组索引。静态分析工具需要知道语言语义何时允许这种未指定(因此不可预测)的行为。
由于需要包含编译器前端的强大功能,许多静态分析工具都建立在感兴趣的语言的现有编译器技术之上。不幸的是,该工具的构建者选择的编译器技术可能与该工具的客户使用的编译器无关。发生这种情况时,静态分析工具可能无法处理客户编写的代码。
例如,如果客户程序使用编译器特定的功能(例如中断处理或特殊的内存映射工具),则无法保证静态分析工具的底层完全或以同样的方式支持它们。编译器前端技术。即使对于可移植代码,客户的编译器和静态分析工具的底层技术也可能存在不同的错误或对语言规则的细微不同的解释。即使解释匹配,编译程序的命令(控制源代码搜索路径、预处理器支持和其他功能的命令行开关)也可能有很大不同。因此,复杂程序的构建过程可能难以转换为对程序执行静态分析的生成过程。
为了解决这些问题,明确的解决方案是将高级静态分析引擎与客户使用的相同编译器技术集成。因此,静态分析引擎必须在某种程度上独立于任何特定编译器技术使用的中间表示,以便该工具可以轻松地适应支持多个编译器前端。
一种方法是让静态分析引擎拥有自己的中间表示,专门设计用于支持工具执行的高级分析。适应支持新的编译器前端需要编写一个转换模块,将编译器的中间表示(前端的输出)转换为静态分析引擎使用的程序表示。翻译模块将结果输出到文件中供以后使用。中间语言翻译器既可以链接到编译器前端,也可以作为独立程序运行,读取编译器的中间表示,转换它,然后写出分析引擎的中间表示。这个过程如图 1 所示。
图 1:中间语言翻译器读取编译器的中间表示,对其进行转换,然后写出静态分析引擎的中间表示。
当采用这种集成方法时,静态分析只是构建过程的另一部分,可以在编译期间执行,或者为了利用整个程序分析,在链接步骤期间执行。对用户的一个关键优势是调用静态分析工具只涉及向编译器和/或链接器提供额外的命令行开关。无需为该工具创建专门的构建脚本或维护两组源(一组与编译器一起使用,另一组与静态分析工具一起使用)。
与开发环境集成
由于软件开发通常通过 Eclipse 等图形集成开发环境 (IDE) 进行,因此将静态分析工具和编译器集成到 IDE 中是很自然的。然后,程序员将立即熟悉该工具的整体界面,从而减少学习曲线并增加定期使用该工具的可能性。
静态分析工具生成的消息必须像编译器生成的错误或警告消息一样处理,并由用户以相同的方式管理和查看。鉴于正在使用多个 IDE,每个 IDE 都有自己的消息格式,静态分析工具将需要表示其消息,以便可以轻松地将它们转换为 IDE 期望的任何格式。
消息表示的自然选择是 XML,因为它使用标记的、自描述的方法来捕获消息特征。使用 XML 的一个附带好处是它有助于简化应用程序的国际化过程,从而可以以客户喜欢的自然语言显示消息。
与历史数据库集成
一旦高级静态分析工具与编译器和 IDE 集成,下一个问题就是处理此类工具可能提供的大量消息。因为高级静态分析工具正在寻找可能的运行时逻辑错误和安全漏洞,所以它们必须模拟运行时程序的执行(识别一组潜在的执行状态)并确定在什么条件下可能会达到不期望的状态。不幸的是,这很少是简单的“是”或“否”。有许多灰色阴影,其中脆弱性程度取决于工具可能未知或超出其分析能力的因素。
这个问题有时用健全性与精确性来表述。如果一个搜索有问题结构的工具能够识别出它正在寻找的所有问题(没有误报),那么它就被认为是可靠的。但稳健性通常是以牺牲精度为代价的。该工具可能会生成大量误报,这些误报是用于识别并非真正问题的警告或错误。考虑这个使用类 C 语法的简单示例:
int k, m, n;
... // Complicated code that assigns a positive value to m
... // and that does not assign to n
if (m《0){
k=n;
...
}
工具可能无法推断 if 语句上的 m《0 条件为假,因此可能会警告 if 语句的主体引用未初始化的变量 (n)。实际问题恰恰相反:if 语句的主体是永远不会执行的代码,有时称为死代码或无法访问的代码。
工具开发人员必须决定是选择健全性(确保没有未被检测到的实际违规)还是精度(确保所有报告的违规都是真正的错误)。当一个工具用于安全关键或高安全性系统时,天平就会倾斜。使用此类工具的开发人员必须有信心检测到所有违规行为。但这引发了前面提到的关于如何处理可能产生的大量误报的问题。当该工具应用于遗留软件(在应用静态分析工具之前开发的代码)时,这个问题尤其明显。对于大型应用程序,用户需要查看的消息数量可能令人望而生畏。
将高级静态分析工具与历史数据库集成,可以有效地使用该工具,最大限度地减少误报导致的问题,即使对于在使用该工具之前开发的复杂应用程序也是如此。关键概念是基线的概念以及该工具突出显示相对于此类基线的增量的能力。通过在历史数据库中记录每个工具运行的所有结果,该工具可以识别任何两次运行之间的增量(更改)。
数据变得更有用
为了使分析运行之间的比较有效地进行,消息必须是唯一可识别的,而不涉及特定的行号,可以从源代码的一个版本切换到另一个版本而无需任何重大更改。识别没有行号的消息的一种方法是记录消息的文本(或相应的 XML),以及它出现的函数的名称,如果消息的文本与同一功能中的一些先前消息。
假设消息使用这个与行号无关的唯一标识符作为关键字存储在数据库中,那么该工具可以轻松识别给定消息是新消息还是先前生成的消息。这使历史数据库的整体大小保持可管理。该工具不需要为工具的所有调用重复存储所有消息的文本,而只需要存储给定消息的文本一次,以及该消息出现的工具调用范围的指示(第一次运行生成消息的位置,以及第一次没有出现的运行)。
该历史数据库使用户界面可以直接显示或突出显示自指定基线以来的新消息。这使得该工具即使在具有大量遗留代码的大型应用程序上也能有效使用。应用程序的已知良好版本可以通过分析工具作为基线运行。可以分析应用程序的当前开发版本,并将分析此已知良好版本的结果作为基线。那些在开发版本上工作的人可以专注于与他们自已知良好版本以来所做的更改相关的任何消息,而不必费力地处理与遗留代码相关的消息。最终,可以致力于处理这些积压的消息,
与历史数据库集成的另一个好处是能够从查看分析结果的用户那里收集评论。在某些情况下,可能需要对特定消息进行大量调查以了解可能的影响。捕获这项工作很重要。历史数据库是记录用户学习内容的自然场所。
另外,如果用户确定所识别的代码是安全可靠的,历史数据库可以记录该给定消息应该从后续输出中被抑制,并且可以记录抑制该消息的支持理由。或者,如果需要更改识别的代码,历史数据库可以记录分配给问题的程序故障报告 (PTR) ID,从而允许问题跟踪系统和分析工具的历史结果之间的可追溯性。当该工具检测到具有关联 PTR ID 的消息消失时,可以将其配置为直接通知问题跟踪系统可以关闭关联的 PTR 记录。自动化关闭 PTR 的过程可以显着减轻通常负担过重的质量保证团队的负担。
静态分析作为开发过程的关键组成部分
随着应用程序变得越来越大和越来越复杂,高级静态分析工具在现代软件开发中发挥着关键作用,它显着减少了查找可能危及系统可靠性、安全性或安全性的错误和漏洞所需的工作量。但许多组织尚未充分利用这些工具,通常是因为将它们纳入日常软件开发过程(构建、回归测试和其他步骤)可能存在很高的进入障碍。
如前所述,两个重要的步骤可以减少这种进入障碍:工具与编译器技术和历史数据库的集成。这不仅仅是一个理论上的提议。CodePeer 是由 SofCheck 和 AdaCore 联合开发的高级静态分析工具,作为 Ada 的自动代码审查器。该工具已完全集成到 AdaCore 的 GNAT Pro Ada 开发环境中,并可通过 GNAT Programming Studio IDE 调用。
与编译器的集成在很大程度上消除了将源代码移植到分析工具的挑战。成功编译源代码的同一编译器前端还可以生成高级静态分析引擎进行更深入分析所需的中间表示。此外,相同的命令行开关、源代码结构和 make 文件可用于编译和静态分析代码。编译器前端将自动处理应用程序使用的任何特定于实现的功能。
降低进入壁垒的第二个主要步骤是与历史数据库的集成,这使得在大型系统上工作的开发人员可以专注于他们最近的更改,并将审查以前发布的遗留代码中的问题推迟到更合适的时间。此外,与数据库的集成允许开发人员记录审查工具输出的结果以及决定隐藏消息或将其归档为 PTR 的理由。最后,数据库会自动验证修复并关闭 PTR。通过这两个步骤,静态分析可以成为嵌入式软件开发人员工具箱中重要且高效的工具。
审核编辑:郭婷
-
嵌入式
+关注
关注
5064文章
18992浏览量
302570 -
编译器
+关注
关注
1文章
1617浏览量
49026
发布评论请先 登录
相关推荐
评论