Clang Static Analyzer (3) Cppcheck 原创 精华

zhushangyuan_
发布于 2022-12-7 18:02
浏览
2收藏

Clang Static Analyzer (3) Cppcheck

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

1、Cppcheck介绍

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

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

1.1、Cppcheck功能特性

  • 独特的代码分析,可检测代码中的各种错误。

  • 命令行界面和图形用户界面都可用。

  • Cppcheck非常注重检测未定义的行为。

1.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) {}
}

1.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]

^

3.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

3.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.配置问题,建议在配置期间仅启用这些

4.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

6、参考站点

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2023-8-25 14:31:23修改
5
收藏 2
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

跟着楼主学完整个系列,成为解决代码疑难杂症好手!


回复
2022-12-8 09:48:21
喝一大口可乐
喝一大口可乐

光是"只检查编译器检查不出来的缺陷"这个功能就很让人想去使用了

回复
2022-12-9 11:22:00
笨笨的婧婧
笨笨的婧婧

这个工具JAVA可以使用吗

回复
2022-12-12 10:45:15
zhushangyuan_
zhushangyuan_ 回复了 笨笨的婧婧
这个工具JAVA可以使用吗

不可以 文中提到的都是面向C/C++,Objective-C的。针对Java应该有类似的工具,例如,​​https://snyk.io/code-checker/java/​​。 还没有深入接触

回复
2022-12-12 11:03:44
回复
    相关推荐