6

Clang Static Analyzer (3) Cppcheck

 1 year ago
source link: https://ost.51cto.com/posts/19842
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

#盲盒+码# Clang Static Analyzer (3) Cppcheck 原创 精华

【本文正在参加「盲盒」+码有奖征文活动】https://ost.51cto.com/posts/19288

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、参考站点

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2022-12-8 09:20:50修改

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK