diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 092756a060255..ce55cb96a3ef5 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -474,17 +474,33 @@ SourceSinkInterpretationInput::SourceOrSinkElement interpretElement( elementSpec(pkg, type, subtypes, name, signature, ext) and // Go does not need to distinguish functions with signature signature = "" and - ( - exists(Field f | f.hasQualifiedName(interpretPackage(pkg), type, name) | result.asEntity() = f) + exists(string p | p = interpretPackage(pkg) | + exists(Field f | f.hasQualifiedName(p, type, name) | result.asEntity() = f) or - exists(Method m | m.hasQualifiedName(interpretPackage(pkg), type, name) | - result.asEntity() = m + exists(Method m | m.hasQualifiedName(p, type, name) | + m.hasQualifiedName(p, type, name) and + elementSpec(pkg, type, subtypes, name, signature, ext) and + p = interpretPackage(pkg) and + result.asEntity() = m and + result.hasReceiverInfo(p, type, subtypes) or - subtypes = true and result.asEntity().(Method).implementsIncludingInterfaceMethods(m) + subtypes = true and + // p.type is an interface and we include types which implement it + exists(Method m2, string pkg2, string type2 | + subtypes = true and + m.hasQualifiedName(p, type, name) and + elementSpec(pkg, type, subtypes, name, signature, ext) and + p = interpretPackage(pkg) and + m2.getReceiverType().implements(p, type) and + m2.getName() = name and + m2.getReceiverBaseType().hasQualifiedName(pkg2, type2) and + result.asEntity() = m2 and + result.hasReceiverInfo(pkg2, type2, subtypes) + ) ) or type = "" and - exists(Entity e | e.hasQualifiedName(interpretPackage(pkg), name) | result.asEntity() = e) + exists(Entity e | e.hasQualifiedName(p, name) | result.asEntity() = e) ) } diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll index 211974f75a56c..42452f7994424 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll @@ -150,20 +150,50 @@ module SourceSinkInterpretationInput implements } private newtype TSourceOrSinkElement = - TEntityElement(Entity e) or + TEntityElement(Entity e, string pkg, string type, boolean subtypes) { + e.(Method).hasQualifiedName(pkg, type, _) and subtypes = [true, false] + or + // or + // exists(Method m, string ppe, string ppm | + // // m.getName() = "Source" and + // ppe = e.(Method).getQualifiedName() and + // ppm = m.getQualifiedName() and + // e.(Method).getReceiverBaseType().getUnderlyingType() instanceof InterfaceType and + // m.implementsIncludingInterfaceMethods(e) and + // m.getReceiverBaseType().hasQualifiedName(pkg, type) and + // subtypes = true + // ) + not e instanceof Method and pkg = "" and type = "" and subtypes = false + } or TAstElement(AstNode n) /** An element representable by CSV modeling. */ class SourceOrSinkElement extends TSourceOrSinkElement { /** Gets this source or sink element as an entity, if it is one. */ - Entity asEntity() { this = TEntityElement(result) } + Entity asEntity() { this = TEntityElement(result, _, _, _) } /** Gets this source or sink element as an AST node, if it is one. */ AstNode asAstNode() { this = TAstElement(result) } + /** + * Holds if this source or sink element is a method that was specified + * with the given values for `pkg`, `type` and `subtypes`. + */ + predicate hasReceiverInfo(string pkg, string type, boolean subtypes) { + this = TEntityElement(any(Method m), pkg, type, subtypes) + } + /** Gets a textual representation of this source or sink element. */ string toString() { + not this.asEntity() instanceof Method and result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()] + or + exists(string pkg, string name, boolean subtypes | + this.hasReceiverInfo(pkg, name, subtypes) and + result = + "element representing " + this.asEntity().toString() + " with receiver type " + pkg + "." + + name + " and subtypes=" + subtypes + ) } /** Gets the location of this element. */ @@ -203,7 +233,53 @@ module SourceSinkInterpretationInput implements /** Gets the target of this call, if any. */ SourceOrSinkElement getCallTarget() { - result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget() + exists(DataFlow::CallNode cn, Function callTarget | + cn = this.asCall().getNode() and + callTarget = cn.getTarget() + | + result.asEntity() = callTarget and + ( + not callTarget instanceof Method + or + exists( + Method m, string pkg, string name, boolean subtypes, DataFlow::Node syntacticRecv, + Type syntacticRecvType, Type syntacticRecvBaseType + | + m = callTarget and + result.hasReceiverInfo(pkg, name, subtypes) and + syntacticRecv = skipImplicitFieldReads(cn.getReceiver()) and + syntacticRecvType = syntacticRecv.getType() and + if syntacticRecvType instanceof PointerType + then syntacticRecvBaseType = syntacticRecvType.(PointerType).getBaseType() + else syntacticRecvBaseType = syntacticRecvType + | + syntacticRecvBaseType.hasQualifiedName(pkg, name) + or + subtypes = true and + ( + // `syntacticRecvBaseType`'s underlying type might be a struct type and `result` + // might relate to a promoted method. + exists(string ppm, StructType st, string ppst, Field embeddedParent, int depth | + ppm = m.getQualifiedName() and + st = syntacticRecvBaseType.getUnderlyingType() and + ppst = st.pp() and + m = st.getMethodOfEmbedded(embeddedParent, _, depth) and + receiverInfoHelper(m, embeddedParent, st, depth, pkg, name) + ) + or + // `syntacticRecvBaseType`'s underlying type might be an interface type and `result` + // might relate to an embedded interface. + exists(Type t, string pprecv | + pprecv = syntacticRecvBaseType.pp() and + t = + syntacticRecvBaseType.getUnderlyingType().(InterfaceType).getAnEmbeddedInterface() and + t.hasQualifiedName(pkg, name) and + m.(Method).hasQualifiedName(pkg, name, _) + ) + ) + ) + ) + ) } /** Gets a textual representation of this node. */ @@ -228,6 +304,41 @@ module SourceSinkInterpretationInput implements } } + private DataFlow::Node skipImplicitFieldReads(DataFlow::Node n) { + not exists(getImplicitFieldReadInstruction(n)) and result = n + or + result = + skipImplicitFieldReads(DataFlow::instructionNode(getImplicitFieldReadInstruction(n) + .getBaseInstruction())) + } + + pragma[inline] + private IR::ImplicitFieldReadInstruction getImplicitFieldReadInstruction(DataFlow::Node n) { + result = n.(DataFlow::InstructionNode).asInstruction() + } + + /** + * Holds if `st` has an embedded field `embeddedParent` at depth `depth` - 1, + * which has a method `m` defined directly on it, and the type of + * `embeddedParent` is `pkg.name`. + */ + private predicate receiverInfoHelper( + Method m, Field embeddedParent, StructType st, int depth, string pkg, string name + ) { + depth = 1 and + embeddedParent = st.getOwnField(_, true) and + embeddedParent.getType().hasQualifiedName(pkg, name) and + m.getReceiverBaseType() = embeddedParent.getType() + or + depth > 1 and + exists(Field f, StructType st2 | + f = st.getOwnField(_, true) and + st2 = f.getType().getUnderlyingType() and + m = st2.getMethodOfEmbedded(embeddedParent, _, depth - 1) and + receiverInfoHelper(m, f, st2, depth - 1, pkg, name) + ) + } + /** Provides additional sink specification logic. */ bindingset[c] predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) { diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/test.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/test.go index 1c41bc05c76a7..9e32c25de3f46 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlowInheritance/test.go @@ -7,83 +7,83 @@ import ( func TestI1(t test.I1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[f] I1[t] ql_I1 SPURIOUS: IEmbedI1[t] SEmbedI1[t] ql_S1 + t.Sink(y) // $ I1[f] I1[t] ql_I1 SPURIOUS: ql_S1 } func TestI2(t test.I2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[f] I2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t] + t.Sink(y) // $ I1[t] I2[f] I2[t] } func TestS1(t test.S1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] S1[f] S1[t] ql_S1 SPURIOUS: IEmbedI1[t] SEmbedI1[t] + t.Sink(y) // $ I1[t] S1[f] S1[t] ql_S1 } func TestS2(t test.S2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t] + t.Sink(y) // $ I1[t] I2[t] } func TestSEmbedI1(t test.SEmbedI1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] SEmbedI1[t] ql_I1 SPURIOUS: I1[f] IEmbedI1[t] ql_S1 + t.Sink(y) // $ I1[t] SEmbedI1[t] ql_I1 SPURIOUS: ql_S1 } func TestSEmbedI2(t test.SEmbedI2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[t] SEmbedI2[t] SPURIOUS: I2[f] IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] + t.Sink(y) // $ I1[t] I2[t] SEmbedI2[t] } func TestIEmbedI1(t test.IEmbedI1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] IEmbedI1[t] ql_I1 SPURIOUS: I1[f] SEmbedI1[t] ql_S1 + t.Sink(y) // $ I1[t] IEmbedI1[t] ql_I1 SPURIOUS: ql_S1 } func TestIEmbedI2(t test.IEmbedI2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[t] IEmbedI2[t] SPURIOUS: I2[f] IEmbedI1[t] SEmbedI1[t] SEmbedI2[t] + t.Sink(y) // $ I1[t] I2[t] IEmbedI2[t] } func TestSImplEmbedI1(t test.SImplEmbedI1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] SImplEmbedI1[t] SPURIOUS: IEmbedI1[t] SEmbedI1[t] + t.Sink(y) // $ I1[t] SImplEmbedI1[t] } func TestSImplEmbedI2(t test.SImplEmbedI2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[t] SImplEmbedI2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t] + t.Sink(y) // $ I1[t] I2[t] SImplEmbedI2[t] } func TestSEmbedS1(t test.SEmbedS1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] S1[t] ql_S1 SPURIOUS: IEmbedI1[t] S1[f] SEmbedI1[t] + t.Sink(y) // $ I1[t] S1[t] ql_S1 } func TestSEmbedS2(t test.SEmbedS2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t] + t.Sink(y) // $ I1[t] I2[t] } func TestSImplEmbedS1(t test.SImplEmbedS1) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] SImplEmbedS1[t] SPURIOUS: IEmbedI1[t] SEmbedI1[t] + t.Sink(y) // $ I1[t] SImplEmbedS1[t] } func TestSImplEmbedS2(t test.SImplEmbedS2) { x := t.Source() y := t.Step(x) - t.Sink(y) // $ I1[t] I2[t] SImplEmbedS2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t] + t.Sink(y) // $ I1[t] I2[t] SImplEmbedS2[t] }