静态分析工具Clang Static Analyzer (3) Cppcheck

系统 OpenHarmony
Cppcheck 是 C/C++ 代码的静态分析工具。它提供独特的代码分析技术来检测缺陷,不检查代码中的语法错误,只检查编译器检查不出来的缺陷,并专注于检测未定义行为错误和危险的编码结构。

想了解更多关于开源的内容,请访问:

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

前文介绍CodeChecker时,使用到了Cppcheck,我们来看看这个工具是什么,如何使用。

1、Cppcheck介绍

Cppcheck 是 C/C++ 代码的静态分析工具。它提供独特的代码分析技术来检测缺陷,不检查代码中的语法错误,只检查编译器检查不出来的缺陷,并专注于检测未定义行为错误和危险的编码结构。其目标是减少误报、零误报,检查代码中真正的错误。Cppcheck旨在能够分析C / C++代码,即使它具有非标准语法(在嵌入式项目中很常见)。

Cppcheck既有开源版本,也有具有扩展的功能和支持的Cppcheck Premium版本,。可以访问 www.cppchecksolutions.com 以获取商业版本的更多信息和购买选项。

(1)Cppcheck功能特性

  • 独特的代码分析,可检测代码中的各种错误。
  • 命令行界面和图形用户界面都可用。
  • Cppcheck非常注重检测未定义的行为。

(2)Cppcheck特有的分析技术

使用多个静态分析工具可能是一个好主意,每个工具都有独特的功能特性。这在研究中已经证实。那么Cppcheck的独特之处在哪里?

Cppcheck使用不健全的流程敏感分析,其他几种分析器使用基于抽象解释的路径敏感分析,这也很好,但既有优点也有缺点。从理论上讲,根据定义,路径敏感分析比流量敏感分析更好。但实际上,这意味着Cppcheck将检测其他工具无法检测到的错误。在Cppcheck中,数据流分析不仅是“前向”的,而且是“双向的”。大多数分析器会诊断这一点,可以确定数组索引为 1000 时会出现溢出。

void foo(int x)
{
int buf[10];
if (x == 1000)
buf[x] = 0; // <- ERROR
}

Cppcheck还将诊断此问题,当x等于1000时,赋值时也会出现数组越界。

void foo(int x)
{
int buf[10];
buf[x] = 0; // <- ERROR
if (x == 1000) {}
}

(3)未定义行为Undefined behaviour

  • Dead pointers 死指针
  • Division by zero 除以零
  • Integer overflows整数溢出
  • Invalid bit shift operands无效的位移操作数
  • Invalid conversions无效转化
  • Invalid usage of STLSTL 的用法无效
  • Memory management内存管理
  • Null pointer dereferences空指针解引用
  • Out of bounds checking越界检查
  • Uninitialized variables未初始化的变量
  • Writing const data写入常量数据

2、Cppcheck安装

Cppcheck也可以从各种包管理器安装;但是,您可能会得到一个过时的版本。为了获取更新版本,可以访问https://github.com/danmar/cppcheck进行源码安装。

  • Debian:
sudo apt-get install cppcheck
  • Fedora:
sudo yum install cppcheck
  • macOS:
brew install cppcheck

3、使用入门

第一个测试程序,这里有一段简单的代码,我们命名为file1.c。

int main()
{
char a[10];
a[10] = 0;
return 0;
}

执行cppcheck file1.c,输出如下:

zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ cppcheck file1.c
Checking file1.c ...
[file1.c:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.

我们再试试上面说的例子,保存到file2.c。

void foo(int x)
{
int buf[10];
buf[x] = 0; // <- ERROR 1
if (x == 1000) {
buf[x] = 0; // <- ERROR 2
}
}

执行cppcheck --enable=all file2.c,输出如下。可以看得出有2个warning和3个style问题。

zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ cppcheck --enable=all file2.c
Checking file2.c ...
file2.c:4:6: warning: Either the condition 'x==1000' is redundant or the array 'buf[10]' is accessed at index 1000, which is out of bounds. [arrayIndexOutOfBoundsCond]
buf[x] = 0; // <- ERROR 1
^
file2.c:5:9: note: Assuming that condition 'x==1000' is not redundant
if (x == 1000) {
^
file2.c:4:6: note: Array index out of bounds
buf[x] = 0; // <- ERROR 1
^
file2.c:6:8: warning: Either the condition 'x==1000' is redundant or the array 'buf[10]' is accessed at index 1000, which is out of bounds. [arrayIndexOutOfBoundsCond]
buf[x] = 0; // <- ERROR 2
^
file2.c:5:9: note: Assuming that condition 'x==1000' is not redundant
if (x == 1000) {
^
file2.c:6:8: note: Array index out of bounds
buf[x] = 0; // <- ERROR 2
^
file2.c:4:10: style: Variable 'buf[x]' is assigned a value that is never used. [unreadVariable]
buf[x] = 0; // <- ERROR 1
^
file2.c:6:12: style: Variable 'buf[x]' is assigned a value that is never used. [unreadVariable]
buf[x] = 0; // <- ERROR 2
^
file2.c:1:0: style: The function 'foo' is never used. [unusedFunction]

^

(1)检查文件夹

Cppcheck支持检查文件夹中的所有文件。通常一个项目会有许多源文件,如果需要同时检查,Cppcheck 可以检查文件夹中的所有文件.如果 path 是一个文件夹,cppcheck 将递归检查这个文件夹中的所有源文件。

cppcheck path

示例输出如下:

zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ cppcheck .
Checking file1.c ...
file1.c:4:4: error: Array 'a[10]' accessed at index 10, which is out of bounds. [arrayIndexOutOfBounds]
a[10] = 0;
^
1/4 files checked 12% done
Checking file2.c ...
2/4 files checked 38% done
Checking hello.c ...
hello.c:2:13: error: Division by zero. [zerodiv]
int x = 7 / 0; // bug here
^
3/4 files checked 50% done
Checking simple.c ...
simple.c:16:11: error: Division by zero. [zerodiv]
return 5/(x-x); // warn
^
simple.c:12:5: error: Uninitialized variable: s [uninitvar]
f(s); // warn
^
simple.c:12:5: error: Uninitialized struct member: s.x [uninitStructMember]
f(s); // warn
^
4/4 files checked 100% done

(2)手动检查文件或使用项目文件

使用 Cppcheck 可以手动检查文件,通过指定文件/文件夹来检查和设置,或者可以使用一个工程文件(cmake/visual studio)。

使用项目文件更快,因为它只需要非常少的配置。

手动检查文件可以更好的控制分析。

不一定哪种方法会有最好的结果,建议尝试一下,可能会得到不同的结果,发现大多数 bug 需要使用这两种方法。

4、严重级别Severities

输出信息中的严重级别支持如下几种:

  • error 错误
    when code is executed there is either undefined behavior or other error, such as
    a memory leak or resource leak。发现未定义行为或其他错误,例如内存泄露、资源泄露
  • warning告警
    when code is executed there might be undefined behavior可能有未定义行为
  • style样式风格
    stylistic issues, such as unused functions, redundant code, constness, operator
    precedence, possible mistakes.样式问题,例如未使用行数,冗余代码,常量性,操作符优先级,可能的错误等
  • performance性能
    run time performance suggestions based on common knowledge, though it is
    not certain any measurable speed difference will be achieved by fixing these
    messages.这些建议只是基于常识,即使修复这些消息,也不确定会得到任何可测量的性能提升。
  • portability可移植性
    portability warnings. Implementation defined behavior. 64-bit portability. Some
    undefined behavior that probably works “as you want”, etc.可移植性警告。64 位的可移植性,代码可能在不同的编译器中运行结果不同。
  • information信息
    configuration problems, which does not relate to the syntactical correctness, but
    the used Cppcheck configuration could be improved.配置问题,建议在配置期间仅启用这些

(1)启用消息

默认情况下,只显示错误消息,可以通过 --enable 命令启用更多检查。

  • 启用警告消息:
cppcheck --enable=warning file.c
  • 启用性能消息:
cppcheck --enable=performance file.c
  • 启用信息消息:
cppcheck --enable=information file.c

由于历史原因 --enable=style 可以启用警告、性能、可移植性和样式信息。当使用旧 XML 格式时,这些都由 style 表示:

cppcheck --enable=style file.c
  • 启用警告和性能消息:
cppcheck --enable=warning,performance file.c
  • 启用 unusedFunction 检查。这不能通过 --enable=style 启用,因为不会在库中正常工作。
cppcheck --enable=unusedFunction file.c
  • 启用所有消息:
cppcheck --enable=all

5、常见错误修改

  • 隐式构造问题。

示例: (style) Class ‘Slice’ has a constructor with 1 argument that is not explicit。

解决方法:在Slice构造函数前加上explicit,使其必须显示构造,当然这种有时并非必须显示构造。

  • 变量未初始化问题。

示例:(warning) Member variable ‘TableFileCreationInfo::file_size’ is not initialized in the constructor.

解决方法:在构造函数中加入变量初始值。

  • 变量/函数未使用问题。

示例:(style) Unused variable: output。

示例:(style) The function ‘rocksmt_wal_iter_status’ is never used。

解决方法:考虑后期是否还需要,不需要的及时删除,需要的保留。

  • raw loop问题。

示例:(style) Consider using std::fill algorithm instead of a raw loop. [useStlAlgorithm]。

示例:(style) Consider using std::transform algorithm instead of a raw loop. [useStlAlgorithm]。

解决办法:将循环便利替换为STL标准库算法函数。

  • 引用传递问题。

示例:(performance) Function parameter ‘f’ should be passed by reference.

解决办法:在声明function时,能用引用传递的尽量使用引用传递,尽量避免值传递。

  • const参数问题。

示例:(performance) Function parameter ‘s’ should be passed by const reference. [passedByValue]。

解决办法:形参s前加上const,在函数中未被修改的变量,尽量声明为const。

想了解更多关于开源的内容,请访问:

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

责任编辑:jianghua 来源: 51CTO 开源基础软件社区
相关推荐

2022-12-12 16:11:47

Clang-Tidy工具

2022-12-13 15:42:56

Clang-Tidy静态分析工具

2022-12-08 15:25:10

Clang分析工具CSA

2011-04-11 13:58:09

TCP

2021-06-08 13:56:34

工具静态代码

2017-04-19 12:05:59

2016-05-17 13:54:05

2021-07-29 06:37:55

KubernetesKubeLinter工具

2010-08-20 15:07:22

浮动静态路由

2016-03-29 14:54:36

2017-05-10 14:27:29

静态代码漏洞安全

2012-05-22 00:28:21

JavaJava开源开源工具

2013-10-31 11:08:15

2020-12-22 08:00:00

开发分析工具

2021-01-05 09:25:27

DockerSemgrep代码静态分析工具

2024-01-08 13:47:00

代码分析工具

2009-11-27 15:13:00

PHP静态变量stat

2021-01-04 07:57:07

C++工具代码

2021-12-27 11:11:30

LLVMSPIR-V后端

2023-09-14 22:57:52

点赞
收藏

51CTO技术栈公众号