使用CodeQL查找未处理错误 - Trail of Bits博客
问题背景
开发人员在代码库中发现了一个未处理的错误代码漏洞,并怀疑存在更多类似问题。手动逐个检查代码效率低下,如同"打地鼠"游戏。未处理的错误可能被利用并导致严重问题,例如Kubernetes的CVE-2018-1002105漏洞就源于错误处理不当。
CodeQL解决方案
我们使用CodeQL进行变体分析,通过现有漏洞模式寻找类似问题。具体步骤包括:
1. 构建CodeQL数据库
1
|
codeql database create -l cpp -c 'MSBuild.exe <solution>.sln' <solution>.codeql
|
2. 设置自定义查询包
创建qlpack.yml
文件:
1
2
3
|
name: <some snazzy QL pack name>
version: 0.0.1
libraryPathDependencies: [codeql-cpp]
|
3. 定义自定义错误类型
1
2
3
4
5
|
class CustomError extends FunctionCall {
CustomError() {
this.getUnderlyingType().getName() = "CustomErrorType"
}
}
|
4. 检测未处理错误
通过污点跟踪技术检查返回值是否影响控制流:
1
2
3
4
5
6
7
8
9
|
predicate isChecked() {
exists (IfStmt is |
TaintTracking::localTaint(
DataFlow::exprNode(this),
DataFlow::exprNode(is.getCondition().getAChild*())
)
) or
// 类似处理while和switch语句
}
|
5. 优化查询精度
排除以下假阳性情况:
- 返回值被直接返回
- 返回值作为函数参数传递
- 返回值赋值给类成员变量
6. 全局污点跟踪
使用全局分析获得更准确结果:
1
2
3
4
5
6
7
8
|
class GuardConfiguration extends TaintTracking::Configuration {
GuardConfiguration() { this = "GuardConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr().(FunctionCall).getUnderlyingType().getName() =
"CustomErrorType"
}
}
|
实践效果
最终查询识别出100多个需要手动审查的代码位置,其中大部分确实是需要修复的真实问题。全局分析运行时间与本地分析相当(约20秒编译,10秒运行)。
技术价值
CodeQL允许我们将漏洞知识编码为可重复使用的查询,确保相同的错误不会再次出现。这种静态分析方法显著提高了代码审计的效率和准确性。
本文展示了如何系统化地使用CodeQL进行代码安全审计,为开发团队提供了实用的技术方案。