Skip to content

Commit

Permalink
Data flow: Cache TNodeEx
Browse files Browse the repository at this point in the history
  • Loading branch information
hvitved committed Aug 27, 2024
1 parent 5bf487b commit d7bb558
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ module ProductFlow {
Flow1::PathGraph::edges(pred1, succ1, _, _) and
exists(ReturnKindExt returnKind |
succ1.getNode() = returnKind.getAnOutNode(call) and
paramReturnNode(_, pred1.asParameterReturnNode(), _, returnKind)
returnKind = getParamReturnPosition(_, pred1.asParameterReturnNode()).getKind()
)
}

Expand Down Expand Up @@ -574,7 +574,7 @@ module ProductFlow {
Flow2::PathGraph::edges(pred2, succ2, _, _) and
exists(ReturnKindExt returnKind |
succ2.getNode() = returnKind.getAnOutNode(call) and
paramReturnNode(_, pred2.asParameterReturnNode(), _, returnKind)
returnKind = getParamReturnPosition(_, pred2.asParameterReturnNode()).getKind()
)
}

Expand Down
105 changes: 9 additions & 96 deletions shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -153,94 +153,19 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
module Impl<FullStateConfigSig Config> {
private class FlowState = Config::FlowState;

private newtype TNodeEx =
TNodeNormal(Node n) or
TNodeImplicitRead(Node n) { Config::allowImplicitRead(n, _) } or
TParamReturnNode(ParameterNode p, SndLevelScopeOption scope) {
paramReturnNode(_, p, scope, _)
}

private class NodeEx extends TNodeEx {
string toString() {
result = this.asNode().toString()
private class NodeEx extends NodeExImpl {
NodeEx() {
Config::allowImplicitRead(any(Node n | this.isImplicitReadNode(n)), _)
or
exists(Node n | this.isImplicitReadNode(n) | result = n.toString() + " [Ext]")
or
result = this.asParamReturnNode().toString() + " [Return]"
}

Node asNode() { this = TNodeNormal(result) }

/** Gets the corresponding Node if this is a normal node or its post-implicit read node. */
Node asNodeOrImplicitRead() { this = TNodeNormal(result) or this = TNodeImplicitRead(result) }

predicate isImplicitReadNode(Node n) { this = TNodeImplicitRead(n) }

ParameterNode asParamReturnNode() { this = TParamReturnNode(result, _) }

Node projectToNode() {
this = TNodeNormal(result) or
this = TNodeImplicitRead(result) or
this = TParamReturnNode(result, _)
}

pragma[nomagic]
private DataFlowCallable getEnclosingCallable0() {
nodeEnclosingCallable(this.projectToNode(), result)
}

pragma[inline]
DataFlowCallable getEnclosingCallable() {
pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
}

pragma[nomagic]
private DataFlowType getDataFlowType0() {
nodeDataFlowType(this.asNode(), result)
or
nodeDataFlowType(this.asParamReturnNode(), result)
}

pragma[inline]
DataFlowType getDataFlowType() {
pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
not this.isImplicitReadNode(_)
}

Location getLocation() { result = this.projectToNode().getLocation() }
}

private class ArgNodeEx extends NodeEx {
ArgNodeEx() { this.asNode() instanceof ArgNode }

DataFlowCall getCall() { this.asNode().(ArgNode).argumentOf(result, _) }
}

private class ParamNodeEx extends NodeEx {
ParamNodeEx() { this.asNode() instanceof ParamNode }

predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
this.asNode().(ParamNode).isParameterOf(c, pos)
}

ParameterPosition getPosition() { this.isParameterOf(_, result) }
}

/**
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a synthesized node for flow out via a parameter.
*/
private class RetNodeEx extends NodeEx {
private ReturnPosition pos;
private class ArgNodeEx extends NodeEx, ArgNodeExImpl { }

RetNodeEx() {
pos = getValueReturnPosition(this.asNode()) or
pos = getParamReturnPosition(_, this.asParamReturnNode())
}
private class ParamNodeEx extends NodeEx, ParamNodeExImpl { }

ReturnPosition getReturnPosition() { result = pos }

ReturnKindExt getKind() { result = pos.getKind() }
}
private class RetNodeEx extends NodeEx, RetNodeExImpl { }

/** If `node` corresponds to a sink, gets the normal node for that sink. */
pragma[nomagic]
Expand Down Expand Up @@ -363,20 +288,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStepEx(NodeEx node1, NodeEx node2, string model) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2), model) and
stepFilter(node1, node2)
)
or
exists(Node n1, Node n2, SndLevelScopeOption scope |
node1.asNode() = n1 and
node2 = TParamReturnNode(n2, scope) and
paramReturnNode(pragma[only_bind_into](n1), pragma[only_bind_into](n2),
pragma[only_bind_into](scope), _) and
model = ""
)
localFlowStepExImpl(node1, node2, model) and
stepFilter(node1, node2)
}

/**
Expand Down
184 changes: 149 additions & 35 deletions shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,85 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {

class SndLevelScopeOption = SndLevelScopeOption::Option;

final class NodeExImpl extends TNodeEx {
string toString() {
result = this.asNode().toString()
or
exists(Node n | this.isImplicitReadNode(n) | result = n.toString() + " [Ext]")
or
result = this.asParamReturnNode().toString() + " [Return]"
}

Node asNode() { this = TNodeNormal(result) }

/** Gets the corresponding Node if this is a normal node or its post-implicit read node. */
Node asNodeOrImplicitRead() { this = TNodeNormal(result) or this = TNodeImplicitRead(result) }

predicate isImplicitReadNode(Node n) { this = TNodeImplicitRead(n) }

ParameterNode asParamReturnNode() { this = TParamReturnNode(result, _) }

Node projectToNode() {
this = TNodeNormal(result) or
this = TNodeImplicitRead(result) or
this = TParamReturnNode(result, _)
}

pragma[nomagic]
private DataFlowCallable getEnclosingCallable0() {
nodeEnclosingCallable(this.projectToNode(), result)
}

pragma[inline]
DataFlowCallable getEnclosingCallable() {
pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
}

pragma[nomagic]
private DataFlowType getDataFlowType0() {
nodeDataFlowType(this.asNode(), result)
or
nodeDataFlowType(this.asParamReturnNode(), result)
}

pragma[inline]
DataFlowType getDataFlowType() {
pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
}

Location getLocation() { result = this.projectToNode().getLocation() }
}

final class ArgNodeExImpl extends NodeExImpl {
ArgNodeExImpl() { this.asNode() instanceof ArgNode }

DataFlowCall getCall() { this.asNode().(ArgNode).argumentOf(result, _) }
}

final class ParamNodeExImpl extends NodeExImpl {
ParamNodeExImpl() { this.asNode() instanceof ParamNode }

predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
this.asNode().(ParamNode).isParameterOf(c, pos)
}

ParameterPosition getPosition() { this.isParameterOf(_, result) }
}

/**
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a synthesized node for flow out via a parameter.
*/
final class RetNodeExImpl extends NodeExImpl {
private ReturnPosition pos;

RetNodeExImpl() { pos = getReturnPositionEx(this) }

ReturnPosition getReturnPosition() { result = pos }

ReturnKindExt getKind() { result = pos.getKind() }
}

cached
private module Cached {
/**
Expand Down Expand Up @@ -927,11 +1006,8 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
)
}

cached
predicate valueReturnNode(ReturnNode n, ReturnKindExt k) { k = TValueReturn(n.getKind()) }

cached
predicate paramReturnNode(
pragma[nomagic]
private predicate paramReturnNode(
PostUpdateNode n, ParamNode p, SndLevelScopeOption scope, ReturnKindExt k
) {
exists(ParameterPosition pos |
Expand Down Expand Up @@ -1541,6 +1617,20 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {

class UnreachableSetOption = UnreachableSetOption::Option;

pragma[nomagic]
private predicate hasValueReturnKindIn(ReturnNode ret, ReturnKindExt kind, DataFlowCallable c) {
c = getNodeEnclosingCallable(ret) and
kind = TValueReturn(ret.getKind())
}

pragma[nomagic]
private predicate hasParamReturnKindIn(
PostUpdateNode n, ParamNode p, ReturnKindExt kind, DataFlowCallable c
) {
c = getNodeEnclosingCallable(n) and
paramReturnNode(n, p, _, kind)
}

cached
newtype TReturnPosition =
TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) {
Expand All @@ -1549,6 +1639,22 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
hasParamReturnKindIn(_, _, kind, c)
}

cached
ReturnPosition getValueReturnPosition(ReturnNode ret) {
exists(ReturnKindExt kind, DataFlowCallable c |
hasValueReturnKindIn(ret, kind, c) and
result = TReturnPosition0(c, kind)
)
}

cached
ReturnPosition getParamReturnPosition(PostUpdateNode n, ParamNode p) {
exists(ReturnKindExt kind, DataFlowCallable c |
hasParamReturnKindIn(n, p, kind, c) and
result = TReturnPosition0(c, kind)
)
}

cached
newtype TLocalFlowCallContext =
TAnyLocalCall() or
Expand Down Expand Up @@ -1599,6 +1705,44 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
newtype TApproxAccessPathFrontOption =
TApproxAccessPathFrontNone() or
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)

cached
newtype TNodeEx =
TNodeNormal(Node n) or
TNodeImplicitRead(Node n) or // will be restricted to nodes with actual implicit reads in `DataFlowImpl.qll`
TParamReturnNode(ParameterNode p, SndLevelScopeOption scope) {
paramReturnNode(_, p, scope, _)
}

/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
cached
predicate localFlowStepExImpl(NodeExImpl node1, NodeExImpl node2, string model) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2), model)
)
or
exists(Node n1, Node n2, SndLevelScopeOption scope |
node1.asNode() = n1 and
node2 = TParamReturnNode(n2, scope) and
paramReturnNode(pragma[only_bind_into](n1), pragma[only_bind_into](n2),
pragma[only_bind_into](scope), _) and
model = ""
)
}

cached
ReturnPosition getReturnPositionEx(NodeExImpl ret) {
result = getValueReturnPosition(ret.asNode())
or
exists(ParamNode p |
ret = TParamReturnNode(p, _) and
result = getParamReturnPosition(_, p)
)
}
}

bindingset[call, tgt]
Expand Down Expand Up @@ -2182,36 +2326,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result))
}

pragma[nomagic]
private predicate hasValueReturnKindIn(ReturnNode ret, ReturnKindExt kind, DataFlowCallable c) {
c = getNodeEnclosingCallable(ret) and
valueReturnNode(ret, kind)
}

pragma[nomagic]
private predicate hasParamReturnKindIn(
PostUpdateNode n, ParamNode p, ReturnKindExt kind, DataFlowCallable c
) {
c = getNodeEnclosingCallable(n) and
paramReturnNode(n, p, _, kind)
}

pragma[nomagic]
ReturnPosition getValueReturnPosition(ReturnNode ret) {
exists(ReturnKindExt kind, DataFlowCallable c |
hasValueReturnKindIn(ret, kind, c) and
result = TReturnPosition0(c, kind)
)
}

pragma[nomagic]
ReturnPosition getParamReturnPosition(PostUpdateNode n, ParamNode p) {
exists(ReturnKindExt kind, DataFlowCallable c |
hasParamReturnKindIn(n, p, kind, c) and
result = TReturnPosition0(c, kind)
)
}

/** An optional Boolean value. */
class BooleanOption extends TBooleanOption {
string toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,7 @@ module Make<
exists(ReturnNode ret, ValueReturnKind kind |
c = "ReturnValue" and
ret = node.asNode() and
valueReturnNode(ret, kind) and
kind.getKind() = ret.getKind() and
kind.getKind() = getStandardReturnValueKind() and
mid.asCallable() = getNodeEnclosingCallable(ret)
)
Expand Down

0 comments on commit d7bb558

Please sign in to comment.