Skip to content

Commit

Permalink
Fix MaD inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
owen-mc committed Oct 16, 2024
1 parent 38d85a3 commit 2dd9a39
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 23 deletions.
28 changes: 22 additions & 6 deletions go/ql/lib/semmle/go/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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

Check warning

Code scanning / CodeQL

Redundant assignment. Warning

The variable subtypes
has previously been assigned
the same value
.
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)
)
}

Expand Down
117 changes: 114 additions & 3 deletions go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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, _)

Check warning

Code scanning / CodeQL

Redundant cast Warning

Redundant cast to
Method
.
)
)
)
)
)
}

/** Gets a textual representation of this node. */
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}

0 comments on commit 2dd9a39

Please sign in to comment.