CSA (Clang Static Analyzer) 是 clang 的一部分。建议使用自行编译的 clang ,源码在 llvm/llvm-project (github.com) 上获取。
编译 clang 前首先要生成 clang 的编译脚本。在生成 clang 的编译脚本时通常需要设置如下几个参数:
-S
源代码路径
-B
生成的编译脚本放置的目录
-G
编译工具的选择,对应生成不同工具需要的编译脚本,如 ninja ,默认是 make 和 cmake
-DLLVM_ENABLE_PROJECTS
说明要编译的子项目,如 clang 、 clang-tools-extra 等,默认是 all
-DCMAKE_BUILD_TYPE
说明编译的类型,如 Debug 、 Release 等,默认是 Debug
-CMAKE_INSTALL_PREFIX
类似于 configure 脚本的 –prefix ,用于配合 INSTALL 指令( make install )使用,指定安装路径
-DCMAKE_CXX_COMPILER
和 DCMAKE_C_COMPILER
指定使用的编译器,如 clang 或 gcc
使用 cmake 的例子1:
cd llvm-project
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ../llvm
cmake -DLLVM_ENABLE_PROJECTS=clang -G "Unix Makefiles" ../llvm
make
使用 cmake 的例子2:
git clone --depth 1 --branch llvmorg-12.0.1 https://github.com/llvm/llvm-project.git
cmake -S llvm-project/llvm -B llvm-project/build \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS=all \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang
cmake --build llvm-project/build -j8
cmake --install llvm-project/build --prefix /usr/local # or somewhere else
使用 ninja 的例子:
cd llvm-project
mkdir build
cd build
cmake -S ../llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi" -DCMAKE_INSTALL_PREFIX=/home/xxx/llvm_project/build_tmp
cd build
ninja && ninja install
最终我的版本:
cd llvm-project
mkdir installed
# 编译 Debug 版本的 clang 和 clang-tools-extra ,中间编译脚本存储在 build ,生成的可执行程序包安装在 installed
cmake -S llvm -B build -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=$PWD/installed
# 使用 build 目录中的内容进行编译
cmake --build build -j8
# 安装到前面 DCMAKE_INSTALL_PREFIX 指定的目录
# 试过 cmake --install build --prefix installed 不成功
make install
# 把生成的 clang 等可执行文件路径设置到环境变量中
export PATH=/xxx/llvm-project/installed/bin/:$PATH
参考:
Clang - Getting Started (llvm.org)
LLVM(1)-编译自己的LLVM和Clang - 掘金 (juejin.cn)
拥有 clang 的可执行文件后,我们可以先试一下输出代码的基础信息,如 AST 、 CFG 、 CallGraph 和 ExplodedGraph 。其中 AST 并非 CSA 提供的功能,后面三种图都可以通过指定 CSA 的 Checker 进行输出。 ExplodedGraph 是 CSA 中最重要的概念,包含了代码的执行路径和程序状态。这里需要注意使用 debug 开头的 Checker 需要 clang 是 Debug 版本,也就是如果想要输出 CFG 、 CallGraph 和 ExplodedGraph 信息,则需要编译 clang 时指定 -DCMAKE_BUILD_TYPE
为 Debug
。
打印 AST 信息:
clang -cc1 -ast-dump test.c
列出 CSA 中支持列出的信息:
# 以 llvm-project 12.0.1 为例,仅 -analyzer-checker-help 无法显示所有的 Checkers
# 若需要显示 debug 开头的 Checkers ,需要使用 -analyzer-checker-help-developer
clang -cc1 -analyzer-checker-help-developer
其中 debug 开头的 Checkers 有:
debug.AnalysisOrder Print callbacks that are called during analysis in order
debug.ConfigDumper Dump config table
debug.DebugContainerModeling Check the analyzer's understanding of C++ containers
debug.DebugIteratorModeling Check the analyzer's understanding of C++ iterators
debug.DumpCFG Display Control-Flow Graphs
debug.DumpCallGraph Display Call Graph
debug.DumpCalls Print calls as they are traversed by the engine
debug.DumpControlDependencies Print the post control dependency tree for a given CFG
debug.DumpDominators Print the dominance tree for a given CFG
debug.DumpLiveExprs Print results of live expression analysis
debug.DumpLiveVars Print results of live variable analysis
debug.DumpPostDominators Print the post dominance tree for a given CFG
debug.DumpTraversal Print branch conditions as they are traversed by the engine
debug.ExprInspection Check the analyzer's understanding of expressions
debug.ReportStmts Emits a warning for every statement.
debug.Stats Emit warnings with analyzer statistics
debug.StdCLibraryFunctionsTester
Add test functions to the summary map, so testing of individual summary constituents becomes possible.
debug.StreamTester Add test functions to StreamChecker for test and debugging purposes.
debug.TaintTest Mark tainted symbols as such.
debug.ViewCFG View Control-Flow Graphs using GraphViz
debug.ViewCallGraph View Call Graph using GraphViz
debug.ViewExplodedGraph View Exploded Graphs using GraphViz
打印 CFG 、 CallGraph 、 ExplodedGraph 信息:
clang -cc1 -analyze -analyzer-checker=debug.DumpCFG test.c
# 针对 c++ ,建议使用 -Xclang 和 -c ,如果使用 -cc1 可能还需要手动 -I 指定库头文件地址
# -Xclang <arg> Pass <arg> to the clang compiler 表示传参给编译器部分
# -c Only run preprocess, compile, and assemble steps
clang++ -Xclang -analyze -Xclang -analyzer-checker=debug.DumpCFG -c test.cpp
# 或直接使用 --analyze 来执行 CSA ,就不需要使用 -c ,本身 CSA 就不会进行编译后的过程
clang++ --analyze -Xclang -analyzer-checker=debug.DumpCFG test.cpp
clang++ --analyze -Xclang -analyzer-checker=debug.ViewCFG test.cpp
clang++ --analyze -Xclang -analyzer-checker=debug.DumpCallGraph test.cpp
clang++ --analyze -Xclang -analyzer-checker=debug.ViewCallGraph test.cpp
clang++ --analyze -Xclang -analyzer-checker=debug.ViewExplodedGraph test.cpp
对于 View 开头的选项即查看 CFG 等,需要有合适的 viewer ,推荐安装 Graphviz :
sudo apt install graphviz
一般来说安装后使用 View 选项即可弹出窗口显示图, dot 文件会暂时保存到 tmp 目录中,若想保存为 SVG (推荐)或 JPG 或 PDF 格式可以:
dot -Tsvg /tmp/ExprEngine-123456.dot > ExprEngine-123456.svg
dot -Tjpg /tmp/ExprEngine-123456.dot > ExprEngine-123456.jpg
dot -Tpdf /tmp/ExprEngine-123456.dot > ExprEngine-123456.pdf
对于一个函数内容比较多时,建议关闭 dotty 弹出窗口,否则有可能一下弹出上千个窗口。
参考:
clang static analyzer源码分析(二)_电影旅行敲代码的博客-CSDN博客
CSA 中包含很多写好的 Checker ,或者我们可以编写自己的 Checker 。这些 Checker 不是被动地读取 ExplodedGraph 的信息,而是主动参与到 ExplodedGraph 的建立中,帮助我们发现安全问题。其实在查看 CFG 、 CallGraph 和 ExplodedGraph 信息时,我们就已经运行了 Checker ,这里更正式地讲一下如何运行 Checker 。
Clang 的 --analyze
编译选项表示运行静态分析器(the static analyzer,指的就是 CSA):
# --analyze Run the static analyzer
clang --analyze test.c
# 另一种方式
clang -Xclang -analyze -c test.c
使用 -analyzer-checker
选项指定具体的 Checker :
clang --analyze -Xclang -analyzer-checker=core test.c
列出可用的 Checker :
clang -cc1 -analyzer-checker-help
clang -cc1 -analyzer-checker-help-developer
默认分析的输出为 plist 格式,指定输出 html 格式的报告,这时需要用 -o
指定输出的路径:
# --analyzer-output <value> Static analyzer report output format (html|plist|plist-multi-file|plist-html|sarif|text).
# 输出的路径不用提前创建,会自动创建
clang --analyze --analyzer-output html -o <output-dir> test.c
clang --analyze --analyzer-output html -o <output-dir> -Xclang -analyzer-checker=core test.c
# 另一种方式(我更偏好)
clang --analyze -Xclang -analyzer-output=html -o <output-dir> test.c
clang --analyze -Xclang -analyzer-output=html -o <output-dir> -Xclang -analyzer-checker=core test.c
(不推荐使用)
如果使用 scan-build
和 scan-view
来集成:
scan-build [scan-build options] <command> [command options]
即在编译命令 <command>
前加上 scan-build
即可。比如 scan-build make
或 scan-build -V clang -c file.c
。其中 -V
表示输出报告。
关于 scan-build
和 scan-view
:
scan-build:
scan-build
is the high-level command line utility for running the analyzer
scan-view:scan-view
a companion command line utility toscan-build
,scan-view
is used to view analysis results generated byscan-build
. There is an option that one can pass to scan-build to cause scan-view to run as soon as it the analysis of a build completes
参考:
scan-build: running the analyzer from the command line (llvm.org)