修改CppCheck,做自己的扫描器 - 01
CppCheck是一个静态语法扫描器,官方介绍:
Static analysis of C/C++ code. Checks for: memory leaks, mismatching allocation-deallocation, buffer overrun, and many more. The goal is 0% false positives. See http://cppcheck.sourceforge.net for more information.
CppCheck是一个开源工程,要对其进行修改,最好还是理清楚它的工作流程。首先,让我们对一个常见的问题进行检查,代码如下:
int a[10];
a[10] = 0;
对这个越界代码进行扫描,可以发现如下调用栈处报警:
> cppcheck-core.dll!CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token * tok, const CheckBufferOverrun::ArrayInfo & arrayInfo, const std::vector<ValueFlow::Value,std::allocator<ValueFlow::Value> > & index) 行 142 C++
cppcheck-core.dll!CheckBufferOverrun::valueFlowCheckArrayIndex(const Token * const tok, const CheckBufferOverrun::ArrayInfo & arrayInfo) 行 878 C++
cppcheck-core.dll!CheckBufferOverrun::bufferOverrun() 行 1593 C++
cppcheck-core.dll!CheckBufferOverrun::runChecks(const Tokenizer * tokenizer, const Settings * settings, ErrorLogger * errorLogger) 行 91 C++
cppcheck-core.dll!CppCheck::checkNormalTokens(const Tokenizer & tokenizer) 行 563 C++
cppcheck-core.dll!CppCheck::checkFile(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & filename, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & cfgname, std::basic_istream<char,std::char_traits<char> > & fileStream) 行 416 C++
cppcheck-core.dll!CppCheck::check(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & path) 行 83 C++
cppcheck.exe!CppCheckExecutor::check_internal(CppCheck & cppcheck, int __formal, const char * const * argv) 行 871 C++
cppcheck.exe!CppCheckExecutor::check(int argc, const char * const * argv) 行 198 C++
cppcheck.exe!main(int argc, char * * argv) 行 95 C++
简单分析一下,基本是这几行起了作用:
cppcheck-core.dll!CheckBufferOverrun::runChecks(const Tokenizer * tokenizer, const Settings * settings, ErrorLogger * errorLogger) 行 91 C++
cppcheck-core.dll!CppCheck::checkNormalTokens(const Tokenizer & tokenizer) 行 563 C++
> cppcheck-core.dll!CppCheck::checkFile(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & filename, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & cfgname, std::basic_istream<char,std::char_traits<char> > & fileStream) 行 416 C++
注意第一行:
void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
{
// call all "runChecks" in all registered Check classes
for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) {
if (mSettings.terminated())
return;
if (tokenizer.isMaxTime())
return;
Timer timerRunChecks((*it)->name() + "::runChecks", mSettings.showtime, &S_timerResults);
(*it)->runChecks(&tokenizer, &mSettings, this);
}
也就是说,it从Check::instances()中遍历所有项目。
Check::instances中有:
名称 值 类型
▶ [0] 0x01557438 {cppcheck-core.dll!Check64BitPortability instance} {...} Check * {Check64BitPortability}
▶ [1] 0x01557494 {cppcheck-core.dll!CheckAssert instance} {...} Check * {CheckAssert}
▶ [2] 0x015574ec {cppcheck-core.dll!CheckAutoVariables instance} {...} Check * {CheckAutoVariables}
▶ [3] 0x01557550 {cppcheck-core.dll!CheckBool instance} {...} Check * {CheckBool}
▶ [4] 0x015575b8 {cppcheck-core.dll!CheckBoost instance} {...} Check * {CheckBoost}
▶ [5] 0x01557614 {cppcheck-core.dll!CheckBufferOverrun instance} {...} Check * {CheckBufferOverrun}
▶ [6] 0x01557774 {cppcheck-core.dll!CheckFunctions instance} {...} Check * {CheckFunctions}
▶ [7] 0x0155768c {cppcheck-core.dll!CheckClass instance} {mSymbolDatabase=0x00000000 <NULL> } Check * {CheckClass}
▶ [8] 0x0155771c {cppcheck-core.dll!CheckCondition instance} {...} Check * {CheckCondition}
▶ [9] 0x01557864 {cppcheck-core.dll!CheckExceptionSafety instance} {...} Check * {CheckExceptionSafety}
▶ [10] 0x015578bc {cppcheck-core.dll!CheckIO instance} {...} Check * {CheckIO}
▶ [11] 0x01557974 {cppcheck-core.dll!CheckLeakAutoVar instance} {...} Check * {CheckLeakAutoVar}
▶ [12] 0x01557a7c {cppcheck-core.dll!CheckMemoryLeakNoVar instance4} {...} Check * {CheckMemoryLeakNoVar}
▶ [13] 0x01557a0c {cppcheck-core.dll!CheckMemoryLeakInClass instance2} {...} Check * {CheckMemoryLeakInClass}
▶ [14] 0x015579d4 {cppcheck-core.dll!CheckMemoryLeakInFunction instance1} {...} Check * {CheckMemoryLeakInFunction}
▶ [15] 0x01557a44 {cppcheck-core.dll!CheckMemoryLeakStructMember instance3} {...} Check * {CheckMemoryLeakStructMember}
▶ [16] 0x01557b34 {cppcheck-core.dll!CheckNullPointer instance} {...} Check * {CheckNullPointer}
▶ [17] 0x01557bb0 {cppcheck-core.dll!CheckOther instance} {...} Check * {CheckOther}
▶ [18] 0x01557d20 {cppcheck-core.dll!CheckStl instance} {...} Check * {CheckStl}
▶ [19] 0x01557cbc {cppcheck-core.dll!CheckSizeof instance} {...} Check * {CheckSizeof}
▶ [20] 0x015577ec {cppcheck-core.dll!CheckString instance} {...} Check * {CheckString}
▶ [21] 0x01557e98 {cppcheck-core.dll!CheckType instance} {...} Check * {CheckType}
▶ [22] 0x01557f00 {cppcheck-core.dll!CheckUninitVar instance} {...} Check * {CheckUninitVar}
▶ [23] 0x01557f70 {cppcheck-core.dll!CheckUnusedFunctions CheckUnusedFunctions::instance} {mFunctions={ size=0 } ...} Check * {CheckUnusedFunctions}
▶ [24] 0x01557ff8 {cppcheck-core.dll!CheckUnusedVar instance} {mIsRecordTypeWithoutSideEffectsMap={ size=0 } ...} Check * {CheckUnusedVar}
▶ [25] 0x01557c64 {cppcheck-core.dll!CheckPostfixOperator instance} {...} Check * {CheckPostfixOperator}
▶ [26] 0x01558070 {cppcheck-core.dll!CheckVaarg instance} {...} Check * {CheckVaarg}
鉴于代码表明_instalces这是一个static变量,所以必然有代码调用了Check::instances().XXXX向它推送代码。
std::list<Check *> &Check::instances()
{
#ifdef __SVR4
// Under Solaris, destructors are called in wrong order which causes a segmentation fault.
// This fix ensures pointer remains valid and reachable until program terminates.
static std::list<Check *> *_instances= new std::list<Check *>;
return *_instances;
#else
static std::list<Check *> _instances;
return _instances;
#endif
}
查找所有的代码以后,只有构造函数有相关代码:
Check::Check(const std::string &aname)
: mTokenizer(nullptr), mSettings(nullptr), mErrorLogger(nullptr), mName(aname)
{
for (std::list<Check*>::iterator i = instances().begin(); i != instances().end(); ++i) {
if ((*i)->name() > aname) {
instances().insert(i, this);
return;
}
}
instances().push_back(this);
}
下断点,重新执行。
cppcheck-core.dll!Check::Check(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & aname) 行 30 C++
cppcheck-core.dll!Check64BitPortability::Check64BitPortability() 行 46 C++
> cppcheck-core.dll!`anonymous namespace'::`dynamic initializer for 'instance''() 行 41 C++
可以看到这样的调用栈,再往下就是程序的初始化代码了,一层层看。
1:最上层可以看到一个类似全局变量的代码
// Register this check class (by creating a static instance of it)
namespace {
Check64BitPortability instance;
}
2:可以看到检测的类都是公开继承Check的
class CPPCHECKLIB Check64BitPortability : public Check {
public:
/** This constructor is used when registering the Check64BitPortability */
Check64BitPortability() : Check(myName()) {
}
3:随后代码走到Check中,该类被加入。
再保留断点继续执行,可以发现所有的Check都是这样加入的,因为这些Check类都是全局变量,所以程序一启动就会自动添加进去。等他们执行完了才会进入main。