转自:https://www.cnblogs.com/ChinaHook/p/4661030.html
https://www.cnblogs.com/ChinaHook/p/4661062.html
1、检查点
1、自动变量检查: 返回自动变量(局部变量)指针;
2、越界检查:数组越界返回自动变量(局部变量)指针;
3、类检查:构造函数初始化;
4、内存泄露检查;
5、空指针检查;
6、废弃函数检查;
7、其他。
2、Cppcheck 架构分析
2.1 总体类图
3、检查点cppcheck系统C++实现类
参数分析&外部输入
内部抽象classSetings
class Settings
{
…
std::string _append;
std::string userDefines;
std::list<std::string> _includePaths;
std::list<Rule> rules;
…
}
字符交互模式
CmdLineParser parser(&_settings);
4、Cppcheck总过程
4.1 预处理
预处理处理由Preprocessor类实现
执行Class Preprocessor::preprocess()
预处理阶段主要处理:
- 去多余空格
- 删除汇编代码
- 处理#Include及嵌套
- 统一预处理语句(例:#if define=> #ifdef)
- 提取预处理配置设置(configuration)
- 替换宏定义
4.2 Tokenize
解析代码成符号,由 class Tokenizer实现,实现接口 class Tokenizer::tokenize()
- 符号:+-*/;…等;
- 变量名;
- 函数名。
4.3 Simplify
目的:简化复杂代码, 统一化
由 class Tokenizer实现,实现接口 class Tokenizer::simplifyTokenList()
Simplify规则:对变量;对条件循环语句if 、for、while
5、Cppcheck 核心类
核心函数check()
处理入口,在此函数对输入代码进行初步分析处理,最后将代码传递给 CheckFile()。
核心函数CheckFile()
函数功能是分析一个代码文件, CheckFile()会将代码流做进一步的分析,做tokenize,simplify,处理后分析代码,报告错误。
Class cppcheck::check()函数 &class cppcheck:: CheckFile()函数的实现
Cppcheck 检查实现类check
Class check
protected:
const std::string _name;
const Tokenizer * const _tokenizer;
const Settings * const _settings;
ErrorLogger * const _errorLogger;
virtual void runChecks(****){ } //不是所有check子类都会实现
virtual void runSimplifiedChecks(***) = 0; //所有check子类都会实现
void reportError() //报告误差,所有check子类都会用到
virtual void getErrorMessages(****) = 0; //所有check子类都会实现,最终都会调用reportError()
Void runChecks()
主要是检查经过tokenize,但未经过simplify的代码流
Void runSimplifiedChecks()
主要是检查经过tokenize,但未经过simplify的代码流
用户扩展接口
CheckOther : public Check
……
virtual void runChecks(****){ } //实现
virtual void runSimplifiedChecks(***) = 0; //实现
添加检查函数方法:
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
{
CheckOther checkOther(tokenizer, settings, errorLogger);
// Coding style checks
checkOther.warningOldStylePointerCast();
checkOther.checkUnsignedDivision();
checkOther.addcheck();
…
}
6、流解析
解析函数中的可能的代码执行流, 函数实际执行中只会执行代码流中的一条流
分析: 分支语句 if-else ,switch-case
循环语句 while, do-while ,for
7、代码流举例
7.1 示例代码
int main(int argc,char ** argv)
{
std::string p_str= new std::string() ;
if(std::string == NULL)
{
return 0;
}
else
{
delete p_str;
}
return 0;
}
7.2 执行代码分析
7.2.1 执行路径1
int main()
{
string *p_str;
p_str= new string() ;
(p_str == NULL);
{
return 0;
}
}
7.2.2 执行路径2
int main()
{
string *p_str;
p_str = new string() ;
(p_str == NULL);
{
delete p_str;
}
return 0;
}
7.2.3 保留条件信息
#conditon ( )
#conditon_contrary ( )
int main()
{
string *p_str;
p_str= new string() ;
#conditon (p_str= = NULL);
{
return 0;
}
}
int main()
{
string *p_str;
p_str= new string() ;
#conditon_contrary (p_str= = NULL);
{
delete p_str;
}
return 0;
}
7.2.4 等价替换—if
If
if(condition)
{
…
}
2 paths
1-> (condition)
2-> (condition){ …}
If else
if(condition)
{
…
}
else
{
……
}
2 paths
1-> (condition){ …}
2-> (condition){ ……}
7.2.5 等价替换—switch
swtich( int_condition)
{
case 1:
codes_1
case 2:
codes_2
default:
codes_DF
}
N paths
1-> swith (int_condition) { codes_1 codes_2 codes_DF}
2-> switch (int_condition) { codes_2 codes_DF }
3-> switch (int_condition) { codes_DF }
7.2.6 等价替换—while
while ( condition)
{
codes
}
2 paths
1-> (condition) ;loop{ codes ;(condition) ;}
2-> (condition) ; //未进入循环语句
7.2.7 等价替换—do while
do
{
codes
}
while ( condition)
1 path
1-> loop{ codes ;(condition) ;}
7.2.8 等价替换—for
for( initial;condition;iterate)
{
codes;
}
2 path
1-> initial ; condition ;loop{ codes ; iterate; condition;}
2-> initial ; condition ;
8、算法思想
8.1 嵌套问题
代码嵌套关系复杂
解决方案 ->递归算法
8.2 空间问题
path多,而且会重复
解决方案 -> codeblock
8.3 codeblock
将连续执行的代码部分以代码块的方式组合。
多条路径共用重复的代码块
codePath<-codeBlock<-token
Codeblock 重用
9、内存泄露检查
函数内内存泄露检查
找出所有与内存分配相关的函数
找出与内存分配地址相关指针(传递性)
是否地址传递到外部空间
路径状态判断
内存泄露特征
内存申请成功&&代码路径上没有释放空间&&地址没有传递到外部空间
地址值传递到外部空间方法:
1.函数参数(指向指针的指针参数)
2.调用其他函数时当参数
3.返回值
4.类成员变量
5.全局变量
10、其他检查流程
10.1 其他解析
危险使用
使用指针前没有判断指针的值是否为空
重复释放
申请释放函数不一致
malloc-free
new-delete
new[]-delete[]
……
10.2 内存解析算法
10.2.1 pointerSet存放分配内存地址的指针
发生指针传递时加入新集合成员
指针被重新赋值时从集合中删除
10.2.2 检查集合中的指针
1.当做函数的参数使用
2.当做返回值
3.赋值给(指向指针的参数)
10.2.2 路径上的内存状态
UNSURE 申请内存结果未知
NULL 申请内存失败
OK 申请内存成功
DEALLOCATE 内存被释放
PASSOUT 内存地址被传递到其他窗口
10.3 条件判断解析
10.3.1 解析#condition(……)
OK NULL UNSURE NEVER
条件语句中常见逻辑符号&& || 及小括号()
((ptr>0)&&other&&other) => OK
((ptr>0)&&other||other) => UNSURE
((ptr>0)&&(other||other)) => OK
从左到右,深度遍历
10.3.2 条件判断解析算法
OK NULL UNSURE NEVER
(any) && UNSURE = any
(any) || UNSURE = UNSURE
(any) && NEVER = NEVER
(any) || NEVER = any
OK && NULL = NEVER
OK || NULL = UNSURE
( A && B || C )
ptr is a pointer in pointerSet
(ptr) OK
(|ptr) NULL
(0 < ptr) (ptr > 0) OK
(0 != ptr) (ptr != 0) OK
(0 == ptr ) NULL
other UNSURE
If(A && B|| C )