使用CodeQL检测未处理的错误代码

本文详细介绍了如何利用CodeQL静态分析工具检测C++代码中未处理的错误返回值,通过构建自定义查询包、定义错误类型类、实现本地和全局污点跟踪等技术手段,高效识别潜在安全漏洞。

使用CodeQL检测未处理的错误代码

当开发者发现代码库中存在未处理的错误代码时,手动排查往往效率低下。以Kubernetes的CVE-2018-1002105漏洞为例,错误处理不当可能导致攻击者通过API建立后门连接。Trail of Bits团队采用CodeQL进行变体分析,批量检测此类问题。

构建CodeQL数据库

首先需要使用CodeQL CLI创建分析数据库:

1
codeql database create -l cpp -c 'MSBuild.exe <solution>.sln' <solution>.codeql

设置自定义查询包

创建qlpack.yml文件定义查询包依赖:

1
2
3
name: <some snazzy QL pack name>
version: 0.0.1
libraryPathDependencies: [codeql-cpp]

检测未处理错误

定义CustomError类型捕获返回CustomErrorType的函数调用:

1
2
3
4
5
class CustomError extends FunctionCall {
    CustomError() {
        this.getUnderlyingType().getName() = "CustomErrorType"
    }
}

基础查询语句定位所有错误返回点:

1
2
3
4
5
6
from 
    CustomError ce
select
    ce.getLocation(), 
    "Unhandled error code in ", ce.getEnclosingFunction().getName(), 
    "Error code returned by ", ce.getTarget().getName()

定义错误处理逻辑

通过本地污点跟踪判断错误是否被处理:

1
2
3
4
5
6
7
8
predicate isChecked() {
    exists (IfStmt is |
        TaintTracking::localTaint(
            DataFlow::exprNode(this),
            DataFlow::exprNode(is.getCondition().getAChild*())
    ) or
    // 类似处理while/switch语句
}

优化查询结果

排除三种误报情况:

  1. 错误被直接返回
  2. 错误作为函数参数传递
  3. 错误赋值给成员变量

对应CodeQL实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
predicate isReturnValue() {
    exists (ReturnStmt rs |
        TaintTracking::localTaint(/*...*/)
    )
}

predicate isPassedToFunction() {
    exists (FunctionCall fc |
        TaintTracking::localTaint(/*...*/)
    )
}

predicate isAssignedToMemberVar() {
    exists (MemberVariable mv, MemberFunction mf |
        mf = this.getEnclosingFunction() and
        mf.canAccessMember(mv, mf.getDeclaringType()) and
        TaintTracking::localTaint(/*...*/)
    )
}

全局污点跟踪分析

定义全局污点配置类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class GuardConfiguration extends TaintTracking::Configuration {
    override predicate isSource(DataFlow::Node source) {
        source.asExpr().(FunctionCall).getUnderlyingType().getName() = "CustomErrorType"
    }
  
    override predicate isSink(DataFlow::Node sink) {
        exists (IfStmt is | sink.asExpr() = is.getCondition().getAChild*())
        // 其他分支语句处理
    }
}

最终该技术在真实项目中发现了150+个需要修复的问题点。CodeQL使我们能够将漏洞模式编码为可重复使用的查询,确保同类错误不会再次出现。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计