mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge pull request #4013 from yoff/SharedDataflow_SequenceFlow
Python: Shared dataflow: Content flow
This commit is contained in:
@@ -277,15 +277,193 @@ predicate jumpStep(Node pred, Node succ) {
|
||||
// Field flow
|
||||
//--------
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to
|
||||
* Holds if data can flow from `nodeFrom` to `nodeTo` via an assignment to
|
||||
* content `c`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content c, Node node2) { none() }
|
||||
predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
listStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
setStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
tupleStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
dictStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
comprehensionStoreStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
/** Data flows from an element of a list to the list. */
|
||||
predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo) {
|
||||
// List
|
||||
// `[..., 42, ...]`
|
||||
// nodeFrom is `42`, cfg node
|
||||
// nodeTo is the list, `[..., 42, ...]`, cfg node
|
||||
// c denotes element of list
|
||||
nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode()
|
||||
}
|
||||
|
||||
/** Data flows from an element of a set to the set. */
|
||||
predicate setStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo) {
|
||||
// Set
|
||||
// `{..., 42, ...}`
|
||||
// nodeFrom is `42`, cfg node
|
||||
// nodeTo is the set, `{..., 42, ...}`, cfg node
|
||||
// c denotes element of list
|
||||
nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode()
|
||||
}
|
||||
|
||||
/** Data flows from an element of a tuple to the tuple at a specific index. */
|
||||
predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo) {
|
||||
// Tuple
|
||||
// `(..., 42, ...)`
|
||||
// nodeFrom is `42`, cfg node
|
||||
// nodeTo is the tuple, `(..., 42, ...)`, cfg node
|
||||
// c denotes element of tuple and index of nodeFrom
|
||||
exists(int n |
|
||||
nodeTo.getNode().(TupleNode).getElement(n) = nodeFrom.getNode() and
|
||||
c.getIndex() = n
|
||||
)
|
||||
}
|
||||
|
||||
/** Data flows from an element of a dictionary to the dictionary at a specific key. */
|
||||
predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, CfgNode nodeTo) {
|
||||
// Dictionary
|
||||
// `{..., "key" = 42, ...}`
|
||||
// nodeFrom is `42`, cfg node
|
||||
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
|
||||
// c denotes element of dictionary and the key `"key"`
|
||||
exists(KeyValuePair item |
|
||||
item = nodeTo.getNode().(DictNode).getNode().(Dict).getAnItem() and
|
||||
nodeFrom.getNode().getNode() = item.getValue() and
|
||||
c.getKey() = item.getKey().(StrConst).getS()
|
||||
)
|
||||
}
|
||||
|
||||
/** Data flows from an element expression in a comprehension to the comprehension. */
|
||||
predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// Comprehension
|
||||
// `[x+1 for x in l]`
|
||||
// nodeFrom is `x+1`, cfg node
|
||||
// nodeTo is `[x+1 for x in l]`, cfg node
|
||||
// c denotes list or set or dictionary without index
|
||||
//
|
||||
// List
|
||||
nodeTo.getNode().getNode().(ListComp).getElt() = nodeFrom.getNode().getNode() and
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
// Set
|
||||
nodeTo.getNode().getNode().(SetComp).getElt() = nodeFrom.getNode().getNode() and
|
||||
c instanceof SetElementContent
|
||||
or
|
||||
// Dictionary
|
||||
nodeTo.getNode().getNode().(DictComp).getElt() = nodeFrom.getNode().getNode() and
|
||||
c instanceof DictionaryElementAnyContent
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
|
||||
* Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content c, Node node2) { none() }
|
||||
predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
subscriptReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
popReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
comprehensionReadStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
/** Data flows from a sequence to a subscript of the sequence. */
|
||||
predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// Subscript
|
||||
// `l[3]`
|
||||
// nodeFrom is `l`, cfg node
|
||||
// nodeTo is `l[3]`, cfg node
|
||||
// c is compatible with 3
|
||||
nodeFrom.getNode() = nodeTo.getNode().(SubscriptNode).getObject() and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
or
|
||||
c instanceof DictionaryElementAnyContent
|
||||
or
|
||||
c.(TupleElementContent).getIndex() =
|
||||
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
|
||||
or
|
||||
c.(DictionaryElementContent).getKey() =
|
||||
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(StrConst).getS()
|
||||
)
|
||||
}
|
||||
|
||||
/** Data flows from a sequence to a call to `pop` on the sequence. */
|
||||
predicate popReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// set.pop or list.pop
|
||||
// `s.pop()`
|
||||
// nodeFrom is `s`, cfg node
|
||||
// nodeTo is `s.pop()`, cfg node
|
||||
// c denotes element of list or set
|
||||
exists(CallNode call, AttrNode a |
|
||||
call.getFunction() = a and
|
||||
a.getName() = "pop" and // Should match appropriate call since we tracked a sequence here.
|
||||
not exists(call.getAnArg()) and
|
||||
nodeFrom.getNode() = a.getObject() and
|
||||
nodeTo.getNode() = call and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
)
|
||||
)
|
||||
or
|
||||
// dict.pop
|
||||
// `d.pop("key")`
|
||||
// nodeFrom is `d`, cfg node
|
||||
// nodeTo is `d.pop("key")`, cfg node
|
||||
// c denotes the key `"key"`
|
||||
exists(CallNode call, AttrNode a |
|
||||
call.getFunction() = a and
|
||||
a.getName() = "pop" and // Should match appropriate call since we tracked a dictionary here.
|
||||
nodeFrom.getNode() = a.getObject() and
|
||||
nodeTo.getNode() = call and
|
||||
c.(DictionaryElementContent).getKey() = call.getArg(0).getNode().(StrConst).getS()
|
||||
)
|
||||
}
|
||||
|
||||
/** Data flows from a iterated sequence to the variable iterating over the sequence. */
|
||||
predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) {
|
||||
// Comprehension
|
||||
// `[x+1 for x in l]`
|
||||
// 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
|
||||
nodeTo.getVar().getDefinition().(AssignmentDefinition).getDefiningNode().getNode() =
|
||||
f.getTarget() and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** 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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`. For example,
|
||||
|
||||
@@ -33,8 +33,15 @@ class Node extends TNode {
|
||||
/** Gets the scope of this node. */
|
||||
Scope getScope() { none() }
|
||||
|
||||
private DataFlowCallable getCallableScope(Scope s) {
|
||||
result.getScope() = s
|
||||
or
|
||||
not exists(DataFlowCallable c | c.getScope() = s) and
|
||||
result = getCallableScope(s.getEnclosingScope())
|
||||
}
|
||||
|
||||
/** Gets the enclosing callable of this node. */
|
||||
DataFlowCallable getEnclosingCallable() { result.getScope() = this.getScope() }
|
||||
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
||||
|
||||
/** Gets the location of this node */
|
||||
Location getLocation() { none() }
|
||||
@@ -138,6 +145,62 @@ class BarrierGuard extends Expr {
|
||||
/**
|
||||
* A reference contained in an object. This is either a field or a property.
|
||||
*/
|
||||
class Content extends string {
|
||||
Content() { this = "Content" }
|
||||
newtype TContent =
|
||||
/** An element of a list. */
|
||||
TListElementContent() or
|
||||
/** An element of a set. */
|
||||
TSetElementContent() or
|
||||
/** An element of a tuple at a specifik index. */
|
||||
TTupleElementContent(int index) { exists(any(TupleNode tn).getElement(index)) } or
|
||||
/** An element of a dictionary under a specific key. */
|
||||
TDictionaryElementContent(string key) {
|
||||
key = any(KeyValuePair kvp).getKey().(StrConst).getS()
|
||||
or
|
||||
key = any(Keyword kw).getArg()
|
||||
} or
|
||||
/** An element of a dictionary at any key. */
|
||||
TDictionaryElementAnyContent()
|
||||
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Content" }
|
||||
}
|
||||
|
||||
class ListElementContent extends TListElementContent, Content {
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = "List element" }
|
||||
}
|
||||
|
||||
class SetElementContent extends TSetElementContent, Content {
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = "Set element" }
|
||||
}
|
||||
|
||||
class TupleElementContent extends TTupleElementContent, Content {
|
||||
int index;
|
||||
|
||||
TupleElementContent() { this = TTupleElementContent(index) }
|
||||
|
||||
/** Gets the index for this tuple element */
|
||||
int getIndex() { result = index }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = "Tuple element at index " + index.toString() }
|
||||
}
|
||||
|
||||
class DictionaryElementContent extends TDictionaryElementContent, Content {
|
||||
string key;
|
||||
|
||||
DictionaryElementContent() { this = TDictionaryElementContent(key) }
|
||||
|
||||
/** Gets the index for this tuple element */
|
||||
string getKey() { result = key }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = "Dictionary element at key " + key }
|
||||
}
|
||||
|
||||
class DictionaryElementAnyContent extends TDictionaryElementAnyContent, Content {
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = "Any dictionary element" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user