Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4ea4e0d

Browse files
committed
Go: seperate real and synthetic callables
This means that when a function has a real body and a summary (usually because it has a real definition in source, and implements an interface that has a model), two callables are created and dispatch considers both possible paths. This specifically overcomes the difficulty with ParameterNodes when the real callable, if any, may or may not define an SsaNode, either because the real parameter is unused or because it is anonymous. Now the synthetic callable will always have parameter nodes, while the real one may or may not depending on whether a definition is present and whether or not it names or uses its parameter.
1 parent cfb273a commit 4ea4e0d

5 files changed

Lines changed: 47 additions & 44 deletions

File tree

go/ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ private predicate isConcreteInterfaceCall(DataFlow::Node call, DataFlow::Node re
5151
isInterfaceCallReceiver(call, recv, _, m) and isConcreteValue(recv)
5252
}
5353

54+
private Function getRealOrSummarizedFunction(DataFlowCallable c) {
55+
result = c.asCallable().asFunction()
56+
or
57+
result = c.asSummarizedCallable().asFunction()
58+
}
59+
5460
/**
5561
* Gets a function that might be called by `call`, where the receiver of `call` has interface type,
5662
* but its concrete types can be determined by local reasoning.
@@ -59,7 +65,7 @@ private DataFlowCallable getConcreteTarget(DataFlow::CallNode call) {
5965
exists(string m | isConcreteInterfaceCall(call, _, m) |
6066
exists(Type concreteReceiverType |
6167
concreteReceiverType = getConcreteType(getInterfaceCallReceiverSource(call)) and
62-
result.asFunction() = concreteReceiverType.getMethod(m)
68+
getRealOrSummarizedFunction(result) = concreteReceiverType.getMethod(m)
6369
)
6470
)
6571
}
@@ -78,7 +84,7 @@ private predicate isInterfaceMethodCall(DataFlow::CallNode call) {
7884
private DataFlowCallable getRestrictedInterfaceTarget(DataFlow::CallNode call) {
7985
exists(InterfaceType tp, Type recvtp, string m |
8086
isInterfaceCallReceiver(call, _, tp, m) and
81-
result.asFunction() = recvtp.getMethod(m) and
87+
getRealOrSummarizedFunction(result) = recvtp.getMethod(m) and
8288
recvtp.implements(tp)
8389
)
8490
}
@@ -93,7 +99,8 @@ DataFlowCallable viableCallable(CallExpr ma) {
9399
else
94100
if isInterfaceMethodCall(call)
95101
then result = getRestrictedInterfaceTarget(call)
96-
else result.asCallable() = call.getACalleeIncludingExternals()
102+
else
103+
[result.asCallable(), result.asSummarizedCallable()] = call.getACalleeIncludingExternals()
97104
)
98105
}
99106

go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@ private newtype TNode =
1010
MkInstructionNode(IR::Instruction insn) or
1111
MkSsaNode(SsaDefinition ssa) or
1212
MkGlobalFunctionNode(Function f) or
13-
MkSummarizedParameterNode(DataFlowCallable c, int i) {
14-
not exists(c.getFuncDef()) and
15-
c.asCallable() instanceof SummarizedCallable and
16-
(
17-
i in [0 .. c.getType().getNumParameter() - 1]
18-
or
19-
c.asFunction() instanceof Method and i = -1
20-
)
13+
MkSummarizedParameterNode(SummarizedCallable c, int i) {
14+
i in [0 .. c.getType().getNumParameter() - 1]
15+
or
16+
c.asFunction() instanceof Method and i = -1
2117
} or
2218
MkSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
2319
FlowSummaryImpl::Private::summaryNodeRange(c, state)
@@ -31,12 +27,18 @@ module Private {
3127
DataFlowCallable nodeGetEnclosingCallable(Node n) {
3228
result.asCallable() = n.getEnclosingCallable()
3329
or
34-
not exists(n.getEnclosingCallable()) and result.asFileScope() = n.getFile()
30+
(n = MkInstructionNode(_) or n = MkSsaNode(_) or n = MkGlobalFunctionNode(_)) and
31+
not exists(n.getEnclosingCallable()) and
32+
result.asFileScope() = n.getFile()
33+
or
34+
n = MkSummarizedParameterNode(result.asSummarizedCallable(), _)
35+
or
36+
n = MkSummaryInternalNode(result.asSummarizedCallable(), _)
3537
}
3638

3739
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
3840
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
39-
p.isParameterOf(c.asCallable(), pos)
41+
p.isParameterOf(c, pos)
4042
}
4143

4244
/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */
@@ -115,15 +117,7 @@ module Public {
115117
ControlFlow::Root getRoot() { none() } // overridden in subclasses
116118

117119
/** INTERNAL: Use `getRoot()` instead. */
118-
Callable getEnclosingCallable() {
119-
result.getFuncDef() = this.getRoot()
120-
or
121-
exists(DataFlowCallable dfc | result = dfc.asCallable() |
122-
this = MkSummarizedParameterNode(dfc, _)
123-
or
124-
this = MkSummaryInternalNode(dfc.asCallable(), _)
125-
)
126-
}
120+
Callable getEnclosingCallable() { result.getFuncDef() = this.getRoot() }
127121

128122
/** Gets the type of this node. */
129123
Type getType() { none() } // overridden in subclasses
@@ -577,20 +571,18 @@ module Public {
577571
/** A representation of a parameter initialization. */
578572
abstract class ParameterNode extends DataFlow::Node {
579573
/** Holds if this node initializes the `i`th parameter of `c`. */
580-
abstract predicate isParameterOf(Callable c, int i);
574+
abstract predicate isParameterOf(DataFlowCallable c, int i);
581575
}
582576

583577
/**
584578
* A summary node which represents a parameter in a function which doesn't
585579
* already have a parameter nodes.
586580
*/
587581
class SummarizedParameterNode extends ParameterNode, MkSummarizedParameterNode {
588-
Callable c;
582+
SummarizedCallable c;
589583
int i;
590584

591-
SummarizedParameterNode() {
592-
this = MkSummarizedParameterNode(any(DataFlowCallable dfc | c = dfc.asCallable()), i)
593-
}
585+
SummarizedParameterNode() { this = MkSummarizedParameterNode(c, i) }
594586

595587
// There are no AST representations of summarized parameter nodes
596588
override ControlFlow::Root getRoot() { none() }
@@ -603,7 +595,9 @@ module Public {
603595
i = -1 and result = c.asFunction().(Method).getReceiverType()
604596
}
605597

606-
override predicate isParameterOf(Callable call, int idx) { c = call and i = idx }
598+
override predicate isParameterOf(DataFlowCallable call, int idx) {
599+
c = call.asSummarizedCallable() and i = idx
600+
}
607601

608602
override string toString() { result = "parameter " + i + " of " + c.toString() }
609603

@@ -622,7 +616,9 @@ module Public {
622616
/** Gets the parameter this node initializes. */
623617
override Parameter asParameter() { result = parm }
624618

625-
override predicate isParameterOf(Callable c, int i) { parm.isParameterOf(c.getFuncDef(), i) }
619+
override predicate isParameterOf(DataFlowCallable c, int i) {
620+
parm.isParameterOf(c.asCallable().getFuncDef(), i)
621+
}
626622
}
627623

628624
/** A representation of a receiver initialization. */

go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ private import DataFlowUtil
33
private import DataFlowImplCommon
44
private import ContainerFlow
55
private import FlowSummaryImpl as FlowSummaryImpl
6+
private import semmle.go.dataflow.FlowSummary as FlowSummary
67
private import codeql.util.Unit
78
import DataFlowNodes::Private
89

@@ -237,31 +238,31 @@ class DataFlowLocation = Location;
237238

238239
private newtype TDataFlowCallable =
239240
TCallable(Callable c) or
240-
TFileScope(File f)
241+
TFileScope(File f) or
242+
TSummarizedCallable(FlowSummary::SummarizedCallable c)
241243

242244
class DataFlowCallable extends TDataFlowCallable {
243245
Callable asCallable() { this = TCallable(result) }
244246

245247
File asFileScope() { this = TFileScope(result) }
246248

247-
FuncDef getFuncDef() { result = this.asCallable().getFuncDef() }
249+
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
248250

249-
Function asFunction() { result = this.asCallable().asFunction() }
250-
251-
FuncLit asFuncLit() { result = this.asCallable().asFuncLit() }
252-
253-
SignatureType getType() { result = this.asCallable().getType() }
251+
SignatureType getType() { result = [this.asCallable(), this.asSummarizedCallable()].getType() }
254252

255253
string toString() {
256254
result = this.asCallable().toString() or
257-
result = "File scope: " + this.asFileScope().toString()
255+
result = "File scope: " + this.asFileScope().toString() or
256+
result = "Summary: " + this.asSummarizedCallable().toString()
258257
}
259258

260259
predicate hasLocationInfo(
261260
string filepath, int startline, int startcolumn, int endline, int endcolumn
262261
) {
263262
this.asCallable().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
264-
this.asFileScope().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
263+
this.asFileScope().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
264+
this.asSummarizedCallable()
265+
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
265266
}
266267
}
267268

@@ -281,6 +282,7 @@ class DataFlowCall extends Expr {
281282

282283
/** Gets the enclosing callable of this call. */
283284
DataFlowCallable getEnclosingCallable() {
285+
// NB. At present calls cannot occur inside summarized callables-- this will change if we implement advanced lambda support.
284286
result.asCallable().getFuncDef() = this.getEnclosingFunction()
285287
or
286288
not exists(this.getEnclosingFunction()) and result.asFileScope() = this.getFile()

go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ private module FlowSummaries {
1717

1818
class SummarizedCallableBase = Callable;
1919

20-
DataFlowCallable inject(SummarizedCallable c) { result.asCallable() = c }
20+
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
2121

2222
/** Gets the parameter position of the instance parameter. */
2323
ArgumentPosition callbackSelfParameterPosition() { result = -1 }
@@ -189,9 +189,7 @@ class InterpretNode extends TInterpretNode {
189189

190190
/** Gets the callable that this node corresponds to, if any. */
191191
DataFlowCallable asCallable() {
192-
result.asFunction() = this.asElement().asEntity()
193-
or
194-
result.asFuncLit() = this.asElement().asAstNode()
192+
result.asSummarizedCallable().asFunction() = this.asElement().asEntity()
195193
}
196194

197195
/** Gets the target of this call, if any. */

go/ql/lib/semmle/go/frameworks/Twirp.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ module Twirp {
165165
*/
166166
class Request extends UntrustedFlowSource::Range instanceof DataFlow::ParameterNode {
167167
Request() {
168-
exists(Callable c, ServiceHandler handler | c.asFunction() = handler |
169-
this.isParameterOf(c, 1) and
168+
exists(FuncDef c, ServiceHandler handler | handler.getFuncDecl() = c |
169+
this.asParameter().isParameterOf(c, 1) and
170170
handler.getParameterType(0).hasQualifiedName("context", "Context") and
171171
this.getType().(PointerType).getBaseType() instanceof ProtobufMessageType
172172
)

0 commit comments

Comments
 (0)