案例研究:在Linux内核中搜索漏洞
字数 1478 2025-08-25 22:59:09
Linux内核漏洞分析与检测工具使用教程
1. 漏洞案例背景
本教程基于一个实际的Linux内核漏洞案例,展示了如何分析内核漏洞以及使用Semmle QL和Coccinelle工具进行漏洞检测。
1.1 漏洞发现过程
漏洞最初通过自定义的syzkaller项目被发现,syzkaller是一个用于内核模糊测试的优秀工具,能够稳定地复现崩溃。
1.2 漏洞代码分析
漏洞位于drivers/block/floppy.c文件的compat_getdrvstat()函数中:
static int compat_getdrvstat(int drive, bool poll, struct compat_floppy_drive_struct __user *arg) {
struct compat_floppy_drive_struct v;
memset(&v, 0, sizeof(struct compat_floppy_drive_struct));
...
if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_struct)))
return -EFAULT;
...
}
漏洞本质:copy_from_user()函数参数顺序错误:
- 第一个参数应为用户空间目标地址
- 第二个参数应为内核空间源地址
- 但代码中正好相反
1.3 漏洞影响
该漏洞会导致:
copy_from_user()尝试向用户空间地址写入内核数据- 当
access_ok()检查失败时,函数会尝试清除目标地址 - 由于目标是用户空间地址,导致内核崩溃
1.4 崩溃日志分析
崩溃日志显示了一个典型的页面错误:
[ 40.937098] BUG: unable to handle page fault for address: 0000000041414242
[ 40.938714] #PF: supervisor write access in kernel mode
[ 40.939951] #PF: error_code(0x0002) - not-present page
...
RIP: 0010:__memset+0x24/0x30
...
Call Trace:
_copy_from_user+0x51/0x60
compat_getdrvstat+0x124/0x170
fd_compat_ioctl+0x69c/0x6d0
2. 使用Semmle QL进行漏洞分析
Semmle QL是一种强大的代码查询语言,可用于静态分析。
2.1 基本查询
查找所有copy_from_user()调用:
import cpp
from FunctionCall call
where call.getTarget().getName() = "copy_from_user"
select call, "I see a copy_from_user here!"
2.2 LGTM平台限制
在LGTM上分析Linux内核时需要注意:
- LGTM使用默认配置构建内核,可能不包含所有子系统
- 分析时间限制为4小时,大型项目可能无法完整分析
- 可通过
lgtm.yml文件自定义构建过程
2.3 局限性
Semmle QL在大型项目中的限制:
- 构建配置限制了分析范围
- 资源限制导致无法分析完整内核
- 目前团队正在寻求解决方案
3. 使用Coccinelle进行漏洞分析
Coccinelle是Linux内核社区广泛使用的模式匹配工具。
3.1 编写SmPL规则
以下规则用于检测copy_from_user()参数顺序错误:
virtual report
@cfu exists@
identifier f;
type t;
identifier v;
position decl_p;
position copy_p;
@@
f(...,
t v@decl_p,
...)
{
... when any
copy_from_user@copy_p(v,
...)
when any
}
@script:python@
f << cfu.f;
t << cfu.t;
v << cfu.v;
decl_p << cfu.decl_p;
copy_p << cfu.copy_p;
@@
if '__user' in t:
msg0 = "function \"" + f + "\" has arg \"" + v + "\" of type \"" + t + "\""
coccilib.report.print_report(decl_p[0], msg0)
msg1 = "copy_from_user uses \"" + v + "\" as the destination. What a shame!\n"
coccilib.report.print_report(copy_p[0], msg1)
3.2 规则解释
- 查找所有函数
f(),其参数v被用作copy_from_user()的第一个参数 - 使用Python脚本检查参数
v的类型是否包含__user注解 - 如果匹配,则报告潜在的错误使用
3.3 检测结果
Coccinelle成功检测到两个漏洞:
./drivers/block/floppy.c:3756:49-52: function "compat_getdrvprm" has arg "arg" of type "struct compat_floppy_drive_params __user *"
./drivers/block/floppy.c:3783:5-19: copy_from_user uses "arg" as the destination. What a shame!
./drivers/block/floppy.c:3789:49-52: function "compat_getdrvstat" has arg "arg" of type "struct compat_floppy_drive_struct __user *"
./drivers/block/floppy.c:3819:5-19: copy_from_user uses "arg" as the destination. What a shame!
4. 其他检测方法
4.1 sparse工具
Jann Horn最初使用sparse工具发现了这些漏洞。sparse是一个静态分析工具,特别适合检测内核中的类型错误。
4.2 补丁状态
这些漏洞虽然已被发现,但由于补丁丢失,直到本文作者报告后才被修复,成为"公开的0 day"漏洞。
5. 总结与最佳实践
5.1 工具选择建议
-
Semmle QL:
- 适合精确查询特定模式
- 受限于项目构建配置和资源
- 需要自定义配置才能分析完整内核
-
Coccinelle:
- 专为Linux内核设计
- 强大的模式匹配能力
- 社区广泛使用,有大量现成规则
-
sparse:
- 特别适合类型检查
- 轻量级,易于集成到构建系统
5.2 漏洞检测流程
- 使用模糊测试工具(如syzkaller)发现崩溃
- 分析崩溃日志定位问题代码
- 使用静态分析工具查找类似问题
- 验证并报告发现的问题
5.3 预防措施
- 对
copy_from_user()等敏感函数进行代码审查 - 在CI中集成静态分析工具
- 关注内核邮件列表中的安全补丁
- 定期更新内核版本
通过本教程,您应该掌握了如何分析Linux内核漏洞以及使用Semmle QL和Coccinelle进行静态分析的基本方法。这些技能对于内核开发和安全研究都非常有价值。