1、 2023 年第 9 期135信息技术与信息化电子与通信技术基于 Cppcheck 的嵌入式静态代码扫描工具应用和规则扩充陈 聪1 李晓龙1 林 卓1CHEN Cong LI Xiaolong LIN Zhuo 摘要 嵌入式软件开发过程中代码规范非常重要,为加速研发效率,提前发现代码问题,合理使用嵌入式静态代码扫描工具十分重要。首先阐述嵌入式静态代码扫描的相关概念和特点;其次详细介绍了 Cppcheck嵌入式静态代码扫描工具的使用和结果分析,并对比其他同类工具;然后详细说明 Cppcheck 缺陷扫描规则和扩充实践;最后结合 Jenkins 进行自动化代码扫描和分析。使用 Cppcheck 并
2、自定义缺陷规则到结合自动化工具,从而使嵌入式代码静态扫描工作能够高效快捷地完成。关键词 C/C+;静态代码扫描;自定义规则扩充;正则表达式;自动测试;持续集成 doi:10.3969/j.issn.1672-9528.2023.09.0291.航空工业西安航空技术计算技术研究所 陕西西安 7100681 嵌入式静态代码扫描1.1 静态代码扫描介绍对于代码的扫描主要划分为动态扫描和 静态扫描两种。其中动、静是指是否需要运行被测试的代码。动态扫描需要在代码运行时进行,通常对过程数据进行测试值指定,通过运行测试后查看测试结果和预期是否相符来判读代码是否有缺陷、漏洞。静态代码扫描不需要运行代码,主要进
3、行代码逻辑 、语法、编码规范等的检查,可在研发前期提前暴露代码设计缺陷,提高软件编码质量,避免缺陷推迟到研发阶段后期暴露带来的研发成本上升等问题。由于不用运行代码,静态扫描可以对部分代码执行,并不关注动态编译路径之类的复杂状态,不需要提前准备测试值和预期值表,只用基于静态扫描规则中的缺陷模式进行自动化匹配分析,方便自动化测试来完成任务,自动化运行时间短,缺陷定位准确,更快捷发现问题。1.2 嵌入式静态代码扫描特点嵌入式开发实现了在特定硬件环境上开发和构建特定可编程软件系统的综合技术。目前主流的嵌入式平台分为ARM、DSP 和 FPGA 平台,嵌入式软件广泛应用于航天、航空、通信、轨道交通等领域
4、,对于软件的安全性、可靠性要求高。与遵循一般的工程化软件开发流程相比,嵌入式开发流程包含需求分析、体系结构设计、软硬件设计、系统集成、系统测试、产品生产等,要求遵循规范的技术、文档模式,软件硬件相综合,而 C/C+是可以对硬件进行操作的语言,相对汇编语言更容易学习、灵活高效、有可移植性,所以嵌入式开发以 C/C+为主要开发语言,在对嵌入式代码进行扫描时,主要考虑 C/C+语言扫描工具。由于 C/C+对语法限制不严格,允许变量类型隐式转换,对数组下标越界不做检查,虽然开发效率高但是数据安全性弱,需要进行静态扫描来完善代码安全性。2 Cppcheck 嵌入式静态代码扫描工具2.1 Cppcheck
5、 介绍 Cppcheck是一种开源的 C/C+代码缺陷静态检查工具。不同于 C/C+编译器及很多其他分析工具,Cppcheck 的核心是基于缺陷模式匹配的方式,通过正则表达式定位并提取出程序中的目标缺陷,从而对非标准代码(例如不同编译器的扩展代码、内联汇编代码等)进行检查。2.2 Cppcheck 应用分析2.2.1 GUI 图形界面展示Windows 安装版本中,点击 cppcheckgui.exe 就可以看到GUI 图形化界面展示,如图 1 所示,选择目录栏的分析按钮,就可以进行对于文件或者目录的代码扫描。图 1 Cppcheck 图形界面2.2.2 常用运行、配置命令(1)扫描一个文件:
6、cppcheck fi le1.c 代 表扫描一个名2023 年第 9 期136信息技术与信息化电子与通信技术 为 fi le1.c 的文件。(2)扫描一个文件夹下的所有文件:cppcheck path 代表扫描 path 路径下所有文件。(3)手动检查文件或者使用项目文件:可以通过手动检查指定的文件/文件夹来检查和设置,或者是使用 CMake或者 Visual Studio 这样的开发环境。(4)基于文件筛选来扫描文件或者文件夹:c ppcheck src/-fi le-fi lter=src/test*代表扫描 src/路径下所有以“test”开头的文件或者文件夹。(5)扫描时排除某个文件
7、或者文件夹:有两种方法,一种是指提供要扫描的路径 cppcheck src/a src/b 代表只扫描src/a 和 src/b 路径下的文件或者文件夹;另一种是用-i 指定要排除的路径 cppcheck-isrc/c src 代表扫描 src 路径下非 c 的所有文件,排除多个路径使用 cppcheck-isrc/b-isrc/c。(6)增量式分析文件夹:mkdir b cppcheck-cppcheck-build-dir=b src 第一次扫描 src 路径下所有文件,并将扫描结果存在 b 路径下。再运行一遍 cppcheck-cppcheck-build-dir=b src 第二次运行
8、时速度会快,只扫描有变动的文件,没有变动的文件扫描结果被复用。(7)扫描结果以 XML 格式输出:c ppcheck-xml fi le1.c e rr.xml 或者 c ppcheck-xml-output-fi le=err.xml fi le1.c 代表扫描名为 fi le1.c 的文件。目前支持 XML 两种输出格式,旧版 version 1 和新版 version 2,默认是 version2,可以用-xml-version=1 或者-xml-version=2 来切换。(8)扫 描 时 抑 制 某 些 错 误 的 生 成:抑 制 即 为 过滤 掉,抑 制 的 错 误 类 型 描 述
9、 可 以 分 为 后 面 三 类 error id:fi lename:line、error id:fi lename2 和 error id。具 体使用方法有以下几种。直接使用命令 cppcheck-suppress=memleak:src/fi le1.cpp src/意为扫描 src 路径下所有文件时,抑制 src/fi le.cpp 里面的 memleak 错误。将要抑制的内容写成 XML 格式文件,然后运用cppcheck-suppress-xml=suppressions.xml src/在扫描中导入要抑制的 XML 文件。具体 XML 文件内容格式如下。uninitvarsrc/
10、fi le1.c10var2.2.3 结果输出展示默认情况下,只显示错误信息,可以通过-enable 命令来启用更多检查,例如:cppcheck-enable=all fi le1.c 代表启用所有类型输出结果cppcheck-enable=error,warning fi le1.c 代表启用 error 和warning 类型的输出结果Cppcheck 的显示信息类型 severity 如下。error 错误:当代码执行到这里时发现未定义的操作或者其他错误,例如内存泄漏、资源泄漏等。warning 警告:当代码执行到这里时可能会发现未定义的操作。style 风格问题:风格相关的问题,例如未
11、使用的函数、冗余代码、常量性、运算符优先级、可能错误等。performance 性能问题:基于常识的运行时性能建议,但即使修复了这些问题,也不能保证运行时性能得到任何可度量的提高。portability 可移植性警告:实现定义的行为,64 位的可移植性。一些未定义的操作可以在不同的编译器中运行出不同结果。information消息信息:配置问题,与语法的正确性不相关,但是使用 Cppcheck 配置可以提升效果。结果显示分析样例:Checking D:Violations.c.D:Violations.c:57:9:error:Found a exit path from function w
12、ith non-void return type that has missing return statement missingReturn *output=10u;表示在 Violations.c 文件的第 57 行第 9 个列位置出现了一个 error 错误,具体类型为 missingReturn,具体说明是“Found a exit path from function with non-void return type that has missing return statement.”这个结果可以是直接显示在命令行中、GUI 页面中,也可以保存到 XML 格式结果文件内:202
13、3 年第 9 期137信息技术与信息化电子与通信技术.由于以上的 XML 类型结果显示不方便阅读、不直观,所以可以使用 Python 和 Pygments 模块来将 XML 转化为更加便于阅读展示的格式。具体使用需要安装 P ython 环境,通过 pip install P ygments 来安装 pygments 模块,该模块可以实现将代码格式化为 H tml 代码、图片、rtf 文件等多种模式,github 中 danmar/cppcheck 源代码里有一个名为 h tmlreport 的文件夹,通过代码基于 Pygments 实现将 XML 转化为 Html格式,使用时可以下载该代码,
14、运行“h tmlreport/cppcheck-html-report -help”命令获取具体使用说明,以下是一个简单样例:cppcheck -xml fi le1.c err.xml htmlreport/cppcheck-html-report -file=err.xml -report-dir=test1 -source-dir=.表示将 err.xml 转化成 test1 文件夹下的 html 文件,源代码可以在当前路径下找到。2.3 其他静态代码扫描工具2.3.1 Flawfi nderF lawfi nder 是一款开源的 C/C+静态分析工具,根据内部字典数据库进行对比搜索,匹
15、配简单的缺陷和漏洞,简单快速。通过命令行进行扫描,可以将分析结果导出为 CSV 文件。它是基于 Python 语言开发的工具,所以要安装 Python环境。如果遇到过文件中有编码异常字符 Python 无法读出的问题,会停下扫描并报错解析错误。2 .3.2 Klocwork Klocwork Insight 是一款商用代码静 态、动态检测工具,能够分析 C/C+、Java 和 C#代码,生成代码问题报告、缺陷报告,以浏览器格式查看。其主要特点是分析的缺陷类型全面,准确性高,是自动化的、适应大规模代码的和准确的自动分析工具。它不仅能够分析质量缺陷,而且能够分析安全漏洞。2.3.3 Testbed
16、T estbed 是一款商用代码静态、动态检测工具,支持Ada、C/C+、Cobalt、Fortran 等,能够实现对于嵌入式语言的静态扫描和动态扫描,并生成结果报表。静态分析可以验证代码是否符合一些成熟的软件编程标准,例如欧洲防务标准 DERA、汽车软件标准 MISRA 等,还可以分析代码中全局变量、局部变量和过程参数的使用状况,能够自动对于源代码进行插装、覆盖率分析、断言分析、测试用例分析。3 Cppcheck 缺陷扫描规则扩充 3.1 正则表达式正则表达式是通过描述一种字符串匹配的模式,来检查某字符串中是否包含匹配的目标字符串,还可以对于匹配的字符串进行提取或 者替换。构建正则表达式方法
17、和创建数学表达式方法类似,是通过多元字符和运算符的结合表达来构建模板进行匹配。常用的字符方法有:x yz 表示配置.中所有 xyz 字符,不限制字符顺序;xyz 表示配置.中所有非 xyz 的字符;A-Z 表示配置中所有大写字母;a-z 表示配置中所有小写字母;.表示匹配换行符 n r 之外的任何字符;sS 表示匹配所有字符,其中 s 指匹配包括换行符在内的所有空白符,S 则与 s 完全相反,表示匹配非空白符,不 包括换行符;w 表示匹配字母、数字、下划线,W 则与 w相反,表示匹配非 字母、非数字、非下划线,但不包括换行符。3.2 扩充自定义缺陷规则Cppcheck 创建自定义缺陷规则方法分
18、两种,第 一种是创 建缺陷规则正则表达式,第 二种就是创建一个 X ML 的规则文件。例如要在第 21 行有已知缺陷的 testfi le.cpp 文件中找到错误格式为 if(p)free(p);,按 照第一种方法创建缺陷规则正则表达式,则 运行命令格式为:cppcheck -rule=if(p)free(p);testfi le.cpp这样会出现日志结果如下:Checking testfi le.cpp.testfi le.cpp:21:(style)found if(p)free(p);按照第二种方式,则文件里面主要包含两部分内容,要检索的缺陷规则正则表达式(pattern)和当匹配了该缺
19、陷后需要报的错误信息(summary)。XML 规则文件取名为t estxml.rule 如下:i f(p)free(p);redundantCondition style R edundant condition.It is valid to free a NULL pointer.则运行命令格式为:cppcheck -rule-fi le=testxml.rule testfi le.cpp这样会出现日志结果如下:Checking testfi le.cpp.testfi le.cpp:21:(style)Redundant condition.It is valid to free a
20、NULL pointer.当正则表达式和 XML 结合时,由于在 XML 中“”和“&”都是非法字符,但是在正则表达式中可能出现,需要使用术语 CDATA 来表示正则表达式里的内容不应该由 XML来解析,即标出需要被 XML 解析器忽略的文本部分,具体2023 年第 9 期138信息技术与信息化电子与通信技术使用格式是:。例如下面是一个用于匹配else部分是空的XML规则文件。normal EmptyElseBlock style Empty else statement can be safely removed.3.3 结 合 Jenkins 进行自动化代码扫描代码扫描工具很重要的功能就是
21、与代码仓库工具、自动化流水线工具、测试管理工具协同工作,实现自动化测试。当代码被开发人员完成,提交到代码仓库,请求合并分支时,就应当开始对于所提交的代码进行快速静态扫描,以发现代码中的逻辑、语法、编码规范上的错误,并将结果反馈给代码提交人员,帮助开发者、代码审查者快速检查代码并加以完善。开源的持续集成引擎工具 J enkins 已经支持了配套Cppcheck 的插件 Cppcheck Plug-in,如 2 图所示,可以在Jenkins 插件市场直接搜索集成。图 2 集成 Cppcheck 插件示例这样集成之后创建 Jenkins 自由风格(FreeStyle)的项目中可以直接选择构建后操作步
22、骤(post build steps)增加publish Cppcheck result,即对于 Cppcheck 结果进行分析、统计、展示。首先在构建中使用命令 cppcheck-xml-version=2-output-fi le=cctest-cppcheck.xml TestFolderPath。可以基于结果要求设置扫描阈值、需要统计的错误类型、展示图的设置,如图 2 所示,这样流水线运行到 Cppcheck 扫描后会基于扫描阈值来判读本次扫描结果是否符合规则,符合则流水线显示成功,不符合则显示失败并报错是因为结果超出阈值而引起。图 3 显示的是集成了 Cp pcheck Result
23、s 之后的 Jenkins 自由风格(FreeStyle)的项目在配置了图 2 所示的各种设置:扫描阈值、需要统计 的错误类型、展示图的设置后,对于最近一次结果的展示,通过 Cppcheck Results 图表和 CppCheck Trend 图像展示。图 3 扫描结果示例对于每一次构建,可以点击进去看到详细的 Cppcheck Results 展示,如图 4 所示包含统计的错误类型和数量和Cppcheck 版本,还有十分重要的详细错误信息展示。图 4 Cppcheck Result 详细结果示例4 结语文章阐述的基于 Cppcheck 嵌入式静态代码扫描工具应用、自定义缺陷规则扩充,以及与自动化流水线工具相结合,能够很好解决嵌入式软件开发领域上静态代码扫描的任务,快速高效发现代码问题,使用简单方便,具有很好的实际应用价值。参考文献:1 张仕金,尚赵伟.Cppcheck 的软件缺陷模式分析与定位J.计算机工程与应用,2015,51(3):69-73.2 张仕金,尚赵伟.基于区间集的 Cppcheck 数组边界缺陷J.计算机应用,2013,33(11):3257-3261.3 王建斌,刘臻.基于静态分析的缺陷模式匹配研究 J.信息安全研究,2018,4(4):359-363.(收稿日期:2023-07-31 修回日期:2023-08-04)