Clang 10.0 手写静态分析器Checker

1.简介

目前网上找到的一些构建Checker的方法都比较古老,随着版本更新一些库文件和依赖文件的变动老方法不太适用,所以整理了一下在10.0版本下构建Checker的方法,10.0之前没没有使用过,后面的版本应该大差不差了。

2.编写Checker

编写自定义的Checker需要在以下目录进行:

llvm/tools/clang/lib/StaticAnalyzer/Checkers

此目录存放的是当前静态分析其所有的Checker文件。
新建MyPathChecker.cpp文件并编写如下代码(实现了对扩展图的DFS,代码是师弟编写的)

#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/Analysis/Analyses/Dominators.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/CallGraph.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/Support/Process.h"
#include <vector>

using namespace clang;
using namespace ento;

namespace {
class EGraphHelper {
private:
    std::vector<ExplodedNode*> path;
    std::vector<std::vector<ExplodedNode*>> paths;

public:
    void dfsTrave(ExplodedGraph &G) {
        llvm::outs() << "start dfs\n";
        path.clear();
        paths.clear();
        // 遍历所有根节点
        for (auto it = G.roots_begin(); it < G.roots_end(); it++) {
            // 指向ExplodedNode*指针
            path.push_back(*it);
            dfs(*it);
            path.pop_back();
        }
        pathTrim(paths);
    }

    void dfs(ExplodedNode* node) {
        // ProgramStateRef state = node->getState();
        // int id = state->getID();
        // llvm::outs() << id << "\n";
        // llvm::outs() << "ptr_addr: " << node << "\n";
        // 搜索到路径终止处
        if (node->getFirstSucc() == nullptr) {
            // 存储当前路径信息
            paths.push_back(path);
            return;
        }

        for (auto it = node->succ_begin(); it < node->succ_end(); it++) {
            path.push_back(*it);
            dfs(*it);
            path.pop_back();
        }
    }

    void pathTrim(std::vector<std::vector<ExplodedNode*>> &paths) {
        int pathNum = 0;
        for (auto path : paths) {
            llvm::outs() << "path-" << ++pathNum << "\n";
            for (auto node : path) {
                // ProgramStateRef state = node->getState();
                // int id = state->getID();
                // llvm::outs() << id << " ";
                ProgramPoint point = node->getLocation();
                llvm::Optional<BlockEntrance> be = point.getAs<BlockEntrance>();
                if (be != None) {
                    // 收集basic block路径信息
                    auto blockId = be->getBlock()->getBlockID();
                    llvm::outs() << blockId << " ";
                }
            }
            llvm::outs() << "\n";
        }
    }
}; // end class EGraphHelper

class MyPathChecker : public Checker< check::EndAnalysis > {
public:
    MyPathChecker() {}
    void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng) const;
};// end class MyPathChecker
}// end namespace


// 调用的回调函数
void MyPathChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng) const {
    llvm::outs() << "get path check\n";
    EGraphHelper helper;
    helper.dfsTrave(G);
}

void ento::registerMyPathChecker(CheckerManager &mgr) {
    mgr.registerChecker<MyPathChecker>();
}

bool ento::shouldRegisterMyPathChecker(const LangOptions &LO) {
    return true;
}

其中 EGraphHelper 是一个自定义工具类,主要是MyPathChecker 这个类,继承Checker类并在registerMyPathChecker中注册,shouldRegisterMyPathChecker这个个人理解是判断是否能注册此Checker,此函数存在一个问题:

bool ento::shouldRegisterMyPathChecker(const LangOptions &LO) {
    return true;
}

函数参数在15.0版本编译的时候应该指定如下格式:

bool ento::shouldRegisterMyPathChecker(const CheckerManager &mgr) {
    return true;
}

我在10.0版本编译的时候用第二种格式会报以下错误,具体原因不太清楚,可能是版本的问题,对照了源码中的方法使用第一种格式可以编译成功:
在这里插入图片描述

3.配置CMakeLists

如下路径配置CMakeLists,添加刚才编写的cpp文件即可

llvm/tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

4.配置Checkers.td

这个是非常重要的一步,这个配置是将自定义的Checker整合到静态分析器中。
如下路径配置Checkers.td,注意:网上很多资料此路径是在lib下,应该是版本比较老的代码结构了,目前此文件在include路径下。

llvm/tools/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

此文件结构如下,ParentPackage 标识当前Checker属于哪一个包(debug、core等),我们把刚才编写的checker放在了Debug中,如下。

//===----------------------------------------------------------------------===//
// Debugging checkers (for analyzer development).
//===----------------------------------------------------------------------===//

let ParentPackage = Debug in {
	def ***
	def ***
	***
	// 在源文件的Debug中添加如下代码即可
	def MyPathChecker : Checker<"MyPathChecker">,
  		HelpText<"Check for get path">,
  		Documentation<NotDocumented>;
}

5.编译构建

上述所有步骤准备完成之后可以构建程序了,进入最外层的build目录make然后make install就完事啦!这里就不放图片啦。

6.测试

命令如下,debug.MyPathChecker就是我们刚才编写的checker,两个-I指定了动态链接库,根据本机做调整即可,最后test.c是测试的源代码:

clang -cc1 -I /usr/include -I /home/zzh/llvm/lib/clang/10.0.0/include -analyze -analyzer-checker=debug.MyPathChecker test.c

生成了扩展图中的路径信息,如下:
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值