时间轴

2026-05-21

init



Smatch 是一个专为 Linux 内核设计的静态分析工具,能深入追踪代码的执行路径,发现一些隐藏得很深的bug,比如空指针解引用、锁使用错误、资源泄漏等。参考文档:

编译 smatch

1
2
3
4
5
6
7
# 安装依赖
sudo apt-get install sqlite3 libsqlite3-dev libdbd-sqlite3-perl
# 编译 smatch
cd ~/repository/linux/
wget https://github.com/error27/smatch/archive/refs/tags/1.74.tar.gz -O smatch-1.74.tar.gz
cd smatch-1.74
make -j$(nproc)

kernel

Smatch 本质上会走 kernel build system, 所以必须能正确找到编译器和头文件, 因此如果需要交叉编译必须先设置环境变量。

1
2
export ARCH=arm64
export CROSS_COMPILE=aarch64-none-linux-gnu-

分析并构建数据库

1
2
3
4
5
6
7
8
cd ~/repository/linux/linux-5.10.256

# 编译Linux
make defconfig
make -j$(nproc) Image

# 静态分析并构建数据库
~/repository/linux/smatch-1.74/smatch_scripts/build_kernel_data.sh

build_kernel_data.sh 会附加 --call-tree, --info, --spammy, --data=$DATA_DIR 参数调用 smatch_scripts/test_kernel.sh, 随后调用 smatch_data/db/create_db.sh 构建 smatch 数据库

smatch 并不强制需要构建数据库,但是构建数据库会更加强大, 一旦总数据库建成,下次再编译时,Smatch 就能通过查表知道:“printk 这个函数在整个内核里被调用了多少次”、“某个结构体指针在其他 C 文件里有没有被释放过”,从而实现史诗级的跨文件漏洞侦测。

运行 smatch 对 kernel 进行静态分析:运行 smatch 检查整个kernel

1
~/repository/linux/smatch-1.74/smatch_scripts/test_kernel.sh --data="$HOME/repository/linux/smatch-1.74/smatch_data"

脚本参数

  • --endian:开启大端/小端字节序检查。
  • --target "bzImage":指定编译目标(默认是 bzImage modules)。
  • --log smatch_compile.warns: 指定编译日志输出文件
  • --wlog smatch_warns.txt:自定义 Smatch 警告的输出文件名。

编译完成后,会在当前目录下生成两个文件:

  • smatch_compile.warns:完整的内核编译日志(含报错和警告)。
  • smatch_warns.txt:纯粹的 Smatch 静态检查警告信息。

也可以只检查某个文件或文件夹下的代码

1
2
~/repository/linux/smatch-1.74/smatch_scripts/kchecker drivers/rpmsg/rpmsg_core.c
~/repository/linux/smatch-1.74/smatch_scripts/kchecker drivers/rpmsg/

smatch作者建议经常更新smatch数据库, 因为每次更新跨函数的检查会更准确

1
~/repository/linux/smatch-1.74/smatch_data/db/create_db.sh -p=kernel smatch_warns.txt

smatch 在运行时会读取smatch_data中设置的一些规则, 在~/repository/linux/smatch-1.74/smatch.cstatic char *get_data_dir(char *arg0)有相关逻辑:可以通过传递--data=/path/to/smatch_data指定

out-of-tree module

对于非kernel项目, 用以下方法构建数据库并分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export ARCH=arm64
export CROSS_COMPILE=aarch64-none-linux-gnu-

make -C ~/repository/linux/linux-5.10.256 \
M=$PWD \
-j$(nproc) \
CHECK="$HOME/repository/linux/smatch-1.74/smatch -p=kernel --file-output --succeed --call-tree --info --spammy --data=$HOME/repository/linux/smatch-1.74/smatch_data" \
C=2

~/repository/linux/smatch-1.74/smatch_data/db/create_db.sh -p=kernel hello_world.c.smatch

make -C ~/repository/linux/linux-5.10.256 \
M=$PWD \
-j$(nproc) \
CHECK="$HOME/repository/linux/smatch-1.74/smatch -p=kernel --succeed --data=$HOME/repository/linux/smatch-1.74/smatch_data" \
C=2
  • --file-output 每个文件输出一个结果
  • --succeed 表示即使出错也继续分析
  • --call-tree 表示开启跨函数调用分析
  • --info 表示更详细的分析日志
  • --spammy 表示更激进的检查

当用了 --file-output 参数后,smatch 默认不会把警告直接打印在终端的 stdout/stderr 上,而是会在你的代码同级目录下生成一堆隐藏的文本文件:

  • .hello_world.c.smatch (常规警告)
  • .hello_world.c.smatch.caller_info (如果你加了 --info 等参数)静态检查分析

参考文献