Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions python/change-notes/2021-03-12-small-api-enhancements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lgtm,codescanning
* The class ParameterNode now extends LocalSourceNode, thus making methods like flowsTo available.
Comment thread
yoff marked this conversation as resolved.
* The new predicate `parameterNode` can now be used to map from a `Parameter` to a data-flow node.
110 changes: 6 additions & 104 deletions python/ql/src/semmle/python/dataflow/new/internal/DataFlowPublic.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ private import python
private import DataFlowPrivate
import semmle.python.dataflow.new.TypeTracker
import Attributes
import LocalSources
private import semmle.python.essa.SsaCompute

/**
Expand Down Expand Up @@ -135,7 +136,7 @@ class Node extends TNode {
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }

/**
* Gets a local source node from which data may flow to this node in zero or more local steps.
* Gets a local source node from which data may flow to this node in zero or more local data-flow steps.
*/
LocalSourceNode getALocalSource() { result.flowsTo(this) }
}
Expand Down Expand Up @@ -215,7 +216,7 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends CfgNode {
class ParameterNode extends CfgNode, LocalSourceNode {
ParameterDefinition def;

ParameterNode() {
Expand All @@ -237,6 +238,9 @@ class ParameterNode extends CfgNode {
Parameter getParameter() { result = def.getParameter() }
}

/** Gets a node corresponding to parameter `p`. */
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }

/** A data flow node that represents a call argument. */
class ArgumentNode extends Node {
ArgumentNode() { this = any(DataFlowCall c).getArg(_) }
Expand Down Expand Up @@ -467,108 +471,6 @@ class BarrierGuard extends GuardNode {
}
}

private predicate comes_from_cfgnode(Node node) {
exists(CfgNode first, Node second |
simpleLocalFlowStep(first, second) and
simpleLocalFlowStep*(second, node)
)
}

/**
* A data flow node that is a source of local flow. This includes things like
* - Expressions
* - Function parameters
*/
class LocalSourceNode extends Node {
cached
LocalSourceNode() {
not comes_from_cfgnode(this) and
not this instanceof ModuleVariableNode
or
this = any(ModuleVariableNode mvn).getARead()
}

/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
pragma[inline]
predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) }

/**
* Gets a reference (read or write) of attribute `attrName` on this node.
*/
AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) }

/**
* Gets a read of attribute `attrName` on this node.
*/
AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) }

/**
* Gets a reference (read or write) of any attribute on this node.
*/
AttrRef getAnAttributeReference() {
Cached::namedAttrRef(this, _, result)
or
Cached::dynamicAttrRef(this, result)
}

/**
* Gets a read of any attribute on this node.
*/
AttrRead getAnAttributeRead() { result = getAnAttributeReference() }

/**
* Gets a call to this node.
*/
CallCfgNode getACall() { Cached::call(this, result) }
}

cached
private module Cached {
/**
* Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps.
*
* The slightly backwards parametering ordering is to force correct indexing.
*/
cached
predicate hasLocalSource(Node sink, LocalSourceNode source) {
source = sink
or
exists(Node second |
simpleLocalFlowStep(source, second) and
simpleLocalFlowStep*(second, sink)
)
}

/**
* Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`.
*/
cached
predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) {
base.flowsTo(ref.getObject()) and
ref.getAttributeName() = attr
}

/**
* Holds if `base` flows to the base of `ref` and `ref` has no known attribute name.
*/
cached
predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) {
base.flowsTo(ref.getObject()) and
not exists(ref.getAttributeName())
}

/**
* Holds if `func` flows to the callee of `call`.
*/
cached
predicate call(LocalSourceNode func, CallCfgNode call) {
exists(CfgNode n |
func.flowsTo(n) and
n = call.getFunction()
)
}
}

/**
* Algebraic datatype for tracking data content associated with values.
* Content can be collection elements or object attributes.
Expand Down
113 changes: 113 additions & 0 deletions python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Provides support for intra-procedural tracking of a customizable
* set of data flow nodes.
*
* Note that unlike `TypeTracker.qll`, this library only performs
* local tracking within a function.
*/

import python
import DataFlowPublic
private import DataFlowPrivate

private predicate comes_from_cfgnode(Node node) {
exists(CfgNode first, Node second |
simpleLocalFlowStep(first, second) and
simpleLocalFlowStep*(second, node)
)
}

/**
* A data flow node that is a source of local flow. This includes things like
* - Expressions
* - Function parameters
*/
class LocalSourceNode extends Node {
cached
LocalSourceNode() {
not comes_from_cfgnode(this) and
not this instanceof ModuleVariableNode
or
this = any(ModuleVariableNode mvn).getARead()
}

/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
pragma[inline]
predicate flowsTo(Node nodeTo) { Cached::hasLocalSource(nodeTo, this) }

/**
* Gets a reference (read or write) of attribute `attrName` on this node.
*/
AttrRef getAnAttributeReference(string attrName) { Cached::namedAttrRef(this, attrName, result) }

/**
* Gets a read of attribute `attrName` on this node.
*/
AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) }

/**
* Gets a reference (read or write) of any attribute on this node.
*/
AttrRef getAnAttributeReference() {
Cached::namedAttrRef(this, _, result)
or
Cached::dynamicAttrRef(this, result)
}

/**
* Gets a read of any attribute on this node.
*/
AttrRead getAnAttributeRead() { result = getAnAttributeReference() }

/**
* Gets a call to this node.
*/
CallCfgNode getACall() { Cached::call(this, result) }
}

cached
private module Cached {
/**
* Holds if `source` is a `LocalSourceNode` that can reach `sink` via local flow steps.
*
* The slightly backwards parametering ordering is to force correct indexing.
*/
cached
predicate hasLocalSource(Node sink, LocalSourceNode source) {
source = sink
or
exists(Node second |
simpleLocalFlowStep(source, second) and
simpleLocalFlowStep*(second, sink)
)
}

/**
* Holds if `base` flows to the base of `ref` and `ref` has attribute name `attr`.
*/
cached
predicate namedAttrRef(LocalSourceNode base, string attr, AttrRef ref) {
base.flowsTo(ref.getObject()) and
ref.getAttributeName() = attr
}

/**
* Holds if `base` flows to the base of `ref` and `ref` has no known attribute name.
*/
cached
predicate dynamicAttrRef(LocalSourceNode base, AttrRef ref) {
base.flowsTo(ref.getObject()) and
not exists(ref.getAttributeName())
}

/**
* Holds if `func` flows to the callee of `call`.
*/
cached
predicate call(LocalSourceNode func, CallCfgNode call) {
exists(CfgNode n |
func.flowsTo(n) and
n = call.getFunction()
)
}
}