分析 Python 中的控制流¶
您可以编写 CodeQL 查询来探索 Python 程序的控制流图,例如,发现不可到达的代码或互斥的代码块。
关于分析控制流¶
要分析 Scope
的控制流图,我们可以使用两个 CodeQL 类 ControlFlowNode
和 BasicBlock
。这些类允许您提出诸如“您能否从点 A 抵达点 B?”或“是否可以到达点 B 而不 经过点 A?”之类的问题。为了报告结果,我们使用类 AstNode
,它表示语法元素并对应于源代码 - 使查询的结果更易于理解。有关更多信息,请参阅维基百科上的 控制流图。
类 ControlFlowNode
¶
类 ControlFlowNode
表示控制流图中的节点。AST 节点与控制流节点之间存在一对多关系。每个语法元素,即 AstNode,
映射到零个、一个或多个 ControlFlowNode
类,但每个 ControlFlowNode
映射到正好一个 AstNode
。
为了说明为什么需要这种复杂的关系,请考虑以下 Python 代码
try:
might_raise()
if cond:
break
finally:
close_resource()
上面代码中存在许多路径。对 close_resource();
的调用有三种不同的路径:一条正常路径、一条跳出循环的路径以及一条由 might_raise()
引发异常的路径。
带注释的流程图
类 ControlFlowNode
和 AstNode
最简单的用法是查找不可到达的代码。任何 AstNode
的每条路径都有一个 ControlFlowNode
,任何不可到达的 AstNode
都没有路径流经它。因此,任何没有相应 ControlFlowNode
的 AstNode
都是不可到达的。
查找不可到达的 AST 节点的示例¶
import python
from AstNode node
where not exists(node.getAFlowNode())
select node
许多代码库都包含一些没有控制流节点的代码,因此这些代码是不可到达的。但是,由于 Module
类也是 AstNode
类的子类,因此查询还会找到以 C 语言实现或没有源代码的任何模块。因此,最好查找所有不可到达的语句。
类 BasicBlock
¶
类 BasicBlock
表示控制流节点的基本块。类 BasicBlock
对直接编写查询来说不是很有用,但对于构建复杂分析(例如数据流)非常有用。它之所以有用,是因为它共享了控制流节点的许多有趣属性,例如,什么可以到达什么以及什么支配什么,但基本块比控制流节点少 - 从而导致查询速度更快且使用更少的内存。有关更多信息,请参阅维基百科上的 基本块 和 支配器。
查找互斥基本块的示例¶
假设我们有以下 Python 代码
if condition():
return 0
pass
我们能否确定在一次执行此代码时不可能同时到达 return 0
语句和 pass
语句?对于两个基本块要互斥,必须不可能从另一个基本块到达其中任何一个。我们可以编写
import python
from BasicBlock b1, BasicBlock b2
where b1 != b2 and not b1.strictlyReaches(b2) and not b2.strictlyReaches(b1)
select b1, b2
但是,根据这个定义,如果两个基本块位于不同的作用域中,那么它们是互斥的。为了使结果更有用,我们需要这两个基本块都能够从同一个函数入口点到达
exists(Function shared, BasicBlock entry |
entry.contains(shared.getEntryNode()) and
entry.strictlyReaches(b1) and entry.strictlyReaches(b2)
)
结合这些条件,我们得到
查找同一个函数内互斥块的示例¶
import python
from BasicBlock b1, BasicBlock b2
where b1 != b2 and not b1.strictlyReaches(b2) and not b2.strictlyReaches(b1) and
exists(Function shared, BasicBlock entry |
entry.contains(shared.getEntryNode()) and
entry.strictlyReaches(b1) and entry.strictlyReaches(b2)
)
select b1, b2
这通常会产生大量结果,因为它在正常的控制流中很常见。但是,它说明了可能进行的控制流分析类型。此类控制流分析对于数据流分析来说是一种重要的辅助工具。有关更多信息,请参阅“分析 Python 中的数据流。”