diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll index 2c7e3356f3c2..af1df52113dd 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll @@ -51,6 +51,12 @@ private predicate isConcreteInterfaceCall(DataFlow::Node call, DataFlow::Node re isInterfaceCallReceiver(call, recv, _, m) and isConcreteValue(recv) } +private Function getRealOrSummarizedFunction(DataFlowCallable c) { + result = c.asCallable().asFunction() + or + result = c.asSummarizedCallable().asFunction() +} + /** * Gets a function that might be called by `call`, where the receiver of `call` has interface type, * but its concrete types can be determined by local reasoning. @@ -59,7 +65,7 @@ private DataFlowCallable getConcreteTarget(DataFlow::CallNode call) { exists(string m | isConcreteInterfaceCall(call, _, m) | exists(Type concreteReceiverType | concreteReceiverType = getConcreteType(getInterfaceCallReceiverSource(call)) and - result.asFunction() = concreteReceiverType.getMethod(m) + getRealOrSummarizedFunction(result) = concreteReceiverType.getMethod(m) ) ) } @@ -78,7 +84,7 @@ private predicate isInterfaceMethodCall(DataFlow::CallNode call) { private DataFlowCallable getRestrictedInterfaceTarget(DataFlow::CallNode call) { exists(InterfaceType tp, Type recvtp, string m | isInterfaceCallReceiver(call, _, tp, m) and - result.asFunction() = recvtp.getMethod(m) and + getRealOrSummarizedFunction(result) = recvtp.getMethod(m) and recvtp.implements(tp) ) } @@ -93,7 +99,8 @@ DataFlowCallable viableCallable(CallExpr ma) { else if isInterfaceMethodCall(call) then result = getRestrictedInterfaceTarget(call) - else result.asCallable() = call.getACalleeIncludingExternals() + else + [result.asCallable(), result.asSummarizedCallable()] = call.getACalleeIncludingExternals() ) } diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 715330f87d9f..7e92b3eb1fe2 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -10,14 +10,8 @@ private newtype TNode = MkInstructionNode(IR::Instruction insn) or MkSsaNode(SsaDefinition ssa) or MkGlobalFunctionNode(Function f) or - MkSummarizedParameterNode(DataFlowCallable c, int i) { - not exists(c.getFuncDef()) and - c.asCallable() instanceof SummarizedCallable and - ( - i in [0 .. c.getType().getNumParameter() - 1] - or - c.asFunction() instanceof Method and i = -1 - ) + MkSummarizedParameterNode(SummarizedCallable c, int i) { + FlowSummaryImpl::Private::summaryParameterNodeRange(c, i) } or MkSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { FlowSummaryImpl::Private::summaryNodeRange(c, state) @@ -31,12 +25,18 @@ module Private { DataFlowCallable nodeGetEnclosingCallable(Node n) { result.asCallable() = n.getEnclosingCallable() or - not exists(n.getEnclosingCallable()) and result.asFileScope() = n.getFile() + (n = MkInstructionNode(_) or n = MkSsaNode(_) or n = MkGlobalFunctionNode(_)) and + not exists(n.getEnclosingCallable()) and + result.asFileScope() = n.getFile() + or + n = MkSummarizedParameterNode(result.asSummarizedCallable(), _) + or + n = MkSummaryInternalNode(result.asSummarizedCallable(), _) } /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { - p.isParameterOf(c.asCallable(), pos) + p.isParameterOf(c, pos) } /** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */ @@ -116,15 +116,7 @@ module Public { ControlFlow::Root getRoot() { none() } // overridden in subclasses /** INTERNAL: Use `getRoot()` instead. */ - Callable getEnclosingCallable() { - result.getFuncDef() = this.getRoot() - or - exists(DataFlowCallable dfc | result = dfc.asCallable() | - this = MkSummarizedParameterNode(dfc, _) - or - this = MkSummaryInternalNode(dfc.asCallable(), _) - ) - } + Callable getEnclosingCallable() { result.getFuncDef() = this.getRoot() } /** Gets the type of this node. */ Type getType() { none() } // overridden in subclasses @@ -578,7 +570,7 @@ module Public { /** A representation of a parameter initialization. */ abstract class ParameterNode extends DataFlow::Node { /** Holds if this node initializes the `i`th parameter of `c`. */ - abstract predicate isParameterOf(Callable c, int i); + abstract predicate isParameterOf(DataFlowCallable c, int i); } /** @@ -586,12 +578,10 @@ module Public { * already have a parameter nodes. */ class SummarizedParameterNode extends ParameterNode, MkSummarizedParameterNode { - Callable c; + SummarizedCallable c; int i; - SummarizedParameterNode() { - this = MkSummarizedParameterNode(any(DataFlowCallable dfc | c = dfc.asCallable()), i) - } + SummarizedParameterNode() { this = MkSummarizedParameterNode(c, i) } // There are no AST representations of summarized parameter nodes override ControlFlow::Root getRoot() { none() } @@ -604,7 +594,9 @@ module Public { i = -1 and result = c.asFunction().(Method).getReceiverType() } - override predicate isParameterOf(Callable call, int idx) { c = call and i = idx } + override predicate isParameterOf(DataFlowCallable call, int idx) { + c = call.asSummarizedCallable() and i = idx + } override string toString() { result = "parameter " + i + " of " + c.toString() } @@ -623,7 +615,9 @@ module Public { /** Gets the parameter this node initializes. */ override Parameter asParameter() { result = parm } - override predicate isParameterOf(Callable c, int i) { parm.isParameterOf(c.getFuncDef(), i) } + override predicate isParameterOf(DataFlowCallable c, int i) { + parm.isParameterOf(c.asCallable().getFuncDef(), i) + } } /** A representation of a receiver initialization. */ diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll index d57adbe5d693..566951bf75be 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -3,6 +3,7 @@ private import DataFlowUtil private import DataFlowImplCommon private import ContainerFlow private import FlowSummaryImpl as FlowSummaryImpl +private import semmle.go.dataflow.FlowSummary as FlowSummary import codeql.util.Unit import DataFlowNodes::Private @@ -237,31 +238,31 @@ class DataFlowLocation = Location; private newtype TDataFlowCallable = TCallable(Callable c) or - TFileScope(File f) + TFileScope(File f) or + TSummarizedCallable(FlowSummary::SummarizedCallable c) class DataFlowCallable extends TDataFlowCallable { Callable asCallable() { this = TCallable(result) } File asFileScope() { this = TFileScope(result) } - FuncDef getFuncDef() { result = this.asCallable().getFuncDef() } + FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) } - Function asFunction() { result = this.asCallable().asFunction() } - - FuncLit asFuncLit() { result = this.asCallable().asFuncLit() } - - SignatureType getType() { result = this.asCallable().getType() } + SignatureType getType() { result = [this.asCallable(), this.asSummarizedCallable()].getType() } string toString() { result = this.asCallable().toString() or - result = "File scope: " + this.asFileScope().toString() + result = "File scope: " + this.asFileScope().toString() or + result = "Summary: " + this.asSummarizedCallable().toString() } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { this.asCallable().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or - this.asFileScope().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.asFileScope().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or + this.asSummarizedCallable() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } @@ -281,6 +282,7 @@ class DataFlowCall extends Expr { /** Gets the enclosing callable of this call. */ DataFlowCallable getEnclosingCallable() { + // NB. At present calls cannot occur inside summarized callables-- this will change if we implement advanced lambda support. result.asCallable().getFuncDef() = this.getEnclosingFunction() or not exists(this.getEnclosingFunction()) and result.asFileScope() = this.getFile() diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll index 7eedf1f911ec..7d84e645b666 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll @@ -17,7 +17,7 @@ private module FlowSummaries { class SummarizedCallableBase = Callable; -DataFlowCallable inject(SummarizedCallable c) { result.asCallable() = c } +DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c } /** Gets the parameter position of the instance parameter. */ ArgumentPosition callbackSelfParameterPosition() { result = -1 } @@ -189,9 +189,7 @@ class InterpretNode extends TInterpretNode { /** Gets the callable that this node corresponds to, if any. */ DataFlowCallable asCallable() { - result.asFunction() = this.asElement().asEntity() - or - result.asFuncLit() = this.asElement().asAstNode() + result.asSummarizedCallable().asFunction() = this.asElement().asEntity() } /** Gets the target of this call, if any. */ diff --git a/go/ql/lib/semmle/go/frameworks/Twirp.qll b/go/ql/lib/semmle/go/frameworks/Twirp.qll index 047280bc48a9..c4fc0737ac14 100644 --- a/go/ql/lib/semmle/go/frameworks/Twirp.qll +++ b/go/ql/lib/semmle/go/frameworks/Twirp.qll @@ -165,8 +165,8 @@ module Twirp { */ class Request extends UntrustedFlowSource::Range instanceof DataFlow::ParameterNode { Request() { - exists(Callable c, ServiceHandler handler | c.asFunction() = handler | - this.isParameterOf(c, 1) and + exists(FuncDef c, ServiceHandler handler | handler.getFuncDecl() = c | + this.asParameter().isParameterOf(c, 1) and handler.getParameterType(0).hasQualifiedName("context", "Context") and this.getType().(PointerType).getBaseType() instanceof ProtobufMessageType ) diff --git a/go/ql/test/library-tests/semmle/go/dataflow/CallGraph/viableCallee.ql b/go/ql/test/library-tests/semmle/go/dataflow/CallGraph/viableCallee.ql index e258145d2d16..5c157e7fffa1 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/CallGraph/viableCallee.ql +++ b/go/ql/test/library-tests/semmle/go/dataflow/CallGraph/viableCallee.ql @@ -17,10 +17,10 @@ string metadata(Locatable l, string key) { query predicate missingCallee(DataFlow::CallNode call, FuncDef callee) { metadata(call.asExpr(), "callee") = metadata(callee, "name") and - not viableCallable(call.asExpr()).getFuncDef() = callee + not viableCallable(call.asExpr()).asCallable().getFuncDef() = callee } query predicate spuriousCallee(DataFlow::CallNode call, FuncDef callee) { - viableCallable(call.asExpr()).getFuncDef() = callee and + viableCallable(call.asExpr()).asCallable().getFuncDef() = callee and not metadata(call.asExpr(), "callee") = metadata(callee, "name") }