Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
)
)
}
Expand All @@ -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)
)
}
Expand All @@ -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()
)
}

Expand Down
46 changes: 20 additions & 26 deletions go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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`. */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -578,20 +570,18 @@ 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);
}

/**
* A summary node which represents a parameter in a function which doesn't
* 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() }
Expand All @@ -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() }

Expand All @@ -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. */
Expand Down
20 changes: 11 additions & 9 deletions go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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. */
Expand Down
4 changes: 2 additions & 2 deletions go/ql/lib/semmle/go/frameworks/Twirp.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}