CodeQL 文档

使用部分流调试数据流查询

注意

此处描述的新的模块化数据流 API 自 CodeQL 2.13.0 起与之前的库一起提供。有关库更改方式以及如何将任何现有查询迁移到模块化 API 的信息,请参阅 CodeQL 查询编写的新的数据流 API.

如果数据流查询没有生成您预期看到的结果,则可以使用部分流来调试问题。

在 CodeQL 中,您可以使用 数据流分析 来计算变量在程序中的不同位置可能持有的值。典型的數據流查詢如下所示

module MyConfig implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node node) { node instanceof MySource }

  predicate isSink(DataFlow::Node node) { node instanceof MySink }
}

module MyFlow = TaintTracking::Global<MyConfig>;

from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Sink is reached from $@.", source.getNode(), "here"

通过不使用 路径解释,可以稍微简化相同的查询

from DataFlow::Node source, DataFlow::Node sink
where MyFlow::flow(source, sink)
select sink, "Sink is reached from $@.", source.getNode(), "here"

如果您编写的數據流查詢没有生成您期望它生成的结果,则您的查询可能存在问题。您可以尝试按照以下步骤调试潜在问题。

检查源和接收器

首先,您应该确保源和接收器定义包含您期望的内容。如果源或接收器为空,则永远不会有任何数据流。检查此方法的最简单方法是使用 CodeQL for VS Code 中的快速评估。选择文本 node instanceof MySource,右键单击并选择“CodeQL: 快速评估”。这将评估突出显示的文本,在本例中表示源集。有关更多信息,请参阅 GitHub 文档中的 运行 CodeQL 查询

如果源和接收器定义看起来都很好,那么我们需要寻找缺少的流步骤。

fieldFlowBranchLimit

数据流配置包含一个名为 fieldFlowBranchLimit 的参数。如果将值设置得太高,您可能会遇到性能下降,但如果设置得太低,您可能会错过结果。在调试数据流时,尝试将 fieldFlowBranchLimit 设置为一个较高的值,并查看您的查询是否生成了更多结果。例如,尝试将以下内容添加到您的配置中

int fieldFlowBranchLimit() { result = 5000 }

如果没有结果并且性能仍然可用,那么最好在进行进一步调试时将此值设置为一个较高的值。

部分流

下一个简单的步骤可能是将接收器定义更改为 any()。这意味着我们将获得大量流向从源可到达的所有位置。虽然这种方法在某些情况下可能有效,但您可能会发现它会生成如此多的结果,以至于很难探索这些发现。它还会严重影响查询性能。更重要的是,您可能甚至无法看到所有部分流路径。这是因为数据流库竭尽全力修剪不可能的路径,并且由于字段存储和读取必须沿路径均匀匹配,因此我们永远不会看到经过存储但无法到达相应读取的路径。这使得很难看到流实际上在哪里停止。

为了避免这些问题,数据流库提供了一种用于探索部分流的机制,该机制试图处理这些警告。这是 MyFlow::FlowExplorationFwd<explorationLimit/0>::partialFlow 谓词

/**
 * Holds if there is a partial data flow path from `source` to `node`. The
 * approximate distance between `node` and the closest source is `dist` and
 * is restricted to be less than or equal to `explorationLimit()`. This
 * predicate completely disregards sink definitions.
 *
 * This predicate is intended for dataflow exploration and debugging and may
 * perform poorly if the number of sources is too big and/or the exploration
 * limit is set too high without using barriers.
 *
 * To use this in a `path-problem` query, import the module `PartialPathGraph`.
 */
predicate partialFlow(PartialPathNode source, PartialPathNode node, int dist) {

还有一个 MyFlow::FlowExplorationRev<explorationLimit/0>::partialFlow 用于从接收器向后探索流。

要访问这些谓词,您必须使用探索限制实例化 MyFlow::FlowExplorationFwd<> 模块(或对于反向流,实例化 MyFlow::FlowExplorationRev<> 模块)。例如

int explorationLimit() { result = 5 }

module MyPartialFlow = MyFlow::FlowExplorationFwd<explorationLimit/0>;

这定义了 partialFlow 返回结果的探索半径。

将单个源作为流探索的起点很有用。最简单的方法是在 isSource 谓词中添加一个临时限制。

要快速评估部分流,最简单的方法通常是向查询中添加一个专门用于快速评估的谓词(右键单击谓词名称,然后选择“CodeQL: 快速评估”)。一个好的起点是像这样

predicate adhocPartialFlow(Callable c, MyPartialFlow::PartialPathNode n, DataFlow::Node src, int dist) {
  exists(MyPartialFlow::PartialPathNode source |
    MyPartialFlow::partialFlow(source, n, dist) and
    src = source.getNode() and
    c = n.getNode().getEnclosingCallable()
  )
}

如果您专注于单个源,那么 src 列是多余的。您当然也可以根据 n 添加其他感兴趣的列,但至少包括封闭的可调用项和到源的距离通常是推荐的,因为它们是用于对结果进行排序以更好地检查结果的有用列。

如果看到大量部分流结果,您可以通过以下两种方式进行聚焦

  • 如果流沿着预期的路径走得很远,这会导致大量无趣的流包含在探索半径中。为了减少无趣的流的数量,您可以用沿路径出现的合适 node 替换源定义,并从该点重新启动部分流探索。
  • 可以巧妙地使用障碍来切断无趣的流路径。这在调试时也会减少要探索的部分流结果的数量。
  • ©GitHub, Inc.
  • 条款
  • 隐私