You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CodeQL C#污点分析无法识别类非const实例字段的污点流路径

CodeQL C# 污点追踪无法识别实例readonly字段流路径问题

复现环境与代码

测试类代码

设定Message1为污点源(source),Console.WriteLine(Message1)为污点汇聚点(sink),测试类定义如下:

class Test
{
    private const string Message1 = "Test Message 1";
    private readonly string Message2 = "Test Message 2";

    public void Run()
    {
        Console.WriteLine(Message1);
        Console.WriteLine(Message2);
    }
}

污点追踪查询代码

import csharp
import DataFlow::PathGraph

class Source extends DataFlow::Node {
  Source() { this.asExpr() instanceof StringLiteral }
}

class WriteLineMethod extends Method {
  WriteLineMethod() { this.hasQualifiedName("System.Console.WriteLine") }
}

class Sink extends DataFlow::Node {
  Sink() {
    exists(MethodCall m |
      m.getTarget() instanceof WriteLineMethod and
      this.asExpr() = m.getArgument(0)
    )
  }
}

class SimpleConfiguration extends TaintTracking::Configuration {
  SimpleConfiguration() { this = "Simple configuration" }

  override predicate isSource(DataFlow::Node source) { source instanceof Source }

  override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
  
}

from DataFlow::PathNode source, DataFlow::PathNode sink, SimpleConfiguration cfg
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ is used in WriteLine method.", source.getNode(),
  "String"

异常现象

执行上述查询时结果不符合预期:

  • 常量字段private const string Message1 = "Test Message 1";到对应Console.WriteLine(Message1);汇聚点的流路径可被正常检出
  • 实例只读字段private readonly string Message2 = "Test Message 2";对应的流路径始终无法识别
  • 静态类中的静态字段流路径可正常检出

原因说明

该现象不是查询配置疏漏,是CodeQL C#默认污点跟踪规则的设计逻辑导致:

  • const字段为编译期常量,编译器会直接将常量值内联到所有调用位置,Console.WriteLine(Message1)本质等价于直接传入字符串字面量,源和汇聚点直接连通,因此可被正常识别。
  • 静态字段为类型级全局唯一成员,不存在多实例值歧义,默认污点流规则已覆盖静态字段读写传播逻辑,因此识别正常。
  • 实例字段归属于具体对象实例,默认污点跟踪规则为控制误报率、降低分析性能开销,默认不会自动追踪跨实例字段的污点传播:如果无差别追踪所有实例字段,会因不同实例同名字段值混淆产生大量误报,同时大幅提升分析计算量。即使是readonly实例字段(初始化后不可修改),默认规则也未对该特殊场景做适配,因此流路径会被截断。

修复方案

在自定义的SimpleConfiguration类中重写isAdditionalTaintStep谓词,手动补充实例字段从初始化/写入位置到读取位置的污点传播规则即可,示例补充代码如下:

override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
  // 补充实例字段从写入点到读取点的污点传播
  exists(FieldAccess fa |
    // n2对应实例字段的读取表达式
    n2.asExpr() = fa and
    not fa.getTarget().isStatic() and
    (
      // 场景1:字段在声明时直接初始化,n1对应字段初始化值
      exists(Field f |
        f = fa.getTarget() and
        n1.asExpr() = f.getInitializer()
      )
      // 场景2:字段在构造函数内赋值,可按需放开下方注释覆盖该场景
      // or exists(AssignExpr ae | ae.getTarget() = fa and n1.asExpr() = ae.getRight())
    )
  )
}

添加上述规则后,实例字段的初始化值到字段读取位置的流路径就会被正常识别。如果需要覆盖更复杂的实例字段传播场景,可以结合指向分析(Points-To Analysis)进一步优化规则精度。

参考说明

  • 测试结果:执行原始查询时仅能检出Message1对应的流路径,添加补充规则后可同时检出两个字段的流路径
  • 复现方式:可基于上述Test类自行构建C# CodeQL数据库验证问题与修复效果
  • 官方反馈:可在CodeQL官方仓库查询对应问题的官方回复

内容的提问来源于stack exchange,提问作者Manu Nair

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 00:31:02