mirror of
https://github.com/github/codeql.git
synced 2025-12-22 03:36:30 +01:00
Merge branch 'main' of https://github.com/github/codeql into python-ruby/track-through-summaries-pm
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
## 0.9.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Type tracking is now aware of reads of captured variables (variables defined in an outer scope). This leads to a richer API graph, and may lead to more results in some queries.
|
||||
* Added more content-flow/field-flow for dictionaries, by adding support for reads through `mydict.get("key")` and `mydict.setdefault("key", value)`, and store steps through `dict["key"] = value` and `mydict.setdefault("key", value)`.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Type tracking is now aware of reads of captured variables (variables defined in an outer scope). This leads to a richer API graph, and may lead to more results in some queries.
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.9.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Type tracking is now aware of reads of captured variables (variables defined in an outer scope). This leads to a richer API graph, and may lead to more results in some queries.
|
||||
* Added more content-flow/field-flow for dictionaries, by adding support for reads through `mydict.get("key")` and `mydict.setdefault("key", value)`, and store steps through `dict["key"] = value` and `mydict.setdefault("key", value)`.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.1
|
||||
lastReleaseVersion: 0.9.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.9.2-dev
|
||||
version: 0.9.3-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -38,6 +38,7 @@ private import DataFlowPrivate
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||
private import semmle.python.internal.CachedStages
|
||||
private import semmle.python.dataflow.new.internal.TypeTracker::CallGraphConstruction as CallGraphConstruction
|
||||
|
||||
newtype TParameterPosition =
|
||||
/** Used for `self` in methods, and `cls` in classmethods. */
|
||||
@@ -467,103 +468,105 @@ private predicate ignoreForCallGraph(File f) {
|
||||
f.getAbsolutePath().matches("%/site-packages/sympy/%")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the function `func`.
|
||||
*/
|
||||
private TypeTrackingNode functionTracker(TypeTracker t, Function func) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
(
|
||||
result.asExpr() = func.getDefinition()
|
||||
private module TrackFunctionInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Function;
|
||||
|
||||
predicate start(Node start, Function func) {
|
||||
start.asExpr() = func.getDefinition()
|
||||
or
|
||||
// when a function is decorated, it's the result of the (last) decorator call that
|
||||
// is used
|
||||
result.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = functionTracker(t2, func).track(t2, t))
|
||||
start.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
|
||||
}
|
||||
|
||||
predicate filter(Node n) { ignoreForCallGraph(n.getLocation().getFile()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the function `func`.
|
||||
*/
|
||||
Node functionTracker(Function func) { functionTracker(TypeTracker::end(), func).flowsTo(result) }
|
||||
Node functionTracker(Function func) {
|
||||
CallGraphConstruction::Simple::Make<TrackFunctionInput>::track(func)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the class `cls`.
|
||||
*/
|
||||
private TypeTrackingNode classTracker(TypeTracker t, Class cls) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
(
|
||||
result.asExpr() = cls.getParent()
|
||||
private module TrackClassInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class cls) {
|
||||
start.asExpr() = cls.getParent()
|
||||
or
|
||||
// when a class is decorated, it's the result of the (last) decorator call that
|
||||
// is used
|
||||
result.asExpr() = cls.getParent().getADecoratorCall()
|
||||
start.asExpr() = cls.getParent().getADecoratorCall()
|
||||
or
|
||||
// `type(obj)`, where obj is an instance of this class
|
||||
result = getTypeCall() and
|
||||
result.(CallCfgNode).getArg(0) = classInstanceTracker(cls)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = classTracker(t2, cls).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
start = getTypeCall() and
|
||||
start.(CallCfgNode).getArg(0) = classInstanceTracker(cls)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the class `cls`.
|
||||
*/
|
||||
Node classTracker(Class cls) { classTracker(TypeTracker::end(), cls).flowsTo(result) }
|
||||
Node classTracker(Class cls) {
|
||||
CallGraphConstruction::Simple::Make<TrackClassInput>::track(cls).(LocalSourceNode).flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to an instance of the class `cls`.
|
||||
*/
|
||||
private TypeTrackingNode classInstanceTracker(TypeTracker t, Class cls) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
resolveClassCall(result.(CallCfgNode).asCfgNode(), cls)
|
||||
or
|
||||
// result of `super().__new__` as used in a `__new__` method implementation
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
exists(Class classUsedInSuper |
|
||||
fromSuperNewCall(result.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
|
||||
classUsedInSuper = getADirectSuperclass*(cls)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = classInstanceTracker(t2, cls).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
private module TrackClassInstanceInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class cls) {
|
||||
resolveClassCall(start.(CallCfgNode).asCfgNode(), cls)
|
||||
or
|
||||
// result of `super().__new__` as used in a `__new__` method implementation
|
||||
exists(Class classUsedInSuper |
|
||||
fromSuperNewCall(start.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
|
||||
classUsedInSuper = getADirectSuperclass*(cls)
|
||||
)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to an instance of the class `cls`.
|
||||
*/
|
||||
Node classInstanceTracker(Class cls) {
|
||||
classInstanceTracker(TypeTracker::end(), cls).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackClassInstanceInput>::track(cls)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `self` argument of a method on class `classWithMethod`.
|
||||
* The method cannot be a `staticmethod` or `classmethod`.
|
||||
*/
|
||||
private TypeTrackingNode selfTracker(TypeTracker t, Class classWithMethod) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
exists(Function func |
|
||||
func = classWithMethod.getAMethod() and
|
||||
not isStaticmethod(func) and
|
||||
not isClassmethod(func)
|
||||
|
|
||||
result.asExpr() = func.getArg(0)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = selfTracker(t2, classWithMethod).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
private module TrackSelfInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class classWithMethod) {
|
||||
exists(Function func |
|
||||
func = classWithMethod.getAMethod() and
|
||||
not isStaticmethod(func) and
|
||||
not isClassmethod(func)
|
||||
|
|
||||
start.asExpr() = func.getArg(0)
|
||||
)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,33 +574,32 @@ private TypeTrackingNode selfTracker(TypeTracker t, Class classWithMethod) {
|
||||
* The method cannot be a `staticmethod` or `classmethod`.
|
||||
*/
|
||||
Node selfTracker(Class classWithMethod) {
|
||||
selfTracker(TypeTracker::end(), classWithMethod).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackSelfInput>::track(classWithMethod)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the enclosing class `classWithMethod` from within one of its
|
||||
* methods, either through the `cls` argument from a `classmethod` or from `type(self)`
|
||||
* from a normal method.
|
||||
*/
|
||||
private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
(
|
||||
private module TrackClsArgumentInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class classWithMethod) {
|
||||
exists(Function func |
|
||||
func = classWithMethod.getAMethod() and
|
||||
isClassmethod(func)
|
||||
|
|
||||
result.asExpr() = func.getArg(0)
|
||||
start.asExpr() = func.getArg(0)
|
||||
)
|
||||
or
|
||||
// type(self)
|
||||
result = getTypeCall() and
|
||||
result.(CallCfgNode).getArg(0) = selfTracker(classWithMethod)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = clsArgumentTracker(t2, classWithMethod).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
start = getTypeCall() and
|
||||
start.(CallCfgNode).getArg(0) = selfTracker(classWithMethod)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -606,26 +608,28 @@ private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod
|
||||
* from a normal method.
|
||||
*/
|
||||
Node clsArgumentTracker(Class classWithMethod) {
|
||||
clsArgumentTracker(TypeTracker::end(), classWithMethod).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackClsArgumentInput>::track(classWithMethod)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the result of calling `super` without any argument, where the
|
||||
* call happened in the method `func` (either a method or a classmethod).
|
||||
*/
|
||||
private TypeTrackingNode superCallNoArgumentTracker(TypeTracker t, Function func) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
not isStaticmethod(func) and
|
||||
exists(CallCfgNode call | result = call |
|
||||
call = getSuperCall() and
|
||||
not exists(call.getArg(_)) and
|
||||
call.getScope() = func
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = superCallNoArgumentTracker(t2, func).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
private module TrackSuperCallNoArgumentInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Function;
|
||||
|
||||
predicate start(Node start, Function func) {
|
||||
not isStaticmethod(func) and
|
||||
exists(CallCfgNode call | start = call |
|
||||
call = getSuperCall() and
|
||||
not exists(call.getArg(_)) and
|
||||
call.getScope() = func
|
||||
)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -633,25 +637,30 @@ private TypeTrackingNode superCallNoArgumentTracker(TypeTracker t, Function func
|
||||
* call happened in the method `func` (either a method or a classmethod).
|
||||
*/
|
||||
Node superCallNoArgumentTracker(Function func) {
|
||||
superCallNoArgumentTracker(TypeTracker::end(), func).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackSuperCallNoArgumentInput>::track(func)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the result of calling `super` with 2 arguments, where the
|
||||
* first is a reference to the class `cls`, and the second argument is `obj`.
|
||||
*/
|
||||
private TypeTrackingNode superCallTwoArgumentTracker(TypeTracker t, Class cls, Node obj) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
exists(CallCfgNode call | result = call |
|
||||
private module TrackSuperCallTwoArgumentInput implements CallGraphConstruction::Simple::InputSig {
|
||||
additional predicate superCall(CallCfgNode call, Class cls, Node obj) {
|
||||
call = getSuperCall() and
|
||||
call.getArg(0) = classTracker(cls) and
|
||||
call.getArg(1) = obj
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = superCallTwoArgumentTracker(t2, cls, obj).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
|
||||
class State = CallCfgNode;
|
||||
|
||||
predicate start(Node start, CallCfgNode call) {
|
||||
superCall(call, _, _) and
|
||||
start = call
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,7 +668,12 @@ private TypeTrackingNode superCallTwoArgumentTracker(TypeTracker t, Class cls, N
|
||||
* first is a reference to the class `cls`, and the second argument is `obj`.
|
||||
*/
|
||||
Node superCallTwoArgumentTracker(Class cls, Node obj) {
|
||||
superCallTwoArgumentTracker(TypeTracker::end(), cls, obj).flowsTo(result)
|
||||
exists(CallCfgNode call |
|
||||
TrackSuperCallTwoArgumentInput::superCall(call, cls, obj) and
|
||||
CallGraphConstruction::Simple::Make<TrackSuperCallTwoArgumentInput>::track(call)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -803,20 +817,30 @@ Function findFunctionAccordingToMroKnownStartingClass(Class startingClass, strin
|
||||
// =============================================================================
|
||||
// attribute trackers
|
||||
// =============================================================================
|
||||
/** Gets a reference to the attribute read `attr` */
|
||||
private TypeTrackingNode attrReadTracker(TypeTracker t, AttrRead attr) {
|
||||
t.start() and
|
||||
result = attr and
|
||||
attr.getObject() in [
|
||||
classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_),
|
||||
superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _)
|
||||
]
|
||||
or
|
||||
exists(TypeTracker t2 | result = attrReadTracker(t2, attr).track(t2, t))
|
||||
private module TrackAttrReadInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = AttrRead;
|
||||
|
||||
predicate start(Node start, AttrRead attr) {
|
||||
start = attr and
|
||||
attr.getObject() in [
|
||||
classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_),
|
||||
superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _)
|
||||
]
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to the attribute read `attr` */
|
||||
Node attrReadTracker(AttrRead attr) { attrReadTracker(TypeTracker::end(), attr).flowsTo(result) }
|
||||
Node attrReadTracker(AttrRead attr) {
|
||||
CallGraphConstruction::Simple::Make<TrackAttrReadInput>::track(attr)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// call and argument resolution
|
||||
|
||||
@@ -1135,8 +1135,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow
|
||||
);
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap);
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t);
|
||||
|
||||
bindingset[typ, contentType]
|
||||
predicate typecheckStore(Typ typ, DataFlowType contentType);
|
||||
@@ -1199,17 +1199,21 @@ module Impl<FullStateConfigSig Config> {
|
||||
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap, ApApprox apa
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t, ap, apa) and
|
||||
PrevStage::revFlow(node, state, apa) and
|
||||
filter(node, state, t, ap)
|
||||
fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
private predicate fwdFlow1(
|
||||
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap
|
||||
ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argT, argAp, t, ap, _)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and
|
||||
PrevStage::revFlow(node, state, apa) and
|
||||
filter(node, state, t0, ap, t)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeStrengthen(Typ t0, Ap ap, Typ t) {
|
||||
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
@@ -1339,6 +1343,11 @@ module Impl<FullStateConfigSig Config> {
|
||||
private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) {
|
||||
fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and
|
||||
cons = apCons(c, t1, tail)
|
||||
or
|
||||
exists(Typ t0 |
|
||||
typeStrengthen(t0, cons, t2) and
|
||||
fwdFlowConsCand(t0, cons, c, t1, tail)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1359,7 +1368,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
ParamNodeOption summaryCtx, TypOption argT, ApOption argAp
|
||||
) {
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2)
|
||||
)
|
||||
@@ -1520,14 +1529,14 @@ module Impl<FullStateConfigSig Config> {
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
) {
|
||||
revFlow0(node, state, returnCtx, returnAp, ap) and
|
||||
fwdFlow(node, state, _, _, _, _, _, ap)
|
||||
fwdFlow(node, state, _, _, _, _, _, ap, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
) {
|
||||
fwdFlow(node, state, _, _, _, _, _, ap) and
|
||||
fwdFlow(node, state, _, _, _, _, _, ap, _) and
|
||||
sinkNode(node, state) and
|
||||
(
|
||||
if hasSinkCallCtx()
|
||||
@@ -1780,13 +1789,13 @@ module Impl<FullStateConfigSig Config> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and
|
||||
fields = count(Content f0 | fwdConsCand(f0, _, _)) and
|
||||
conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap))
|
||||
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and
|
||||
@@ -1963,10 +1972,10 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
PrevStage::revFlowState(state) and
|
||||
exists(t) and
|
||||
t0 = t and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state) and
|
||||
(
|
||||
@@ -2197,8 +2206,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, LocalCc lcc
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
|
||||
LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
|
||||
exists(lcc)
|
||||
@@ -2218,10 +2227,16 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
exists(state) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
|
||||
// We can get away with not using type strengthening here, since we aren't
|
||||
// going to use the tracked types in the construction of Stage 4 access
|
||||
// paths. For Stage 4 and onwards, the tracked types must be consistent as
|
||||
// the cons candidates including types are used to construct subsequent
|
||||
// access path approximations.
|
||||
t0 = t and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
@@ -2241,6 +2256,16 @@ module Impl<FullStateConfigSig Config> {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
bindingset[node, t0]
|
||||
private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) {
|
||||
if castingNodeEx(node)
|
||||
then
|
||||
exists(DataFlowType nt | nt = node.getDataFlowType() |
|
||||
if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0)
|
||||
)
|
||||
else t = t0
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
@@ -2274,8 +2299,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, LocalCc lcc
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
|
||||
LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
|
||||
@@ -2333,11 +2358,11 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
exists(state) and
|
||||
not clear(node, ap) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
|
||||
strengthenType(node, t0, t) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
@@ -2365,7 +2390,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _,
|
||||
apf)
|
||||
apf, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2579,8 +2604,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
import LocalCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, LocalCc lcc
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
|
||||
LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
|
||||
@@ -2609,9 +2634,9 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
strengthenType(node, t0, t) and
|
||||
exists(state) and
|
||||
exists(ap)
|
||||
}
|
||||
@@ -2632,7 +2657,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
Stage5::parameterMayFlowThrough(p, _) and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _,
|
||||
TAccessPathApproxSome(apa), _, apa0)
|
||||
TAccessPathApproxSome(apa), _, apa0, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2649,7 +2674,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) {
|
||||
exists(AccessPathApprox apa | ap.getApprox() = apa |
|
||||
Stage5::parameterMayFlowThrough(p, apa) and
|
||||
Stage5::fwdFlow(p, state, _, _, _, _, t, apa) and
|
||||
Stage5::fwdFlow(p, state, _, _, Option<DataFlowType>::some(t), _, _, apa, _) and
|
||||
Stage5::revFlow(p, state, _)
|
||||
)
|
||||
}
|
||||
@@ -2820,9 +2845,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
ap = TAccessPathNil()
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
pathStep(_, node, state, cc, sc, t, ap) and
|
||||
Stage5::revFlow(node, state, ap.getApprox()) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any())
|
||||
pathStep(_, node, state, cc, sc, t, ap)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state) {
|
||||
exists(PathNodeMid sink |
|
||||
@@ -3340,13 +3363,24 @@ module Impl<FullStateConfigSig Config> {
|
||||
ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
|
||||
AccessPath ap
|
||||
) {
|
||||
exists(DataFlowType t0 |
|
||||
pathStep0(mid, node, state, cc, sc, t0, ap) and
|
||||
Stage5::revFlow(node, state, ap.getApprox()) and
|
||||
strengthenType(node, t0, t)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
private predicate pathStep0(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
|
||||
AccessPath ap
|
||||
) {
|
||||
@@ -3964,7 +3998,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
ap = TPartialNil() and
|
||||
exists(explorationLimit())
|
||||
or
|
||||
partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
|
||||
distSrc(node.getEnclosingCallable()) <= explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
@@ -3990,11 +4024,20 @@ module Impl<FullStateConfigSig Config> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathNodeMk0(
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
|
||||
) {
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
|
||||
partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathStep1(
|
||||
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t,
|
||||
PartialAccessPath ap
|
||||
) {
|
||||
partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and
|
||||
not fullBarrier(node) and
|
||||
not stateBarrier(node, state) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
@@ -4002,9 +4045,14 @@ module Impl<FullStateConfigSig Config> {
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), t)
|
||||
else any()
|
||||
strengthenType(node, t0, t)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathTypeStrengthen(
|
||||
DataFlowType t0, PartialAccessPath ap, DataFlowType t
|
||||
) {
|
||||
partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4183,7 +4231,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
pragma[nomagic]
|
||||
private predicate partialPathStep0(
|
||||
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
|
||||
) {
|
||||
@@ -4309,6 +4358,11 @@ module Impl<FullStateConfigSig Config> {
|
||||
DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2
|
||||
) {
|
||||
partialPathStoreStep(_, t1, ap1, c, _, t2, ap2)
|
||||
or
|
||||
exists(DataFlowType t0 |
|
||||
partialPathTypeStrengthen(t0, ap2, t2) and
|
||||
apConsFwd(t1, ap1, c, t0, ap2)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
|
||||
@@ -502,6 +502,8 @@ class CastNode extends Node {
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
|
||||
|
||||
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of `node`.
|
||||
*/
|
||||
|
||||
@@ -166,28 +166,21 @@ module Public {
|
||||
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
|
||||
}
|
||||
|
||||
private predicate noComponentSpecific(SummaryComponent sc) {
|
||||
not exists(getComponentSpecific(sc))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this component used for flow summaries. */
|
||||
private string getComponent(SummaryComponent sc) {
|
||||
result = getComponentSpecific(sc)
|
||||
or
|
||||
noComponentSpecific(sc) and
|
||||
(
|
||||
exists(ArgumentPosition pos |
|
||||
sc = TParameterSummaryComponent(pos) and
|
||||
result = "Parameter[" + getArgumentPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
sc = TArgumentSummaryComponent(pos) and
|
||||
result = "Argument[" + getParameterPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
|
||||
exists(ArgumentPosition pos |
|
||||
sc = TParameterSummaryComponent(pos) and
|
||||
result = "Parameter[" + getArgumentPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
sc = TArgumentSummaryComponent(pos) and
|
||||
result = "Argument[" + getParameterPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this stack used for flow summaries. */
|
||||
|
||||
@@ -224,6 +224,50 @@ private module Cached {
|
||||
|
||||
private import Cached
|
||||
|
||||
private predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
stepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
stepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
|
||||
step(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeFrom, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
stepProj(nodeFrom, summary) and
|
||||
result = t.append(summary) and
|
||||
step(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
smallstepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
smallstepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
|
||||
smallstep(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeFrom, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
smallstepProj(nodeFrom, summary) and
|
||||
result = t.append(summary) and
|
||||
smallstep(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`.
|
||||
*
|
||||
@@ -298,21 +342,50 @@ class StepSummary extends TStepSummary {
|
||||
module StepSummary {
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
* inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate is inlined, which enables better join-orders when
|
||||
* the call graph construction and type tracking are mutually recursive.
|
||||
* In such cases, non-linear recursion involving `step` will be limited
|
||||
* to non-linear recursion for the parts of `step` that involve the
|
||||
* call graph.
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate stepCall = Cached::stepCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* intra-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate stepNoCall = Cached::stepNoCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
stepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
stepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate smallstepNoCall = Cached::smallstepNoCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* intra-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate smallstepCall = Cached::smallstepCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
@@ -320,7 +393,6 @@ module StepSummary {
|
||||
* Unlike `StepSummary::step`, this predicate does not compress
|
||||
* type-preserving steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
smallstepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
@@ -431,10 +503,7 @@ class TypeTracker extends TTypeTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::step(nodeFrom, pragma[only_bind_out](nodeTo), pragma[only_bind_into](summary)) and
|
||||
result = this.append(pragma[only_bind_into](summary))
|
||||
)
|
||||
result = stepInlineLate(this, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,10 +532,7 @@ class TypeTracker extends TTypeTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeTracker smallstep(Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
|
||||
result = this.append(summary)
|
||||
)
|
||||
result = smallstepInlineLate(this, nodeFrom, nodeTo)
|
||||
or
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) and
|
||||
result = this
|
||||
@@ -481,6 +547,39 @@ module TypeTracker {
|
||||
TypeTracker end() { result.end() }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
step(_, nodeTo, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeTo, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeBackTracker backStepInlineLate(
|
||||
TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
|
||||
) {
|
||||
exists(StepSummary summary |
|
||||
backStepProj(nodeTo, summary) and
|
||||
result = t.prepend(summary) and
|
||||
step(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
smallstep(_, nodeTo, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeTo, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
backSmallstepProj(nodeTo, summary) and
|
||||
result = t.prepend(summary) and
|
||||
smallstep(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
|
||||
*
|
||||
@@ -564,10 +663,7 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::step(pragma[only_bind_out](nodeFrom), nodeTo, pragma[only_bind_into](summary)) and
|
||||
this = result.prepend(pragma[only_bind_into](summary))
|
||||
)
|
||||
this = backStepInlineLate(result, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,10 +692,7 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
|
||||
this = result.prepend(summary)
|
||||
)
|
||||
this = backSmallstepInlineLate(result, nodeFrom, nodeTo)
|
||||
or
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) and
|
||||
this = result
|
||||
@@ -635,3 +728,169 @@ module TypeBackTracker {
|
||||
*/
|
||||
TypeBackTracker end() { result.end() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides logic for constructing a call graph in mutual recursion with type tracking.
|
||||
*
|
||||
* When type tracking is used to construct a call graph, we cannot use the join-order
|
||||
* from `stepInlineLate`, because `step` becomes a recursive call, which means that we
|
||||
* will have a conjunct with 3 recursive calls: the call to `step`, the call to `stepProj`,
|
||||
* and the recursive type tracking call itself. The solution is to split the three-way
|
||||
* non-linear recursion into two non-linear predicates: one that first joins with the
|
||||
* projected `stepCall` relation, followed by a predicate that joins with the full
|
||||
* `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the
|
||||
* same way as in `stepInlineLate`).
|
||||
*/
|
||||
module CallGraphConstruction {
|
||||
/** The input to call graph construction. */
|
||||
signature module InputSig {
|
||||
/** A state to track during type tracking. */
|
||||
class State;
|
||||
|
||||
/** Holds if type tracking should start at `start` in state `state`. */
|
||||
predicate start(Node start, State state);
|
||||
|
||||
/**
|
||||
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
|
||||
* which _does not_ depend on the call graph.
|
||||
*
|
||||
* Implementing this predicate using `StepSummary::[small]stepNoCall` yields
|
||||
* standard type tracking.
|
||||
*/
|
||||
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary);
|
||||
|
||||
/**
|
||||
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
|
||||
* which _does_ depend on the call graph.
|
||||
*
|
||||
* Implementing this predicate using `StepSummary::[small]stepCall` yields
|
||||
* standard type tracking.
|
||||
*/
|
||||
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary);
|
||||
|
||||
/** A projection of an element from the state space. */
|
||||
class StateProj;
|
||||
|
||||
/** Gets the projection of `state`. */
|
||||
StateProj stateProj(State state);
|
||||
|
||||
/** Holds if type tracking should stop at `n` when we are tracking projected state `stateProj`. */
|
||||
predicate filter(Node n, StateProj stateProj);
|
||||
}
|
||||
|
||||
/** Provides the `track` predicate for use in call graph construction. */
|
||||
module Make<InputSig Input> {
|
||||
pragma[nomagic]
|
||||
private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
|
||||
Input::stepNoCall(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
|
||||
Input::stepCall(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeFrom, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeTracker stepNoCallInlineLate(
|
||||
TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
|
||||
) {
|
||||
exists(StepSummary summary |
|
||||
stepNoCallProj(nodeFrom, summary) and
|
||||
result = t.append(summary) and
|
||||
Input::stepNoCall(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[state]
|
||||
pragma[inline_late]
|
||||
private Input::StateProj stateProjInlineLate(Input::State state) {
|
||||
result = Input::stateProj(state)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Node track(Input::State state, TypeTracker t) {
|
||||
t.start() and Input::start(result, state)
|
||||
or
|
||||
exists(Input::StateProj stateProj |
|
||||
stateProj = stateProjInlineLate(state) and
|
||||
not Input::filter(result, stateProj)
|
||||
|
|
||||
exists(TypeTracker t2 | t = stepNoCallInlineLate(t2, track(state, t2), result))
|
||||
or
|
||||
exists(StepSummary summary |
|
||||
// non-linear recursion
|
||||
Input::stepCall(trackCall(state, t, summary), result, summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[t, summary]
|
||||
pragma[inline_late]
|
||||
private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
|
||||
result = t.append(summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Node trackCall(Input::State state, TypeTracker t, StepSummary summary) {
|
||||
exists(TypeTracker t2 |
|
||||
// non-linear recursion
|
||||
result = track(state, t2) and
|
||||
stepCallProj(result, summary) and
|
||||
t = appendInlineLate(t2, summary)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a node that can be reached from _some_ start node in state `state`. */
|
||||
pragma[nomagic]
|
||||
Node track(Input::State state) { result = track(state, TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/** A simple version of `CallGraphConstruction` that uses standard type tracking. */
|
||||
module Simple {
|
||||
/** The input to call graph construction. */
|
||||
signature module InputSig {
|
||||
/** A state to track during type tracking. */
|
||||
class State;
|
||||
|
||||
/** Holds if type tracking should start at `start` in state `state`. */
|
||||
predicate start(Node start, State state);
|
||||
|
||||
/** Holds if type tracking should stop at `n`. */
|
||||
predicate filter(Node n);
|
||||
}
|
||||
|
||||
/** Provides the `track` predicate for use in call graph construction. */
|
||||
module Make<InputSig Input> {
|
||||
private module I implements CallGraphConstruction::InputSig {
|
||||
private import codeql.util.Unit
|
||||
|
||||
class State = Input::State;
|
||||
|
||||
predicate start(Node start, State state) { Input::start(start, state) }
|
||||
|
||||
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
StepSummary::stepNoCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
StepSummary::stepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
class StateProj = Unit;
|
||||
|
||||
Unit stateProj(State state) { exists(state) and exists(result) }
|
||||
|
||||
predicate filter(Node n, Unit u) {
|
||||
Input::filter(n) and
|
||||
exists(u)
|
||||
}
|
||||
}
|
||||
|
||||
import CallGraphConstruction::Make<I>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.7.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -34,4 +34,4 @@ where
|
||||
)
|
||||
)
|
||||
select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.",
|
||||
left.getLocation(), "left", right.getLocation(), "right"
|
||||
left, "left", right, "right"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @name Deserializing untrusted input
|
||||
* @name Deserialization of user-controlled data
|
||||
* @description Deserializing user-controlled data may allow attackers to execute arbitrary code.
|
||||
* @kind path-problem
|
||||
* @id py/unsafe-deserialization
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
Consider this regular expression:
|
||||
</p>
|
||||
<sample language="python">
|
||||
^_(__|.)+_$
|
||||
</sample>
|
||||
^_(__|.)+_$</sample>
|
||||
<p>
|
||||
Its sub-expression <code>"(__|.)+?"</code> can match the string <code>"__"</code> either by the
|
||||
first alternative <code>"__"</code> to the left of the <code>"|"</code> operator, or by two
|
||||
@@ -25,8 +24,7 @@
|
||||
the two branches of the alternative inside the repetition:
|
||||
</p>
|
||||
<sample language="python">
|
||||
^_(__|[^_])+_$
|
||||
</sample>
|
||||
^_(__|[^_])+_$</sample>
|
||||
</example>
|
||||
|
||||
<include src="ReDoSReferences.inc.qhelp"/>
|
||||
|
||||
@@ -36,5 +36,5 @@ where
|
||||
ex = Value::named("sys.exc_info") and
|
||||
ex.getACall().getScope() = f
|
||||
)
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(),
|
||||
e.toString(), f.getLocation(), f.getName()
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.", e, e.toString(), f,
|
||||
f.getName()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* The display name (`@name`) of the `py/unsafe-deserialization` query has been updated in favor of consistency with other languages.
|
||||
3
python/ql/src/change-notes/released/0.7.2.md
Normal file
3
python/ql/src/change-notes/released/0.7.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.7.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.7.1
|
||||
lastReleaseVersion: 0.7.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.7.2-dev
|
||||
version: 0.7.3-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| TruncatedDivision_test.py:65:7:65:11 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:65:7:65:7 | TruncatedDivision_test.py:65 | left | TruncatedDivision_test.py:65:11:65:11 | TruncatedDivision_test.py:65 | right |
|
||||
| TruncatedDivision_test.py:72:7:72:35 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:25:12:25:12 | TruncatedDivision_test.py:25 | left | TruncatedDivision_test.py:28:12:28:12 | TruncatedDivision_test.py:28 | right |
|
||||
| TruncatedDivision_test.py:65:7:65:11 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:65:7:65:7 | ControlFlowNode for IntegerLiteral | left | TruncatedDivision_test.py:65:11:65:11 | ControlFlowNode for IntegerLiteral | right |
|
||||
| TruncatedDivision_test.py:72:7:72:35 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:25:12:25:12 | ControlFlowNode for IntegerLiteral | left | TruncatedDivision_test.py:28:12:28:12 | ControlFlowNode for IntegerLiteral | right |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| nonsense.py:0:1:0:1 | Syntax Error | Syntax Error (in Python 2). |
|
||||
| nonsense.py:1:1:1:1 | Syntax Error | Syntax Error (in Python 2). |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| nonsense.py:0:1:0:1 | Syntax Error | Syntax Error (in Python 3). |
|
||||
| nonsense.py:1:2:1:2 | Syntax Error | Syntax Error (in Python 3). |
|
||||
|
||||
@@ -317,7 +317,7 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
|
||||
location = response.getLocation() and
|
||||
element = response.toString() and
|
||||
// Ensure that an expectation value such as "mimetype=text/html; charset=utf-8" is parsed as a
|
||||
// single expectation with tag mimetype, and not as two expecations with tags mimetype and
|
||||
// single expectation with tag mimetype, and not as two expectations with tags mimetype and
|
||||
// charset.
|
||||
(
|
||||
if exists(response.getMimetype().indexOf(" "))
|
||||
|
||||
@@ -1 +1 @@
|
||||
| statements_test.py:187:5:187:9 | Delete | Unnecessary deletion of local variable $@ in function $@. | statements_test.py:187:9:187:9 | statements_test.py:187 | x | statements_test.py:185:1:185:31 | statements_test.py:185 | error_unnecessary_delete |
|
||||
| statements_test.py:187:5:187:9 | Delete | Unnecessary deletion of local variable $@ in function $@. | statements_test.py:187:9:187:9 | x | x | statements_test.py:185:1:185:31 | Function error_unnecessary_delete | error_unnecessary_delete |
|
||||
|
||||
Reference in New Issue
Block a user