From cf0e38b22cb386e8061a9bda4f5efb2ff63f1866 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Wed, 5 Feb 2020 13:49:54 +0000 Subject: [PATCH] Move virtual dispatch resolution from `CallExpr` to `CallNode` and generalise it very slightly. --- ql/src/semmle/go/Expr.qll | 14 ++--------- .../go/dataflow/internal/DataFlowUtil.qll | 25 ++++++++++++++++--- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/ql/src/semmle/go/Expr.qll b/ql/src/semmle/go/Expr.qll index b86c73d3697..5b080e639c8 100644 --- a/ql/src/semmle/go/Expr.qll +++ b/ql/src/semmle/go/Expr.qll @@ -476,18 +476,8 @@ class CallExpr extends CallOrConversionExpr { * interface type. */ FuncDef getACallee() { - result = getTarget().(DeclaredFunction).getFuncDecl() - or - exists(SelectorExpr sel, InterfaceType declaredRecv, Type actualRecv | - sel = getCalleeExpr().stripParens() and - declaredRecv = sel.getBase().getType().getUnderlyingType() and - actualRecv.implements(declaredRecv) - | - result = actualRecv - .(PointerType) - .getBaseType() - .(NamedType) - .getMethodDecl(sel.getSelector().getName()) + exists(DataFlow::CallNode call | call.asExpr() = this | + result = call.getACallee() ) } diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll index f12c05ecc43..ce8a67563ef 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll @@ -264,8 +264,27 @@ class CallNode extends ExprNode { /** Gets the declared target of this call */ Function getTarget() { result = expr.getTarget() } - /** Get the definition of a possible target of this call. See `CallExpr.getACallee`. */ - FuncDef getACallee() { result = expr.getACallee() } + /** + * Gets the definition of a possible target of this call. + * + * For non-virtual calls, there is at most one possible call target (but there may be none if the + * target has no declaration). + * + * For virtual calls, we look up possible targets in all types that implement the receiver + * interface type. + */ + FuncDef getACallee() { + result = getTarget().(DeclaredFunction).getFuncDecl() + or + exists(DataFlow::Node calleeSource | calleeSource.getASuccessor*() = getCalleeNode() | + exists(Method m, InterfaceType declaredRecv, Type actualRecv | + calleeSource = m.getARead() and + declaredRecv = m.getReceiverType().(NamedType).getBaseType() and + actualRecv.implements(declaredRecv) and + result = actualRecv.getMethod(m.getName()).(DeclaredFunction).getFuncDecl() + ) + ) + } /** Gets the name of the function or method being called, if it can be determined. */ string getCalleeName() { result = expr.getTarget().getName() or result = expr.getCalleeName() } @@ -328,7 +347,7 @@ class MethodCallNode extends CallNode { override Method getTarget() { result = expr.getTarget() } - override MethodDecl getACallee() { result = expr.getACallee() } + override MethodDecl getACallee() { result = super.getACallee() } } /** A representation of a receiver initialization. */