Skip to content

Commit e46ec5a

Browse files
committed
Fix MaD inheritance
1 parent 91375d3 commit e46ec5a

40 files changed

+442
-254
lines changed

go/ql/lib/semmle/go/dataflow/ExternalFlow.qll

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -474,17 +474,30 @@ SourceSinkInterpretationInput::SourceOrSinkElement interpretElement(
474474
elementSpec(pkg, type, subtypes, name, signature, ext) and
475475
// Go does not need to distinguish functions with signature
476476
signature = "" and
477-
(
478-
exists(Field f | f.hasQualifiedName(interpretPackage(pkg), type, name) | result.asEntity() = f)
477+
exists(string p | p = interpretPackage(pkg) |
478+
exists(Field f | f.hasQualifiedName(p, type, name) |
479+
result.asEntity() = f and
480+
result.hasTypeInfo(p, type, subtypes)
481+
)
479482
or
480-
exists(Method m | m.hasQualifiedName(interpretPackage(pkg), type, name) |
481-
result.asEntity() = m
483+
exists(Method m | m.hasQualifiedName(p, type, name) |
484+
result.asEntity() = m and
485+
result.hasTypeInfo(p, type, subtypes)
482486
or
483-
subtypes = true and result.asEntity().(Method).implementsIncludingInterfaceMethods(m)
487+
subtypes = true and
488+
// p.type is an interface and we include types which implement it
489+
exists(Method m2, string pkg2, string type2 |
490+
m2.getReceiverType().implements(p, type) and
491+
m2.getName() = name and
492+
m2.getReceiverBaseType().hasQualifiedName(pkg2, type2)
493+
|
494+
result.asEntity() = m2 and
495+
result.hasTypeInfo(pkg2, type2, subtypes)
496+
)
484497
)
485498
or
486499
type = "" and
487-
exists(Entity e | e.hasQualifiedName(interpretPackage(pkg), name) | result.asEntity() = e)
500+
exists(Entity e | e.hasQualifiedName(p, name) | result.asEntity() = e)
488501
)
489502
}
490503

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

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,20 +150,50 @@ module SourceSinkInterpretationInput implements
150150
}
151151

152152
private newtype TSourceOrSinkElement =
153-
TEntityElement(Entity e) or
153+
TMethodEntityElement(Method m, string pkg, string type, boolean subtypes) {
154+
m.hasQualifiedName(pkg, type, _) and subtypes = [true, false]
155+
} or
156+
TFieldEntityElement(Field f, string pkg, string type, boolean subtypes) {
157+
f.hasQualifiedName(pkg, type, _) and subtypes = [true, false]
158+
} or
159+
TOtherEntityElement(Entity e) {
160+
not e instanceof Method and
161+
not e instanceof Field
162+
} or
154163
TAstElement(AstNode n)
155164

156165
/** An element representable by CSV modeling. */
157166
class SourceOrSinkElement extends TSourceOrSinkElement {
158167
/** Gets this source or sink element as an entity, if it is one. */
159-
Entity asEntity() { this = TEntityElement(result) }
168+
Entity asEntity() {
169+
this = TMethodEntityElement(result, _, _, _) or
170+
this = TFieldEntityElement(result, _, _, _) or
171+
this = TOtherEntityElement(result)
172+
}
160173

161174
/** Gets this source or sink element as an AST node, if it is one. */
162175
AstNode asAstNode() { this = TAstElement(result) }
163176

177+
/**
178+
* Holds if this source or sink element is a method that was specified
179+
* with the given values for `pkg`, `type` and `subtypes`.
180+
*/
181+
predicate hasTypeInfo(string pkg, string type, boolean subtypes) {
182+
this = TMethodEntityElement(_, pkg, type, subtypes) or
183+
this = TFieldEntityElement(_, pkg, type, subtypes)
184+
}
185+
164186
/** Gets a textual representation of this source or sink element. */
165187
string toString() {
188+
not this.hasTypeInfo(_, _, _) and
166189
result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()]
190+
or
191+
exists(string pkg, string name, boolean subtypes |
192+
this.hasTypeInfo(pkg, name, subtypes) and
193+
result =
194+
"element representing " + this.asEntity().toString() + " with receiver type " + pkg + "." +
195+
name + " and subtypes=" + subtypes
196+
)
167197
}
168198

169199
/** Gets the location of this element. */
@@ -203,7 +233,17 @@ module SourceSinkInterpretationInput implements
203233

204234
/** Gets the target of this call, if any. */
205235
SourceOrSinkElement getCallTarget() {
206-
result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget()
236+
exists(DataFlow::CallNode cn, Function callTarget |
237+
cn = this.asCall().getNode() and
238+
callTarget = cn.getTarget()
239+
|
240+
result.asEntity() = callTarget and
241+
(
242+
not callTarget instanceof Method
243+
or
244+
ensureCorrectTypeInfo(result, cn.getReceiver())
245+
)
246+
)
207247
}
208248

209249
/** Gets a textual representation of this node. */
@@ -228,6 +268,90 @@ module SourceSinkInterpretationInput implements
228268
}
229269
}
230270

271+
private predicate ensureCorrectTypeInfo(SourceOrSinkElement sse, DataFlow::Node recv) {
272+
(
273+
exists(DataFlow::CallNode cn | cn.getReceiver() = recv and cn.getTarget() = sse.asEntity())
274+
or
275+
exists(DataFlow::FieldReadNode frn | frn.getBase() = recv and frn.getField() = sse.asEntity())
276+
or
277+
exists(DataFlow::Write fw | fw.writesField(recv, sse.asEntity(), _))
278+
) and
279+
exists(string pkg, string typename, boolean subtypes, Type syntacticRecvBaseType |
280+
sse.hasTypeInfo(pkg, typename, subtypes) and
281+
syntacticRecvBaseType = lookThroughPointerType(getSyntacticRecv(recv).getType())
282+
|
283+
subtypes = [true, false] and
284+
syntacticRecvBaseType.hasQualifiedName(pkg, typename)
285+
or
286+
subtypes = true and
287+
(
288+
// `syntacticRecvBaseType`'s underlying type might be an interface type and `sse`
289+
// might be a method defined on an interface which is a subtype of it.
290+
exists(Type t |
291+
t = syntacticRecvBaseType.getUnderlyingType().(InterfaceType).getAnEmbeddedInterface() and
292+
t.hasQualifiedName(pkg, typename) and
293+
sse.asEntity().(Method).hasQualifiedName(pkg, typename, _)
294+
)
295+
or
296+
// `syntacticRecvBaseType`'s underlying type might be a struct type and `sse`
297+
// might be a promoted method or field.
298+
exists(StructType st, Field field1, Field field2, int depth1, int depth2, Type t1, Type t2 |
299+
st = syntacticRecvBaseType.getUnderlyingType() and
300+
field1 = st.getFieldAtDepth(_, depth1) and
301+
field2 = st.getFieldAtDepth(_, depth2) and
302+
t1 = lookThroughPointerType(field1.getType()) and
303+
t2 = lookThroughPointerType(field2.getType()) and
304+
(
305+
field1 = field2
306+
or
307+
field2 = t1.getUnderlyingType().(StructType).getFieldAtDepth(_, depth2 - depth1 - 1)
308+
) and
309+
matchTypeInfo(sse, t1)
310+
|
311+
sse.asEntity().(Method).getReceiverBaseType() = t2
312+
or
313+
sse.asEntity().(Field).getDeclaringType() = t2.getUnderlyingType()
314+
)
315+
)
316+
)
317+
}
318+
319+
private DataFlow::Node getSyntacticRecv(DataFlow::Node n) {
320+
exists(DataFlow::Node n2 |
321+
// look through implicit dereference, if there is one
322+
not exists(n.asInstruction().(IR::EvalImplicitDerefInstruction).getOperand()) and
323+
n2 = n
324+
or
325+
n2.asExpr() = n.asInstruction().(IR::EvalImplicitDerefInstruction).getOperand()
326+
|
327+
result = skipImplicitFieldReads(n2)
328+
)
329+
}
330+
331+
private DataFlow::Node skipImplicitFieldReads(DataFlow::Node n) {
332+
not exists(getImplicitFieldReadInstruction(n)) and result = n
333+
or
334+
result = skipImplicitFieldReads(getImplicitFieldReadInstruction(n))
335+
}
336+
337+
pragma[inline]
338+
private DataFlow::Node getImplicitFieldReadInstruction(DataFlow::Node n) {
339+
result.asInstruction() =
340+
n.(DataFlow::InstructionNode)
341+
.asInstruction()
342+
.(IR::ImplicitFieldReadInstruction)
343+
.getBaseInstruction()
344+
}
345+
346+
bindingset[sse, t]
347+
pragma[inline_late]
348+
private predicate matchTypeInfo(SourceOrSinkElement sse, Type t) {
349+
exists(string pkg, string typename |
350+
sse.hasTypeInfo(pkg, typename, true) and
351+
t.hasQualifiedName(pkg, typename)
352+
)
353+
}
354+
231355
/** Provides additional sink specification logic. */
232356
bindingset[c]
233357
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
@@ -244,8 +368,11 @@ module SourceSinkInterpretationInput implements
244368
(c = "Parameter" or c = "") and
245369
node.asNode().asParameter() = e.asEntity()
246370
or
247-
c = "" and
248-
n.(DataFlow::FieldReadNode).getField() = e.asEntity()
371+
exists(DataFlow::FieldReadNode frn | frn = n |
372+
c = "" and
373+
frn.getField() = e.asEntity() and
374+
ensureCorrectTypeInfo(e, frn.getBase())
375+
)
249376
)
250377
}
251378

@@ -259,10 +386,13 @@ module SourceSinkInterpretationInput implements
259386
mid.asCallable() = getNodeEnclosingCallable(ret)
260387
)
261388
or
262-
exists(DataFlow::Write fw, Field f |
389+
exists(SourceOrSinkElement e, DataFlow::Write fw, DataFlow::Node base, Field f |
390+
e = mid.asElement() and
391+
f = e.asEntity()
392+
|
263393
c = "" and
264-
f = mid.asElement().asEntity() and
265-
fw.writesField(_, f, node.asNode())
394+
fw.writesField(base, f, node.asNode()) and
395+
ensureCorrectTypeInfo(e, base)
266396
)
267397
}
268398
}

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_IEmbedI1_subtypes_true.ext.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extensions:
33
pack: codeql/go-all
44
extensible: sourceModel
55
data:
6-
- ["github.com/nonexistent/test", "IEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
6+
- ["github.com/nonexistent/test", "IEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
77
- addsTo:
88
pack: codeql/go-all
99
extensible: summaryModel
@@ -13,4 +13,4 @@ extensions:
1313
pack: codeql/go-all
1414
extensible: sinkModel
1515
data:
16-
- ["github.com/nonexistent/test", "IEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
16+
- ["github.com/nonexistent/test", "IEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_IEmbedI1_subtypes_true.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import go
2-
import semmle.go.dataflow.ExternalFlow
32
import ModelValidation
4-
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
53
import TestUtilities.InlineExpectationsTest
64
import MakeTest<FlowTest>
75

86
module Config implements DataFlow::ConfigSig {
9-
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
7+
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
108

11-
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
9+
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
1210
}
1311

1412
module Flow = TaintTracking::Global<Config>;

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_IEmbedI2_subtypes_true.ext.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extensions:
33
pack: codeql/go-all
44
extensible: sourceModel
55
data:
6-
- ["github.com/nonexistent/test", "IEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
6+
- ["github.com/nonexistent/test", "IEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
77
- addsTo:
88
pack: codeql/go-all
99
extensible: summaryModel
@@ -13,4 +13,4 @@ extensions:
1313
pack: codeql/go-all
1414
extensible: sinkModel
1515
data:
16-
- ["github.com/nonexistent/test", "IEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
16+
- ["github.com/nonexistent/test", "IEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_IEmbedI2_subtypes_true.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import go
2-
import semmle.go.dataflow.ExternalFlow
32
import ModelValidation
4-
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
53
import TestUtilities.InlineExpectationsTest
64
import MakeTest<FlowTest>
75

86
module Config implements DataFlow::ConfigSig {
9-
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
7+
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
108

11-
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
9+
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
1210
}
1311

1412
module Flow = TaintTracking::Global<Config>;

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_SEmbedI1_subtypes_true.ext.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ extensions:
33
pack: codeql/go-all
44
extensible: sourceModel
55
data:
6-
- ["github.com/nonexistent/test", "SEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
6+
- ["github.com/nonexistent/test", "SEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
7+
- ["github.com/nonexistent/test", "SEmbedI1", True, "SourceField", "", "", "", "qltest", "manual"]
78
- addsTo:
89
pack: codeql/go-all
910
extensible: summaryModel
@@ -13,4 +14,5 @@ extensions:
1314
pack: codeql/go-all
1415
extensible: sinkModel
1516
data:
16-
- ["github.com/nonexistent/test", "SEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
17+
- ["github.com/nonexistent/test", "SEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
18+
- ["github.com/nonexistent/test", "SEmbedI1", True, "SinkField", "", "", "", "qltest", "manual"]

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_SEmbedI1_subtypes_true.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import go
2-
import semmle.go.dataflow.ExternalFlow
32
import ModelValidation
4-
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
53
import TestUtilities.InlineExpectationsTest
64
import MakeTest<FlowTest>
75

86
module Config implements DataFlow::ConfigSig {
9-
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
7+
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
108

11-
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
9+
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
1210
}
1311

1412
module Flow = TaintTracking::Global<Config>;

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_SEmbedI2_subtypes_true.ext.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extensions:
33
pack: codeql/go-all
44
extensible: sourceModel
55
data:
6-
- ["github.com/nonexistent/test", "SEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
6+
- ["github.com/nonexistent/test", "SEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
77
- addsTo:
88
pack: codeql/go-all
99
extensible: summaryModel
@@ -13,4 +13,4 @@ extensions:
1313
pack: codeql/go-all
1414
extensible: sinkModel
1515
data:
16-
- ["github.com/nonexistent/test", "SEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
16+
- ["github.com/nonexistent/test", "SEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]

go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/mad_SEmbedI2_subtypes_true.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import go
2-
import semmle.go.dataflow.ExternalFlow
32
import ModelValidation
4-
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
53
import TestUtilities.InlineExpectationsTest
64
import MakeTest<FlowTest>
75

86
module Config implements DataFlow::ConfigSig {
9-
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
7+
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
108

11-
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
9+
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
1210
}
1311

1412
module Flow = TaintTracking::Global<Config>;

0 commit comments

Comments
 (0)