ClangCheckerExpand
-
在已有的检查器包——
alpha.core
中添加自定义的检查器,那么在Checkers.td
文件中添加如下内容:def MainCallChecker : Checker<"MainCall">, HelpText<"Check for calls to main">, Documentation<NotDocumented>;
修改后的内容如下:
let ParentPackage = CoreAlpha in { // 省略 ... def MainCallChecker : Checker<"MainCall">, HelpText<"Check for calls to main">, Documentation<NotDocumented>; } // end "alpha.core"
注:
Checkers.td
文件位于clang/include/clang/StaticAnalyzer/Checkers/
目录中。clang -cc1 -analyzer-checker-help
和clang -cc1 -analyzer-checker-help-alpha
等命令所显示的检查器列表来源于Checkers.td
文件。def MainCallChecker
,表示检查器的注册名称为MainCallChecker
。"MainCall"
,表示检查器的名称为MainCall
。也就是说,可以通过-analyzer-checker=alpha.core.MainCall
标志来启用该检查器。HelpText
选项,用于指定该检查器对应的描述。从而,在执行类似于-help
命令时显示。Documentation
选项,用于指定检查器文档的URI
地址。
需要注意的是, 修改的
Checkers.td
文件在重新编译安装 Clang 后才生效。 -
添加检查器的实现代码
在clang/lib/StaticAnalyzer/Checkers/
目录中新建MainCallChecker.cpp
文件,内容如下:
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class MainCallChecker : public Checker<check::PreCall> {
public:
void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const;
private:
mutable std::unique_ptr<BugType> BT;
};
} // anonymous namespace
void MainCallChecker::checkPreCall(const CallEvent &Call,
CheckerContext &Ctx) const {
if (const IdentifierInfo *II = Call.getCalleeIdentifier()) {
if (II->isStr("main")) {
if (!BT) {
BT.reset(new BugType(this, "Call to main", "Example checker"));
}
ExplodedNode *Node = Ctx.generateErrorNode();
auto Report
= std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), Node);
Ctx.emitReport(std::move(Report));
}
}
}
void ento::registerMainCallChecker(CheckerManager &Mgr) {
Mgr.registerChecker<MainCallChecker>();
}
bool ento::shouldRegisterMainCallChecker(const CheckerManager &mgr) {
return true;
}
注:
- 新建的源文件名称不必是
MainCallChecker.cpp
,也可以是其他名称。 - 检查器的类名不必与在
Checkers.td
文件中定义的检查器的注册名称保持一致。 void ento::registerXXX(CheckerManager &Mgr)
中的XXX
必须与在Checkers.td
文件中定义的检查器的注册名称保持一致。Mgr.registerChecker();
中的XXX
必须与检查器的类名保持一致。ento::shouldRegisterXXX(const CheckerManager &mgr)
中的XXX
必须与在Checkers.td
文件中定义的检查器的注册名称保持一致。
-
修改 CMakeLists.txt
在
clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
文件中添加如下内容:MainCallChecker.cpp
注:
MainCallChecker.cpp
即上文中新建的用于存放检查器实现代码的源文件。修改后的内容如下:
add_clang_library( # 省略 … MainCallChecker.cpp # 省略 …)
-
编译安装 Clang
省略
-
查看自定义的检查器
clang -cc1 -analyzer-checker-help-alpha | grep MainCall alpha.core.MainCall (Enable only for development!) Check for calls to main
从上面的输出可以看出:
alpha.core.MainCall
,即我们自定义的检查器的完整名称。Check for calls to main
,即我们在Checkers.td
文件中为自定义的检查器所添加的描述。
-
编写测试用例
Example_Test.c
typedef int (*main_t)(int, char **); int main(int argc, char **argv) { main_t foo = main; int exit_code = foo(argc, argv); // actually calls main()! return exit_code; }
上述测试程序存在这样的错误:通过函数指针变量
foo
调用main
函数。 -
运行自定义的检查器
clang --analyze -Xanalyzer -analyzer-checker=alpha.core.MainCall Example_Test.c
output
Example_Test.c:4:19: warning: Call to main [alpha.core.MainCall] int exit_code = foo(argc, argv); // actually calls main()! ^~~~~~~~~~~~~~~ 1 warning generated.