Add dataflow callback to filter out receiver argument flow to Golang interface dispatch candidates.

Other langauges stub the callback.
This commit is contained in:
Chris Smowton
2023-04-03 17:40:08 +01:00
parent 7ffe863ba6
commit 4d8ca3d759
17 changed files with 108 additions and 16 deletions

View File

@@ -79,3 +79,11 @@ class ArgumentPosition extends int {
/** Holds if arguments at position `apos` match parameters at position `ppos`. */ /** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline] pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos } predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, ParameterNode p, ArgumentNode arg) { any() }

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -271,3 +271,11 @@ Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
/** Holds if arguments at position `apos` match parameters at position `ppos`. */ /** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline] pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos } predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, ParameterNode p, ArgumentNode arg) { any() }

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -555,3 +555,11 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
apos.isImplicitCapturedArgumentPosition(v) apos.isImplicitCapturedArgumentPosition(v)
) )
} }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, ParameterNode p, ArgumentNode arg) { any() }

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -133,3 +133,25 @@ class ArgumentPosition extends int {
/** Holds if arguments at position `apos` match parameters at position `ppos`. */ /** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline] pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos } predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
private predicate isInterfaceMethod(Method c) {
c.getReceiverBaseType().getUnderlyingType() instanceof InterfaceType
}
/**
* Holds if `call` is passing `arg` to param `p` in any circumstance except passing
* a receiver parameter to a concrete method.
*/
pragma[inline]
predicate viableParamArgSpecific(
DataFlowCall call, DataFlow::ParameterNode p, DataFlow::ArgumentNode arg
) {
// Interface methods calls may be passed strictly to that exact method's model receiver:
arg.getPosition() != -1
or
exists(Function callTarget | callTarget = call.getNode().(DataFlow::CallNode).getTarget() |
not isInterfaceMethod(callTarget)
or
callTarget = p.getCallable().asSummarizedCallable().asFunction()
)
}

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -570,6 +570,12 @@ module Public {
abstract class ParameterNode extends DataFlow::Node { abstract class ParameterNode extends DataFlow::Node {
/** Holds if this node initializes the `i`th parameter of `c`. */ /** Holds if this node initializes the `i`th parameter of `c`. */
abstract predicate isParameterOf(DataFlowCallable c, int i); abstract predicate isParameterOf(DataFlowCallable c, int i);
/** Gets the callable that this parameter belongs to. */
DataFlowCallable getCallable() { this.isParameterOf(result, _) }
/** Gets this parameter's position. */
int getPosition() { this.isParameterOf(_, result) }
} }
/** /**
@@ -722,20 +728,18 @@ module Public {
*/ */
predicate argumentOf(CallExpr call, int pos) { predicate argumentOf(CallExpr call, int pos) {
call = c.asExpr() and call = c.asExpr() and
pos = i and pos = i
(
i != -1
or
exists(c.(MethodCallNode).getTarget().getBody())
or
hasExternalSpecification(c.(DataFlow::MethodCallNode).getTarget())
)
} }
/** /**
* Gets the `CallNode` this is an argument to. * Gets the `CallNode` this is an argument to.
*/ */
CallNode getCall() { result = c } CallNode getCall() { result = c }
/**
* Gets this argument's position.
*/
int getPosition() { result = i }
} }
/** /**

View File

@@ -171,6 +171,14 @@ private module DispatchImpl {
/** Holds if arguments at position `apos` match parameters at position `ppos`. */ /** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline] pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos } predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, ParameterNode p, ArgumentNode arg) { any() }
} }
import DispatchImpl import DispatchImpl

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -1614,3 +1614,11 @@ private module OutNodes {
* `kind`. * `kind`.
*/ */
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, ParameterNode p, ArgumentNode arg) { any() }

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -1380,3 +1380,13 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
or or
apos.isAnyNamed() and ppos.isKeyword(_) apos.isAnyNamed() and ppos.isKeyword(_)
} }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, DataFlow::Node param, ArgumentNode arg) {
any()
}

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }

View File

@@ -333,3 +333,11 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
or or
ppos.(PositionalParameterPosition).getIndex() = apos.(PositionalArgumentPosition).getIndex() ppos.(PositionalParameterPosition).getIndex() = apos.(PositionalArgumentPosition).getIndex()
} }
/**
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
*
* This is a filter on top of the language-neutral argument/parameter matching implemented by `viableParamArg`.
*/
pragma[inline]
predicate viableParamArgSpecific(DataFlowCall call, ParameterNode p, ArgumentNode arg) { any() }

View File

@@ -425,7 +425,8 @@ private module Cached {
exists(ParameterPosition ppos | exists(ParameterPosition ppos |
viableParam(call, ppos, p) and viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and argumentPositionMatch(call, arg, ppos) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
viableParamArgSpecific(call, p, arg)
) )
} }