mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge branch 'main' into python-add-global-flow-steps
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
private import python
|
||||
private import DataFlowPublic
|
||||
import semmle.python.SpecialMethods
|
||||
private import semmle.python.essa.SsaCompute
|
||||
|
||||
//--------
|
||||
// Data flow graph
|
||||
@@ -31,7 +32,7 @@ class StorePreUpdateNode extends PreUpdateNode, CfgNode {
|
||||
}
|
||||
}
|
||||
|
||||
/** A node marking the state change of an object after a read */
|
||||
/** A node marking the state change of an object after a read. */
|
||||
class ReadPreUpdateNode extends PreUpdateNode, CfgNode {
|
||||
ReadPreUpdateNode() {
|
||||
exists(Attribute a |
|
||||
@@ -97,12 +98,19 @@ module EssaFlow {
|
||||
contextManager.strictlyDominates(var)
|
||||
)
|
||||
or
|
||||
// Use
|
||||
// First use after definition
|
||||
// `y = 42`
|
||||
// `x = f(y)`
|
||||
// nodeFrom is `y` on first line, essa var
|
||||
// nodeTo is `y` on second line, cfg node
|
||||
nodeFrom.(EssaNode).getVar().getASourceUse() = nodeTo.(CfgNode).getNode()
|
||||
defToFirstUse(nodeFrom.asVar(), nodeTo.asCfgNode())
|
||||
or
|
||||
// Next use after use
|
||||
// `x = f(y)`
|
||||
// `z = y + 1`
|
||||
// nodeFrom is 'y' on first line, cfg node
|
||||
// nodeTo is `y` on second line, cfg node
|
||||
useToNextUse(nodeFrom.asCfgNode(), nodeTo.asCfgNode())
|
||||
or
|
||||
// Refinements
|
||||
exists(EssaEdgeRefinement r |
|
||||
@@ -126,6 +134,14 @@ module EssaFlow {
|
||||
// Module variable write
|
||||
nodeFrom = nodeTo.(ModuleVariableNode).getAWrite()
|
||||
}
|
||||
|
||||
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
|
||||
AdjacentUses::adjacentUseUseSameVar(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
|
||||
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
//--------
|
||||
@@ -137,20 +153,27 @@ module EssaFlow {
|
||||
* excludes SSA flow through instance fields.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
not nodeFrom.asVar() instanceof GlobalSsaVariable and
|
||||
not nodeTo.asVar() instanceof GlobalSsaVariable and
|
||||
not nodeFrom instanceof ModuleVariableNode and
|
||||
not nodeTo instanceof ModuleVariableNode and
|
||||
EssaFlow::essaFlowStep(update(nodeFrom), nodeTo)
|
||||
// If there is ESSA-flow out of a node `node`, we want flow
|
||||
// both out of `node` and any post-update node of `node`.
|
||||
exists(Node node |
|
||||
not node.(EssaNode).getVar() instanceof GlobalSsaVariable and
|
||||
not nodeTo.(EssaNode).getVar() instanceof GlobalSsaVariable and
|
||||
EssaFlow::essaFlowStep(node, nodeTo) and
|
||||
nodeFrom = update(node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `result` is either `node`, or the post-update node for `node`.
|
||||
*/
|
||||
private Node update(Node node) {
|
||||
exists(PostUpdateNode pun |
|
||||
node = pun.getPreUpdateNode() and
|
||||
result = pun
|
||||
)
|
||||
or
|
||||
not exists(PostUpdateNode pun | node = pun.getPreUpdateNode()) and
|
||||
result = node
|
||||
}
|
||||
|
||||
@@ -467,6 +490,10 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// Dictionary
|
||||
nodeTo.getNode().getNode().(DictComp).getElt() = nodeFrom.getNode().getNode() and
|
||||
c instanceof DictionaryElementAnyContent
|
||||
or
|
||||
// Generator
|
||||
nodeTo.getNode().getNode().(GeneratorExp).getElt() = nodeFrom.getNode().getNode() and
|
||||
c instanceof ListElementContent
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -544,33 +571,23 @@ predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) {
|
||||
// nodeFrom is `l`, cfg node
|
||||
// nodeTo is `x`, essa var
|
||||
// c denotes element of list or set
|
||||
exists(For f, Comp comp |
|
||||
f = getCompFor(comp) and
|
||||
nodeFrom.getNode().getNode() = getCompIter(comp) and
|
||||
exists(Comp comp |
|
||||
// outermost for
|
||||
nodeFrom.getNode().getNode() = comp.getIterable() and
|
||||
nodeTo.getVar().getDefinition().(AssignmentDefinition).getDefiningNode().getNode() =
|
||||
f.getTarget() and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
comp.getIterationVariable(0).getAStore()
|
||||
or
|
||||
// an inner for
|
||||
exists(int n | n > 0 |
|
||||
nodeFrom.getNode().getNode() = comp.getNthInnerLoop(n).getIter() and
|
||||
nodeTo.getVar().getDefinition().(AssignmentDefinition).getDefiningNode().getNode() =
|
||||
comp.getNthInnerLoop(n).getTarget()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** This seems to compensate for extractor shortcomings */
|
||||
For getCompFor(Comp c) {
|
||||
c.contains(result) and
|
||||
c.getFunction() = result.getScope()
|
||||
}
|
||||
|
||||
/** This seems to compensate for extractor shortcomings */
|
||||
AstNode getCompIter(Comp c) {
|
||||
c.contains(result) and
|
||||
c.getScope() = result.getScope() and
|
||||
not result = c.getFunction() and
|
||||
not exists(AstNode between |
|
||||
c.contains(between) and
|
||||
between.contains(result)
|
||||
) and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ private import experimental.dataflow.internal.DataFlowPrivate
|
||||
private import experimental.dataflow.internal.TaintTrackingPublic
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a barrier in all global taint flow configurations
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `nodeFrom` to `nodeTo` should be included in all
|
||||
|
||||
@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
defaultTaintBarrier(node)
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
|
||||
@@ -4,18 +4,22 @@ import python
|
||||
abstract class Comp extends Expr {
|
||||
abstract Function getFunction();
|
||||
|
||||
/** Gets the iteration variable for the nth innermost generator of this list comprehension */
|
||||
/** Gets the iterable of this set comprehension. */
|
||||
abstract Expr getIterable();
|
||||
|
||||
/** Gets the iteration variable for the nth innermost generator of this comprehension. */
|
||||
Variable getIterationVariable(int n) {
|
||||
result.getAnAccess() = this.getNthInnerLoop(n).getTarget()
|
||||
}
|
||||
|
||||
private For getNthInnerLoop(int n) {
|
||||
/** Gets the nth innermost For expression of this comprehension. */
|
||||
For getNthInnerLoop(int n) {
|
||||
n = 0 and result = this.getFunction().getStmt(0)
|
||||
or
|
||||
result = this.getNthInnerLoop(n - 1).getStmt(0)
|
||||
}
|
||||
|
||||
/** Gets the iteration variable for a generator of this list comprehension */
|
||||
/** Gets the iteration variable for a generator of this list comprehension. */
|
||||
Variable getAnIterationVariable() { result = this.getIterationVariable(_) }
|
||||
|
||||
/** Gets the scope in which the body of this list comprehension evaluates. */
|
||||
@@ -62,6 +66,8 @@ class ListComp extends ListComp_, Comp {
|
||||
|
||||
override Function getFunction() { result = ListComp_.super.getFunction() }
|
||||
|
||||
override Expr getIterable() { result = ListComp_.super.getIterable() }
|
||||
|
||||
override string toString() { result = ListComp_.super.toString() }
|
||||
|
||||
override Expr getElt() { result = Comp.super.getElt() }
|
||||
@@ -79,6 +85,8 @@ class SetComp extends SetComp_, Comp {
|
||||
override predicate hasSideEffects() { any() }
|
||||
|
||||
override Function getFunction() { result = SetComp_.super.getFunction() }
|
||||
|
||||
override Expr getIterable() { result = SetComp_.super.getIterable() }
|
||||
}
|
||||
|
||||
/** A dictionary comprehension, such as `{ k:v for k, v in enumerate("0123456789") }` */
|
||||
@@ -93,6 +101,8 @@ class DictComp extends DictComp_, Comp {
|
||||
override predicate hasSideEffects() { any() }
|
||||
|
||||
override Function getFunction() { result = DictComp_.super.getFunction() }
|
||||
|
||||
override Expr getIterable() { result = DictComp_.super.getIterable() }
|
||||
}
|
||||
|
||||
/** A generator expression, such as `(var for var in iterable)` */
|
||||
@@ -107,4 +117,6 @@ class GeneratorExp extends GeneratorExp_, Comp {
|
||||
override predicate hasSideEffects() { any() }
|
||||
|
||||
override Function getFunction() { result = GeneratorExp_.super.getFunction() }
|
||||
|
||||
override Expr getIterable() { result = GeneratorExp_.super.getIterable() }
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ private module SsaComputeImpl {
|
||||
defUseRank(v, b, rankix, i)
|
||||
}
|
||||
|
||||
/** A `VarAccess` `use` of `v` in `b` at index `i`. */
|
||||
/** A variable access `use` of `v` in `b` at index `i`. */
|
||||
cached
|
||||
predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) {
|
||||
(v.getAUse() = use or v.hasRefinement(use, _)) and
|
||||
@@ -348,11 +348,147 @@ private module SsaComputeImpl {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
module AdjacentUsesImpl {
|
||||
/**
|
||||
* Holds if `rankix` is the rank the index `i` at which there is an SSA definition or explicit use of
|
||||
* `v` in the basic block `b`.
|
||||
*/
|
||||
cached
|
||||
predicate defSourceUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) {
|
||||
i = rank[rankix](int j | variableDefine(v, _, b, j) or variableSourceUse(v, _, b, j))
|
||||
}
|
||||
|
||||
/** A variable access `use` of `v` in `b` at index `i`. */
|
||||
cached
|
||||
predicate variableSourceUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) {
|
||||
v.getASourceUse() = use and
|
||||
exists(int j |
|
||||
b.getNode(j) = use and
|
||||
i = 2 * j
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the maximum rank index for the given variable and basic block. */
|
||||
private int lastSourceUseRank(SsaSourceVariable v, BasicBlock b) {
|
||||
result = max(int rankix | defSourceUseRank(v, b, rankix, _))
|
||||
}
|
||||
|
||||
/** Holds if `v` is defined or used in `b`. */
|
||||
private predicate varOccursInBlock(SsaSourceVariable v, BasicBlock b) {
|
||||
defSourceUseRank(v, b, _, _)
|
||||
}
|
||||
|
||||
/** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */
|
||||
private predicate blockPrecedesVar(SsaSourceVariable v, BasicBlock b) {
|
||||
varOccursInBlock(v, b.getASuccessor*())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b2` is a transitive successor of `b1` and `v` occurs in `b1` and
|
||||
* in `b2` or one of its transitive successors but not in any block on the path
|
||||
* between `b1` and `b2`.
|
||||
*/
|
||||
private predicate varBlockReaches(SsaSourceVariable v, BasicBlock b1, BasicBlock b2) {
|
||||
varOccursInBlock(v, b1) and
|
||||
b2 = b1.getASuccessor() and
|
||||
blockPrecedesVar(v, b2)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(v, b1, mid) and
|
||||
b2 = mid.getASuccessor() and
|
||||
not varOccursInBlock(v, mid) and
|
||||
blockPrecedesVar(v, b2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b2` is a transitive successor of `b1` and `v` occurs in `b1` and
|
||||
* `b2` but not in any block on the path between `b1` and `b2`.
|
||||
*/
|
||||
private predicate varBlockStep(SsaSourceVariable v, BasicBlock b1, BasicBlock b2) {
|
||||
varBlockReaches(v, b1, b2) and
|
||||
varOccursInBlock(v, b2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` occurs at index `i1` in `b1` and at index `i2` in `b2` and
|
||||
* there is a path between them without any occurrence of `v`.
|
||||
*/
|
||||
cached
|
||||
predicate adjacentVarRefs(SsaSourceVariable v, BasicBlock b1, int i1, BasicBlock b2, int i2) {
|
||||
exists(int rankix |
|
||||
b1 = b2 and
|
||||
defSourceUseRank(v, b1, rankix, i1) and
|
||||
defSourceUseRank(v, b2, rankix + 1, i2)
|
||||
)
|
||||
or
|
||||
defSourceUseRank(v, b1, lastSourceUseRank(v, b1), i1) and
|
||||
varBlockStep(v, b1, b2) and
|
||||
defSourceUseRank(v, b2, 1, i2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use1` and `use2` form an adjacent use-use-pair of the same SSA
|
||||
* variable, that is, the value read in `use1` can reach `use2` without passing
|
||||
* through any other use or any SSA definition of the variable.
|
||||
*/
|
||||
cached
|
||||
predicate adjacentUseUseSameVar(ControlFlowNode use1, ControlFlowNode use2) {
|
||||
exists(SsaSourceVariable v, BasicBlock b1, int i1, BasicBlock b2, int i2 |
|
||||
adjacentVarRefs(v, b1, i1, b2, i2) and
|
||||
variableSourceUse(v, use1, b1, i1) and
|
||||
variableSourceUse(v, use2, b2, i2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value defined at `def` can reach `use` without passing through
|
||||
* any other uses, but possibly through phi nodes and uncertain implicit updates.
|
||||
*/
|
||||
cached
|
||||
predicate firstUse(EssaDefinition def, ControlFlowNode use) {
|
||||
exists(SsaSourceVariable v, BasicBlock b1, int i1, BasicBlock b2, int i2 |
|
||||
adjacentVarRefs(v, b1, i1, b2, i2) and
|
||||
definesAt(def, v, b1, i1) and
|
||||
variableSourceUse(v, use, b2, i2)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SsaSourceVariable v, EssaDefinition redef, BasicBlock b1, int i1, BasicBlock b2, int i2
|
||||
|
|
||||
redef instanceof PhiFunction
|
||||
|
|
||||
adjacentVarRefs(v, b1, i1, b2, i2) and
|
||||
definesAt(def, v, b1, i1) and
|
||||
definesAt(redef, v, b2, i2) and
|
||||
firstUse(redef, use)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` defines `v` at the specified position.
|
||||
* Phi nodes are placed at index -1.
|
||||
*/
|
||||
cached
|
||||
predicate definesAt(EssaDefinition def, SsaSourceVariable v, BasicBlock b, int i) {
|
||||
exists(ControlFlowNode defNode |
|
||||
def.(EssaNodeDefinition).definedBy(v, defNode) and
|
||||
variableDefine(v, defNode, b, i)
|
||||
)
|
||||
or
|
||||
v = def.(PhiFunction).getSourceVariable() and
|
||||
b = def.(PhiFunction).getBasicBlock() and
|
||||
i = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import SsaComputeImpl::SsaDefinitionsImpl as SsaDefinitions
|
||||
import SsaComputeImpl::EssaDefinitionsImpl as EssaDefinitions
|
||||
import SsaComputeImpl::LivenessImpl as Liveness
|
||||
import SsaComputeImpl::AdjacentUsesImpl as AdjacentUses
|
||||
|
||||
/* This is exported primarily for testing */
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user