Merge pull request #492 from owen-mc/promoted-field-data-flow-non-pointer-type

Add control flow nodes for implicit fields reads when reading a promoted field
This commit is contained in:
Owen Mansel-Chan
2021-03-30 11:15:55 +01:00
committed by GitHub
27 changed files with 879 additions and 70 deletions

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The data-flow library has been improved to represent reads and writes of promoted fields correctly, which may lead to more alerts.

View File

@@ -593,6 +593,33 @@ class SelectorExpr extends @selectorexpr, Expr {
override string getAPrimaryQlClass() { result = "SelectorExpr" }
}
/**
* A selector expression that refers to a promoted field or a promoted method. These
* selectors may implicitly address an embedded struct of their base type - for example,
* the selector `x.field` may implicitly address `x.Embedded.field`). Note they may also
* explicitly address `field`; being a `PromotedSelector` only indicates the addressed
* field or method may be promoted, not that it is promoted in this particular context.
*/
class PromotedSelector extends SelectorExpr {
PromotedSelector() {
exists(ValueEntity ve | this.refersTo(ve) |
ve instanceof PromotedField or ve instanceof PromotedMethod
)
}
/**
* Gets the underlying struct type of this selector's base. Note because this selector
* addresses a promoted field, the addressed field may not directly occur in the returned
* struct type.
*/
StructType getSelectedStructType() {
exists(Type baseType | baseType = this.getBase().getType().getUnderlyingType() |
pragma[only_bind_into](result) =
[baseType, baseType.(PointerType).getBaseType().getUnderlyingType()]
)
}
}
/**
* An index expression, that is, a base expression followed by an index.
*

View File

@@ -353,6 +353,15 @@ class Field extends Variable {
}
}
/**
* A field that belongs to a struct that may be embedded within another struct.
*
* When a selector addresses such a field, it is possible it is implicitly addressing a nested struct.
*/
class PromotedField extends Field {
PromotedField() { this = any(StructType t).getFieldOfEmbedded(_, _, _, _) }
}
/** A built-in or declared function. */
class Function extends ValueEntity, @functionobject {
/** Gets a call to this function. */
@@ -530,6 +539,15 @@ class Method extends Function {
}
}
/**
* A method whose receiver may be embedded within a struct.
*
* When a selector addresses such a method, it is possible it is implicitly addressing a nested struct.
*/
class PromotedMethod extends Method {
PromotedMethod() { this = any(StructType t).getMethodOfEmbedded(_, _, _) }
}
/** A declared function. */
class DeclaredFunction extends Function, DeclaredEntity, @declfunctionobject {
override FuncDecl getFuncDecl() { result.getNameExpr() = this.getDeclaration() }

View File

@@ -375,39 +375,59 @@ class StructType extends @structtype, CompositeType {
this.hasOwnField(_, name, _, isEmbedded)
}
/**
* Holds if there is an embedded field at `depth`, with either type `tp` or a pointer to `tp`.
*/
private predicate hasEmbeddedField(Type tp, int depth) {
hasFieldOrMethodCand(_, tp, depth, true, false)
or
exists(PointerType embeddedPtr |
hasFieldOrMethodCand(_, embeddedPtr, depth, true, false) and
tp = embeddedPtr.getBaseType()
exists(Field f | this.hasFieldCand(_, f, depth, true) |
tp = f.getType() or
tp = f.getType().(PointerType).getBaseType()
)
}
private predicate hasFieldOrMethodCand(
string name, Type tp, int depth, boolean isEmbedded, boolean isMethod
) {
hasOwnField(_, name, tp, isEmbedded) and depth = 0 and isMethod = false
or
not hasOwnField(_, name, _, _) and
exists(Type embedded | hasEmbeddedField(embedded, depth - 1) |
embedded.getUnderlyingType().(StructType).hasOwnField(_, name, tp, isEmbedded) and
isMethod = false
/**
* Gets a field of `embeddedParent`, which is then embedded into this struct type.
*/
Field getFieldOfEmbedded(Field embeddedParent, string name, int depth, boolean isEmbedded) {
// embeddedParent is a field of 'this' at depth 'depth - 1'
this.hasFieldCand(_, embeddedParent, depth - 1, true) and
// embeddedParent's type has the result field
exists(StructType embeddedType, Type fieldType |
fieldType = embeddedParent.getType().getUnderlyingType() and
pragma[only_bind_into](embeddedType) =
[fieldType, fieldType.(PointerType).getBaseType().getUnderlyingType()]
|
result = embeddedType.getOwnField(name, isEmbedded)
)
}
/**
* Gets a method of `embeddedParent`, which is then embedded into this struct type.
*/
Method getMethodOfEmbedded(Field embeddedParent, string name, int depth) {
// embeddedParent is a field of 'this' at depth 'depth - 1'
this.hasFieldCand(_, embeddedParent, depth - 1, true) and
result.getName() = name and
(
result.getReceiverBaseType() = embeddedParent.getType()
or
exists(MethodDecl md | md.getReceiverType() = embedded |
name = md.getName() and
tp = md.getType()
) and
isEmbedded = false and
isMethod = true
result.getReceiverBaseType() = embeddedParent.getType().(PointerType).getBaseType()
or
methodhosts(result, embeddedParent.getType())
)
}
private predicate hasFieldOrMethod(string name, Type tp, boolean isMethod) {
exists(int mindepth |
mindepth = min(int depth | hasFieldOrMethodCand(name, _, depth, _, _)) and
hasFieldOrMethodCand(name, tp, mindepth, _, isMethod) and
(strictcount(getFieldCand(name, mindepth, _)) = 1 or isMethod = true)
private predicate hasFieldCand(string name, Field f, int depth, boolean isEmbedded) {
f = this.getOwnField(name, isEmbedded) and depth = 0
or
not this.hasOwnField(_, name, _, _) and
f = this.getFieldOfEmbedded(_, name, depth, isEmbedded)
}
private predicate hasMethodCand(string name, Method m, int depth) {
name = m.getName() and
exists(Type embedded | this.hasEmbeddedField(embedded, depth - 1) |
m.getReceiverType() = embedded
)
}
@@ -415,7 +435,12 @@ class StructType extends @structtype, CompositeType {
* Holds if this struct contains a field `name` with type `tp`, possibly inside a (nested)
* embedded field.
*/
predicate hasField(string name, Type tp) { hasFieldOrMethod(name, tp, false) }
predicate hasField(string name, Type tp) {
exists(int mindepth |
mindepth = min(int depth | this.hasFieldCand(name, _, depth, _)) and
tp = unique(Field f | f = this.getFieldCand(name, mindepth, _)).getType()
)
}
private Field getFieldCand(string name, int depth, boolean isEmbedded) {
result = this.getOwnField(name, isEmbedded) and depth = 0
@@ -425,15 +450,33 @@ class StructType extends @structtype, CompositeType {
)
}
override Field getField(string name) {
exists(int mindepth |
mindepth = min(int depth | exists(getFieldCand(name, depth, _))) and
result = getFieldCand(name, mindepth, _) and
strictcount(getFieldCand(name, mindepth, _)) = 1
)
override Field getField(string name) { result = getFieldAtDepth(name, _) }
/**
* Gets the field `f` with depth `depth` of this type.
*
* This includes fields promoted from an embedded field. It is not possible
* to access a field that is shadowed by a promoted field with this function.
* The number of embedded fields traversed to reach `f` is called its depth.
* The depth of a field `f` declared in this type is zero.
*/
Field getFieldAtDepth(string name, int depth) {
depth = min(int depthCand | exists(getFieldCand(name, depthCand, _))) and
result = getFieldCand(name, depth, _) and
strictcount(getFieldCand(name, depth, _)) = 1
}
override predicate hasMethod(string name, SignatureType tp) { hasFieldOrMethod(name, tp, true) }
Method getMethodAtDepth(string name, int depth) {
depth = min(int depthCand | hasMethodCand(name, _, depthCand)) and
result = unique(Method m | hasMethodCand(name, m, depth))
}
override predicate hasMethod(string name, SignatureType tp) {
exists(int mindepth |
mindepth = min(int depth | this.hasMethodCand(name, _, depth)) and
tp = unique(Method m | this.hasMethodCand(name, m, mindepth)).getType()
)
}
language[monotonicAggregates]
override string pp() {

View File

@@ -46,9 +46,9 @@ private TVariableWithFields accessPath(IR::Instruction insn) {
* by variable with fields value `base`.
*/
private IR::Instruction fieldAccessPathAux(TVariableWithFields base, Field f) {
exists(IR::FieldReadInstruction fr, IR::EvalInstruction frb |
exists(IR::FieldReadInstruction fr, IR::Instruction frb |
fr.getBase() = frb or
fr.getBase() = IR::implicitDerefInstruction(frb.getExpr())
fr.getBase() = IR::implicitDerefInstruction(frb.(IR::EvalInstruction).getExpr())
|
base = accessPath(frb) and
f = fr.getField() and
@@ -61,9 +61,9 @@ private IR::Instruction fieldAccessPathAux(TVariableWithFields base, Field f) {
* by variable with fields value `base`.
*/
private IR::WriteTarget fieldWriteAccessPathAux(TVariableWithFields base, Field f) {
exists(IR::FieldTarget ft, IR::EvalInstruction ftb |
exists(IR::FieldTarget ft, IR::Instruction ftb |
ft.getBase() = ftb or
ft.getBase() = IR::implicitDerefInstruction(ftb.getExpr())
ft.getBase() = IR::implicitDerefInstruction(ftb.(IR::EvalInstruction).getExpr())
|
base = accessPath(ftb) and
ft.getField() = f and

View File

@@ -48,6 +48,47 @@ private predicate isCond(Expr e) {
e = any(ParenExpr par | isCond(par)).getExpr()
}
/**
* Holds if `e` implicitly reads the embedded field `implicitField`.
*
* The `index` is the distance from the promoted field. For example, if `A` contains an embedded
* field `B`, `B` contains an embedded field `C` and `C` contains the non-embedded field `x`.
* Then `a.x` implicitly reads `C` with index 1 and `B` with index 2.
*/
private predicate implicitFieldSelectionForField(PromotedSelector e, int index, Field implicitField) {
exists(StructType baseType, PromotedField child, int implicitFieldDepth |
baseType = e.getSelectedStructType() and
(
e.refersTo(child)
or
implicitFieldSelectionForField(e, implicitFieldDepth + 1, child)
)
|
child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and
exists(PromotedField explicitField, int explicitFieldDepth |
e.refersTo(explicitField) and baseType.getFieldAtDepth(_, explicitFieldDepth) = explicitField
|
index = explicitFieldDepth - implicitFieldDepth
)
)
}
private predicate implicitFieldSelectionForMethod(PromotedSelector e, int index, Field implicitField) {
exists(StructType baseType, PromotedMethod method, int mDepth, int implicitFieldDepth |
baseType = e.getSelectedStructType() and
e.refersTo(method) and
baseType.getMethodAtDepth(_, mDepth) = method and
index = mDepth - implicitFieldDepth
|
method = baseType.getMethodOfEmbedded(implicitField, _, implicitFieldDepth + 1)
or
exists(PromotedField child |
child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and
implicitFieldSelectionForMethod(e, implicitFieldDepth + 1, child)
)
)
}
/**
* A node in the intra-procedural control-flow graph of a Go function or file.
*
@@ -301,6 +342,17 @@ newtype TControlFlowNode =
e = any(SliceExpr se).getBase()
)
} or
/**
* A control-flow node that represents the implicit selection of a field when
* accessing a promoted field.
*
* If that field has a pointer type then this control-flow node also
* represents an implicit dereference of it.
*/
MkImplicitFieldSelection(PromotedSelector e, int i, Field implicitField) {
implicitFieldSelectionForField(e, i, implicitField) or
implicitFieldSelectionForMethod(e, i, implicitField)
} or
/**
* A control-flow node that represents the start of the execution of a function or file.
*/
@@ -1692,9 +1744,9 @@ module CFG {
}
private class SelectorExprTree extends ControlFlowTree, SelectorExpr {
SelectorExprTree() { getBase() instanceof ValueExpr }
SelectorExprTree() { this.getBase() instanceof ValueExpr }
override predicate firstNode(ControlFlow::Node first) { firstNode(getBase(), first) }
override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) }
override predicate lastNode(ControlFlow::Node last, Completion cmpl) {
ControlFlowTree.super.lastNode(last, cmpl)
@@ -1708,16 +1760,29 @@ module CFG {
}
override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) {
lastNode(getBase(), pred, normalCompletion()) and
(
succ = MkImplicitDeref(this.getBase())
or
not exists(MkImplicitDeref(this.getBase())) and
succ = mkExprOrSkipNode(this)
)
exists(int i | pred = this.getStepWithRank(i) and succ = this.getStepWithRank(i + 1))
}
private ControlFlow::Node getStepOrdered(int i) {
i = -2 and lastNode(this.getBase(), result, normalCompletion())
or
pred = MkImplicitDeref(this.getBase()) and
succ = mkExprOrSkipNode(this)
i = -1 and result = MkImplicitDeref(this.getBase())
or
exists(int maxIndex |
maxIndex = max(int k | k = 0 or exists(MkImplicitFieldSelection(this, k, _)))
|
result = MkImplicitFieldSelection(this, maxIndex - i, _)
or
i = maxIndex and
result = mkExprOrSkipNode(this)
)
}
private ControlFlow::Node getStepWithRank(int i) {
exists(int j |
result = this.getStepOrdered(j) and
j = rank[i + 1](int k | exists(this.getStepOrdered(k)))
)
}
}

View File

@@ -48,14 +48,15 @@ module IR {
this instanceof MkImplicitLowerSliceBound or
this instanceof MkImplicitUpperSliceBound or
this instanceof MkImplicitMaxSliceBound or
this instanceof MkImplicitDeref
this instanceof MkImplicitDeref or
this instanceof MkImplicitFieldSelection
}
/** Holds if this instruction reads the value of variable or constant `v`. */
predicate reads(ValueEntity v) { readsField(_, v) or readsMethod(_, v) }
predicate reads(ValueEntity v) { this.readsField(_, v) or this.readsMethod(_, v) }
/** Holds if this instruction updates variable or constant `v` to the value of `rhs`. */
predicate writes(ValueEntity v, Instruction rhs) { writesField(_, v, rhs) }
predicate writes(ValueEntity v, Instruction rhs) { this.writesField(_, v, rhs) }
/** Holds if this instruction reads the value of field `f` on the value of `base`. */
predicate readsField(Instruction base, Field f) { none() }
@@ -173,6 +174,8 @@ module IR {
this instanceof MkImplicitMaxSliceBound and result = "implicit maximum"
or
this instanceof MkImplicitDeref and result = "implicit dereference"
or
this instanceof MkImplicitFieldSelection and result = "implicit field selection"
}
}
@@ -230,20 +233,30 @@ module IR {
)
or
this instanceof ReadResultInstruction
or
this instanceof MkImplicitFieldSelection
}
}
/**
* Gets the effective base of a selector, index or slice expression, taking implicit dereferences
* into account.
* and implicit field reads into account.
*
* For a selector expression `b.f`, this will either be the implicit dereference `*b`, or just
* `b` if there is no implicit dereferencing.
* For a selector expression `b.f`, this could be the implicit dereference `*b`, or the implicit
* field access `b.Embedded` if the field `f` is promoted from an embedded type `Embedded`, or a
* combination of both `*(b.Embedded)`, or simply `b` if neither case applies.
*/
private Instruction selectorBase(Expr e) {
exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1 |
result = fri
)
or
not exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1) and
exists(Expr base |
base = e.(SelectorExpr).getBase() or
base = e.(IndexExpr).getBase() or
base = e.(SelectorExpr).getBase()
or
base = e.(IndexExpr).getBase()
or
base = e.(SliceExpr).getBase()
|
result = MkImplicitDeref(base)
@@ -258,16 +271,24 @@ module IR {
*
* This is either a field of a struct, or an element of an array, map, slice or string.
*/
class ComponentReadInstruction extends ReadInstruction, EvalInstruction {
class ComponentReadInstruction extends ReadInstruction {
ComponentReadInstruction() {
e instanceof IndexExpr
exists(Expr e | e = this.(EvalInstruction).getExpr() |
e instanceof IndexExpr
or
e.(SelectorExpr).getBase() instanceof ValueExpr and
not e.(SelectorExpr).getSelector() = any(Method method).getAReference()
)
or
e.(SelectorExpr).getBase() instanceof ValueExpr and
not e.(SelectorExpr).getSelector() = any(Method method).getAReference()
this instanceof MkImplicitFieldSelection
}
/** Gets the instruction computing the base value on which the field or element is read. */
Instruction getBase() { result = selectorBase(e) }
Instruction getBase() {
result = this.(ImplicitFieldReadInstruction).getBaseInstruction()
or
result = selectorBase(this.(EvalInstruction).getExpr())
}
}
/**
@@ -277,12 +298,77 @@ module IR {
* misclassified as field reads.
*/
class FieldReadInstruction extends ComponentReadInstruction {
override SelectorExpr e;
SelectorExpr e;
int index;
Field field;
FieldReadInstruction() {
e = this.(EvalInstruction).getExpr() and
index = 0 and
field.getAReference() = e.getSelector()
or
this = MkImplicitFieldSelection(e, index, field)
}
/** Gets the `SelectorExpr` of this field read. */
SelectorExpr getSelectorExpr() { result = e }
/** Gets the index of this field read. */
int getIndex() { result = index }
/** Gets the field being read. */
Field getField() { e.getSelector() = result.getAReference() }
Field getField() { result = field }
override predicate readsField(Instruction base, Field f) { base = getBase() and f = getField() }
Instruction getBaseInstruction() {
exists(ImplicitFieldReadInstruction fri |
fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1)
|
result = fri
)
or
not exists(ImplicitFieldReadInstruction fri |
fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1)
) and
(
result = MkImplicitDeref(e.getBase())
or
not exists(MkImplicitDeref(e.getBase())) and
result = evalExprInstruction(e.getBase())
)
}
override predicate readsField(Instruction base, Field f) {
base = this.getBaseInstruction() and f = field
}
}
/**
* An IR instruction for an implicit field read as part of reading a
* promoted field.
*
* If the field that is being implicitly read has a pointer type then this
* instruction represents an implicit dereference of it.
*/
class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection {
ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) }
override predicate reads(ValueEntity v) { v = field }
override Type getResultType() {
if field.getType() instanceof PointerType
then result = field.getType().(PointerType).getBaseType()
else result = field.getType()
}
override ControlFlow::Root getRoot() { result.isRootOf(e) }
override string toString() { result = "implicit read of field " + field.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
e.getBase().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -308,7 +394,7 @@ module IR {
/**
* An IR instruction that reads an element of an array, slice, map or string.
*/
class ElementReadInstruction extends ComponentReadInstruction {
class ElementReadInstruction extends ComponentReadInstruction, EvalInstruction {
override IndexExpr e;
/** Gets the instruction computing the index of the element being looked up. */
@@ -1395,7 +1481,7 @@ module IR {
}
/** Get the type of the base of this field access, that is, the type that contains the field. */
Type getBaseType() { result = this.getBase().(EvalInstruction).getExpr().getType() }
Type getBaseType() { result = this.getBase().getResultType() }
override predicate refersTo(ValueEntity e) {
exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.uses(e))

View File

@@ -319,9 +319,9 @@ private TSsaWithFields accessPath(IR::Instruction insn) {
* by ssa-with-fields value `base`.
*/
private IR::Instruction accessPathAux(TSsaWithFields base, Field f) {
exists(IR::FieldReadInstruction fr, IR::EvalInstruction frb |
exists(IR::FieldReadInstruction fr, IR::Instruction frb |
fr.getBase() = frb or
fr.getBase() = IR::implicitDerefInstruction(frb.getExpr())
fr.getBase() = IR::implicitDerefInstruction(frb.(IR::EvalInstruction).getExpr())
|
base = accessPath(frb) and
f = fr.getField() and

View File

@@ -279,6 +279,11 @@ module Revel {
override DataFlow::Node getADataArgument() { result = this.getArgumentVariable().getAUse() }
}
private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) {
result = insn or
result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase())
}
/** A call to `Controller.Render`. */
private class ControllerRender extends TemplateRender, DataFlow::MethodCallNode {
ControllerRender() { this.getTarget().hasQualifiedName(packagePath(), "Controller", "Render") }
@@ -286,8 +291,9 @@ module Revel {
override DataFlow::Node getTemplateArgument() { none() }
override File getRenderedFile() {
exists(string controllerRe, string handlerRe, string pathRe |
controllerRe = "\\Q" + this.getReceiver().getType().getName() + "\\E" and
exists(Type controllerType, string controllerRe, string handlerRe, string pathRe |
controllerType = skipImplicitFieldReads(this.getReceiver().asInstruction()).getResultType() and
controllerRe = "\\Q" + controllerType.getName() + "\\E" and
handlerRe = "\\Q" + this.getEnclosingCallable().getName() + "\\E" and
// find a file named '/views/<controller>/<handler>(.<template type>).html
pathRe = "/views/" + controllerRe + "/" + handlerRe + "(\\..*)?\\.html?"

View File

@@ -2,11 +2,16 @@
| cyclic.go:8:3:8:3 | u | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| cyclic.go:9:2:9:2 | f | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| cyclic.go:13:2:13:2 | t | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| cyclic.go:17:2:17:2 | s | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:6:2:6:2 | b | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:7:2:7:2 | c | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:11:2:11:2 | f | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:15:2:15:2 | d | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:19:2:19:2 | f | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| embedded.go:4:2:4:2 | A | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| embedded.go:8:3:8:5 | Baz | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| embedded.go:13:2:13:4 | Qux | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| embedded.go:14:2:14:4 | Baz | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| pkg1/embedding.go:19:23:19:26 | base | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 |
| pkg1/embedding.go:22:27:22:30 | base | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 |
| pkg1/embedding.go:25:24:25:31 | embedder | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 |

View File

@@ -5,6 +5,7 @@
| cyclic.go:9:2:9:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.u | f |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.t | t |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.u | t |
| cyclic.go:17:2:17:2 | s | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.v | s |
| depth.go:6:2:6:2 | b | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | b |
| depth.go:7:2:7:2 | c | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | c |
| depth.go:11:2:11:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | f |
@@ -13,6 +14,11 @@
| depth.go:15:2:15:2 | d | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.c | d |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.c | f |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.d | f |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Baz | A |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Qux | A |
| embedded.go:8:3:8:5 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Qux | Baz |
| embedded.go:13:2:13:4 | Qux | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.EmbedsBaz | Qux |
| embedded.go:14:2:14:4 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.EmbedsBaz | Baz |
| pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder | base |
| pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder2 | base |
| pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder3 | base |

View File

@@ -5,6 +5,7 @@
| cyclic.go:9:2:9:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | u | f |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | t | t |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | u | t |
| cyclic.go:17:2:17:2 | s | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | v | s |
| depth.go:6:2:6:2 | b | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | a | b |
| depth.go:7:2:7:2 | c | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | a | c |
| depth.go:11:2:11:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | a | f |
@@ -13,6 +14,11 @@
| depth.go:15:2:15:2 | d | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | c | d |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | c | f |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | d | f |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | Baz | A |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | Qux | A |
| embedded.go:8:3:8:5 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | Qux | Baz |
| embedded.go:13:2:13:4 | Qux | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | EmbedsBaz | Qux |
| embedded.go:14:2:14:4 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | EmbedsBaz | Baz |
| pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder | base |
| pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder2 | base |
| pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder3 | base |

View File

@@ -16,5 +16,6 @@
| embedder | f | func() int |
| embedder2 | f | func() int |
| embedder3 | f | func() int |
| embedder4 | f | func() int |
| ptrembedder | f | func() int |
| ptrembedder | g | func() int |

View File

@@ -5,9 +5,12 @@
| AExtended | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.AExtended |
| B | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.B |
| Bar | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.Bar |
| Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Baz |
| C | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.C |
| EmbedsBaz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.EmbedsBaz |
| Foo | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.Foo |
| G | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg2.G |
| Qux | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Qux |
| T | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.T |
| T | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg2.T |
| T2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.T2 |
@@ -26,3 +29,4 @@
| s | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.s |
| t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.t |
| u | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.u |
| v | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.v |

View File

@@ -1,8 +1,13 @@
| Bar | pkg1/tst.go:29:10:31:1 | struct type | flag | bool |
| Baz | embedded.go:3:10:5:1 | struct type | A | string |
| EmbedsBaz | embedded.go:12:16:15:1 | struct type | Baz | string |
| EmbedsBaz | embedded.go:12:16:15:1 | struct type | Qux | Qux |
| Foo | pkg1/tst.go:24:10:27:1 | struct type | flag | bool |
| Foo | pkg1/tst.go:24:10:27:1 | struct type | val | int |
| G | pkg2/tst.go:3:8:5:1 | struct type | g | int |
| G | pkg2/tst.go:7:8:9:1 | struct type | g | int |
| Qux | embedded.go:7:10:9:1 | struct type | A | string |
| Qux | embedded.go:7:10:9:1 | struct type | Baz | * Baz |
| T | pkg1/tst.go:3:8:7:1 | struct type | Bar | Bar |
| T | pkg1/tst.go:3:8:7:1 | struct type | Foo | Foo |
| T | pkg1/tst.go:3:8:7:1 | struct type | f | int |
@@ -46,3 +51,4 @@
| u | cyclic.go:12:8:14:1 | struct type | f | int |
| u | cyclic.go:12:8:14:1 | struct type | t | t |
| u | cyclic.go:12:8:14:1 | struct type | u | * u |
| v | cyclic.go:16:8:18:1 | struct type | s | s |

View File

@@ -5,9 +5,12 @@
| AExtended | AExtended |
| B | B |
| Bar | Bar |
| Baz | Baz |
| C | C |
| EmbedsBaz | EmbedsBaz |
| Foo | Foo |
| G | G |
| Qux | Qux |
| T | T |
| T | T |
| T2 | T2 |
@@ -26,3 +29,4 @@
| s | s |
| t | t |
| u | u |
| v | v |

View File

@@ -12,3 +12,12 @@ type t struct {
type u struct {
t
}
type v struct {
s
}
// the below will cause the test to not terminate
// type w struct {
// v
// }

View File

@@ -0,0 +1,15 @@
package main
type Baz struct {
A string
}
type Qux struct {
*Baz
}
// EmbedsBaz should have a field A but does not
type EmbedsBaz struct {
Qux
Baz string
}

View File

@@ -0,0 +1,38 @@
import go
import TestUtilities.InlineExpectationsTest
class SourceFunction extends Function {
SourceFunction() { this.getName() = "source" }
}
class SinkFunction extends Function {
SinkFunction() { this.getName() = "sink" }
}
class TestConfig extends DataFlow::Configuration {
TestConfig() { this = "testconfig" }
override predicate isSource(DataFlow::Node source) {
source = any(SourceFunction f).getACall().getAResult()
}
override predicate isSink(DataFlow::Node sink) {
sink = any(SinkFunction f).getACall().getAnArgument()
}
}
class PromotedFieldsTest extends InlineExpectationsTest {
PromotedFieldsTest() { this = "PromotedFieldsTest" }
override string getARelevantTag() { result = "promotedfields" }
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
exists(TestConfig config, DataFlow::PathNode source, DataFlow::PathNode sink |
config.hasFlowPath(source, sink) and
sink.hasLocationInfo(file, line, _, _, _) and
element = sink.toString() and
value = "" and
tag = "promotedfields"
)
}
}

View File

@@ -0,0 +1,184 @@
| main.go:3:6:3:11 | function source | main.go:23:31:23:36 | source |
| main.go:3:6:3:11 | function source | main.go:31:31:31:36 | source |
| main.go:3:6:3:11 | function source | main.go:40:30:40:35 | source |
| main.go:3:6:3:11 | function source | main.go:46:32:46:37 | source |
| main.go:3:6:3:11 | function source | main.go:54:17:54:22 | source |
| main.go:3:6:3:11 | function source | main.go:62:18:62:23 | source |
| main.go:3:6:3:11 | function source | main.go:72:17:72:22 | source |
| main.go:3:6:3:11 | function source | main.go:80:18:80:23 | source |
| main.go:3:6:3:11 | function source | main.go:91:16:91:21 | source |
| main.go:3:6:3:11 | function source | main.go:98:17:98:22 | source |
| main.go:3:6:3:11 | function source | main.go:107:22:107:27 | source |
| main.go:3:6:3:11 | function source | main.go:114:23:114:28 | source |
| main.go:3:6:3:11 | function source | main.go:123:23:123:28 | source |
| main.go:3:6:3:11 | function source | main.go:130:24:130:29 | source |
| main.go:3:6:3:11 | function source | main.go:139:29:139:34 | source |
| main.go:3:6:3:11 | function source | main.go:146:30:146:35 | source |
| main.go:7:6:7:9 | function sink | main.go:25:2:25:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:26:2:26:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:27:2:27:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:28:2:28:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:33:2:33:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:34:2:34:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:35:2:35:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:36:2:36:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:41:2:41:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:42:2:42:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:43:2:43:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:44:2:44:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:47:2:47:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:48:2:48:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:49:2:49:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:50:2:50:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:57:2:57:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:58:2:58:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:59:2:59:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:60:2:60:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:65:2:65:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:66:2:66:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:67:2:67:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:68:2:68:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:75:2:75:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:76:2:76:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:77:2:77:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:78:2:78:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:83:2:83:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:84:2:84:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:85:2:85:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:86:2:86:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:92:2:92:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:93:2:93:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:94:2:94:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:95:2:95:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:99:2:99:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:100:2:100:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:101:2:101:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:102:2:102:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:108:2:108:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:109:2:109:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:110:2:110:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:111:2:111:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:115:2:115:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:116:2:116:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:117:2:117:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:118:2:118:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:124:2:124:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:125:2:125:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:126:2:126:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:127:2:127:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:131:2:131:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:132:2:132:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:133:2:133:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:134:2:134:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:140:2:140:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:141:2:141:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:142:2:142:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:143:2:143:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:147:2:147:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:148:2:148:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:149:2:149:5 | sink |
| main.go:7:6:7:9 | function sink | main.go:150:2:150:5 | sink |
| main.go:22:2:22:6 | definition of outer | main.go:25:7:25:11 | outer |
| main.go:22:2:22:6 | definition of outer | main.go:26:7:26:11 | outer |
| main.go:22:2:22:6 | definition of outer | main.go:27:7:27:11 | outer |
| main.go:22:2:22:6 | definition of outer | main.go:28:7:28:11 | outer |
| main.go:22:11:24:2 | struct literal | main.go:22:2:22:6 | definition of outer |
| main.go:30:2:30:7 | definition of outerp | main.go:33:7:33:12 | outerp |
| main.go:30:2:30:7 | definition of outerp | main.go:34:7:34:12 | outerp |
| main.go:30:2:30:7 | definition of outerp | main.go:35:7:35:12 | outerp |
| main.go:30:2:30:7 | definition of outerp | main.go:36:7:36:12 | outerp |
| main.go:30:12:32:2 | &... | main.go:30:2:30:7 | definition of outerp |
| main.go:40:2:40:6 | definition of outer | main.go:41:7:41:11 | outer |
| main.go:40:2:40:6 | definition of outer | main.go:42:7:42:11 | outer |
| main.go:40:2:40:6 | definition of outer | main.go:43:7:43:11 | outer |
| main.go:40:2:40:6 | definition of outer | main.go:44:7:44:11 | outer |
| main.go:40:11:40:40 | struct literal | main.go:40:2:40:6 | definition of outer |
| main.go:46:2:46:7 | definition of outerp | main.go:47:7:47:12 | outerp |
| main.go:46:2:46:7 | definition of outerp | main.go:48:7:48:12 | outerp |
| main.go:46:2:46:7 | definition of outerp | main.go:49:7:49:12 | outerp |
| main.go:46:2:46:7 | definition of outerp | main.go:50:7:50:12 | outerp |
| main.go:46:12:46:42 | &... | main.go:46:2:46:7 | definition of outerp |
| main.go:54:2:54:6 | definition of inner | main.go:55:19:55:23 | inner |
| main.go:54:11:54:25 | struct literal | main.go:54:2:54:6 | definition of inner |
| main.go:55:2:55:7 | definition of middle | main.go:56:17:56:22 | middle |
| main.go:55:12:55:24 | struct literal | main.go:55:2:55:7 | definition of middle |
| main.go:56:2:56:6 | definition of outer | main.go:57:7:57:11 | outer |
| main.go:56:2:56:6 | definition of outer | main.go:58:7:58:11 | outer |
| main.go:56:2:56:6 | definition of outer | main.go:59:7:59:11 | outer |
| main.go:56:2:56:6 | definition of outer | main.go:60:7:60:11 | outer |
| main.go:56:11:56:23 | struct literal | main.go:56:2:56:6 | definition of outer |
| main.go:62:2:62:7 | definition of innerp | main.go:63:20:63:25 | innerp |
| main.go:62:12:62:26 | struct literal | main.go:62:2:62:7 | definition of innerp |
| main.go:63:2:63:8 | definition of middlep | main.go:64:18:64:24 | middlep |
| main.go:63:13:63:26 | struct literal | main.go:63:2:63:8 | definition of middlep |
| main.go:64:2:64:7 | definition of outerp | main.go:65:7:65:12 | outerp |
| main.go:64:2:64:7 | definition of outerp | main.go:66:7:66:12 | outerp |
| main.go:64:2:64:7 | definition of outerp | main.go:67:7:67:12 | outerp |
| main.go:64:2:64:7 | definition of outerp | main.go:68:7:68:12 | outerp |
| main.go:64:12:64:25 | struct literal | main.go:64:2:64:7 | definition of outerp |
| main.go:72:2:72:6 | definition of inner | main.go:73:26:73:30 | inner |
| main.go:72:11:72:25 | struct literal | main.go:72:2:72:6 | definition of inner |
| main.go:73:2:73:7 | definition of middle | main.go:74:25:74:30 | middle |
| main.go:73:12:73:31 | struct literal | main.go:73:2:73:7 | definition of middle |
| main.go:74:2:74:6 | definition of outer | main.go:75:7:75:11 | outer |
| main.go:74:2:74:6 | definition of outer | main.go:76:7:76:11 | outer |
| main.go:74:2:74:6 | definition of outer | main.go:77:7:77:11 | outer |
| main.go:74:2:74:6 | definition of outer | main.go:78:7:78:11 | outer |
| main.go:74:11:74:31 | struct literal | main.go:74:2:74:6 | definition of outer |
| main.go:80:2:80:7 | definition of innerp | main.go:81:27:81:32 | innerp |
| main.go:80:12:80:26 | struct literal | main.go:80:2:80:7 | definition of innerp |
| main.go:81:2:81:8 | definition of middlep | main.go:82:26:82:32 | middlep |
| main.go:81:13:81:33 | struct literal | main.go:81:2:81:8 | definition of middlep |
| main.go:82:2:82:7 | definition of outerp | main.go:83:7:83:12 | outerp |
| main.go:82:2:82:7 | definition of outerp | main.go:84:7:84:12 | outerp |
| main.go:82:2:82:7 | definition of outerp | main.go:85:7:85:12 | outerp |
| main.go:82:2:82:7 | definition of outerp | main.go:86:7:86:12 | outerp |
| main.go:82:12:82:33 | struct literal | main.go:82:2:82:7 | definition of outerp |
| main.go:90:6:90:10 | definition of outer | main.go:91:2:91:6 | outer |
| main.go:90:6:90:10 | definition of outer | main.go:92:7:92:11 | outer |
| main.go:90:6:90:10 | definition of outer | main.go:93:7:93:11 | outer |
| main.go:90:6:90:10 | definition of outer | main.go:94:7:94:11 | outer |
| main.go:90:6:90:10 | definition of outer | main.go:95:7:95:11 | outer |
| main.go:90:6:90:10 | zero value for outer | main.go:90:6:90:10 | definition of outer |
| main.go:97:6:97:11 | definition of outerp | main.go:98:2:98:7 | outerp |
| main.go:97:6:97:11 | definition of outerp | main.go:99:7:99:12 | outerp |
| main.go:97:6:97:11 | definition of outerp | main.go:100:7:100:12 | outerp |
| main.go:97:6:97:11 | definition of outerp | main.go:101:7:101:12 | outerp |
| main.go:97:6:97:11 | definition of outerp | main.go:102:7:102:12 | outerp |
| main.go:97:6:97:11 | zero value for outerp | main.go:97:6:97:11 | definition of outerp |
| main.go:106:6:106:10 | definition of outer | main.go:107:2:107:6 | outer |
| main.go:106:6:106:10 | definition of outer | main.go:108:7:108:11 | outer |
| main.go:106:6:106:10 | definition of outer | main.go:109:7:109:11 | outer |
| main.go:106:6:106:10 | definition of outer | main.go:110:7:110:11 | outer |
| main.go:106:6:106:10 | definition of outer | main.go:111:7:111:11 | outer |
| main.go:106:6:106:10 | zero value for outer | main.go:106:6:106:10 | definition of outer |
| main.go:113:6:113:11 | definition of outerp | main.go:114:2:114:7 | outerp |
| main.go:113:6:113:11 | definition of outerp | main.go:115:7:115:12 | outerp |
| main.go:113:6:113:11 | definition of outerp | main.go:116:7:116:12 | outerp |
| main.go:113:6:113:11 | definition of outerp | main.go:117:7:117:12 | outerp |
| main.go:113:6:113:11 | definition of outerp | main.go:118:7:118:12 | outerp |
| main.go:113:6:113:11 | zero value for outerp | main.go:113:6:113:11 | definition of outerp |
| main.go:122:6:122:10 | definition of outer | main.go:123:2:123:6 | outer |
| main.go:122:6:122:10 | definition of outer | main.go:124:7:124:11 | outer |
| main.go:122:6:122:10 | definition of outer | main.go:125:7:125:11 | outer |
| main.go:122:6:122:10 | definition of outer | main.go:126:7:126:11 | outer |
| main.go:122:6:122:10 | definition of outer | main.go:127:7:127:11 | outer |
| main.go:122:6:122:10 | zero value for outer | main.go:122:6:122:10 | definition of outer |
| main.go:129:6:129:11 | definition of outerp | main.go:130:2:130:7 | outerp |
| main.go:129:6:129:11 | definition of outerp | main.go:131:7:131:12 | outerp |
| main.go:129:6:129:11 | definition of outerp | main.go:132:7:132:12 | outerp |
| main.go:129:6:129:11 | definition of outerp | main.go:133:7:133:12 | outerp |
| main.go:129:6:129:11 | definition of outerp | main.go:134:7:134:12 | outerp |
| main.go:129:6:129:11 | zero value for outerp | main.go:129:6:129:11 | definition of outerp |
| main.go:138:6:138:10 | definition of outer | main.go:139:2:139:6 | outer |
| main.go:138:6:138:10 | definition of outer | main.go:140:7:140:11 | outer |
| main.go:138:6:138:10 | definition of outer | main.go:141:7:141:11 | outer |
| main.go:138:6:138:10 | definition of outer | main.go:142:7:142:11 | outer |
| main.go:138:6:138:10 | definition of outer | main.go:143:7:143:11 | outer |
| main.go:138:6:138:10 | zero value for outer | main.go:138:6:138:10 | definition of outer |
| main.go:145:6:145:11 | definition of outerp | main.go:146:2:146:7 | outerp |
| main.go:145:6:145:11 | definition of outerp | main.go:147:7:147:12 | outerp |
| main.go:145:6:145:11 | definition of outerp | main.go:148:7:148:12 | outerp |
| main.go:145:6:145:11 | definition of outerp | main.go:149:7:149:12 | outerp |
| main.go:145:6:145:11 | definition of outerp | main.go:150:7:150:12 | outerp |
| main.go:145:6:145:11 | zero value for outerp | main.go:145:6:145:11 | definition of outerp |

View File

@@ -0,0 +1,5 @@
import go
from DataFlow::Node nd, DataFlow::Node succ
where DataFlow::localFlowStep(nd, succ)
select nd, succ

View File

@@ -0,0 +1,151 @@
package main
func source() string {
return "hello world"
}
func sink(s string) {}
type Inner struct {
field string
}
type Middle struct {
Inner
}
type Outer struct {
Middle
}
func testPromotedFieldNamedInitialization() {
outer := Outer{
Middle: Middle{Inner: Inner{source()}},
}
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
outerp := &Outer{
Middle: Middle{Inner: Inner{source()}},
}
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldUnnamedInitialization() {
outer := Outer{Middle{Inner{source()}}}
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
outerp := &Outer{Middle{Inner{source()}}}
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldUnnamedInitializationFromVariable() {
inner := Inner{source()}
middle := Middle{inner}
outer := Outer{middle}
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
innerp := Inner{source()}
middlep := Middle{innerp}
outerp := Outer{middlep}
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldNamedInitializationFromVariable() {
inner := Inner{source()}
middle := Middle{Inner: inner}
outer := Outer{Middle: middle}
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
innerp := Inner{source()}
middlep := Middle{Inner: innerp}
outerp := Outer{Middle: middlep}
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldDirectAssignment() {
var outer Outer
outer.field = source()
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
var outerp Outer
outerp.field = source()
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldIndirectAssignment1() {
var outer Outer
outer.Inner.field = source()
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
var outerp Outer
outerp.Inner.field = source()
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldIndirectAssignment2() {
var outer Outer
outer.Middle.field = source()
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
var outerp Outer
outerp.Middle.field = source()
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}
func testPromotedFieldIndirectAssignment3() {
var outer Outer
outer.Middle.Inner.field = source()
sink(outer.field) // $promotedfields
sink(outer.Inner.field) // $promotedfields
sink(outer.Middle.field) // $promotedfields
sink(outer.Middle.Inner.field) // $promotedfields
var outerp Outer
outerp.Middle.Inner.field = source()
sink(outerp.field) // $promotedfields
sink(outerp.Inner.field) // $promotedfields
sink(outerp.Middle.field) // $promotedfields
sink(outerp.Middle.Inner.field) // $promotedfields
}

View File

@@ -0,0 +1,39 @@
import go
import TestUtilities.InlineExpectationsTest
class SourceFunction extends Function {
SourceFunction() { this.getName() = "source" }
}
class SinkFunction extends Function {
SinkFunction() { this.getName() = "sink" }
}
class TestConfig extends DataFlow::Configuration {
TestConfig() { this = "testconfig" }
override predicate isSource(DataFlow::Node source) {
source = any(SourceFunction f).getACall().getAResult()
}
override predicate isSink(DataFlow::Node sink) {
sink = any(SinkFunction f).getACall().getAnArgument()
}
}
class PromotedMethodsTest extends InlineExpectationsTest {
PromotedMethodsTest() { this = "PromotedMethodsTest" }
override string getARelevantTag() { result = "promotedmethods" }
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
exists(TestConfig config, DataFlow::Node source, DataFlow::Node sink |
config.hasFlow(source, sink)
|
sink.hasLocationInfo(file, line, _, _, _) and
element = sink.toString() and
value = source.getEnclosingCallable().getName() and
tag = "promotedmethods"
)
}
}

View File

@@ -0,0 +1,81 @@
package main
func source() string {
return "hello world"
}
func sink(s string) {}
type Embedded struct {
field string
}
type Base1 struct {
Embedded
}
type Base2 struct {
*Embedded
}
func (e Embedded) sinkFieldOnEmbeddedNonPointerReceiver() {
sink(e.field) // $promotedmethods=nonPointerSender1 $promotedmethods=pointerSender1 $promotedmethods=nonPointerSender2 $promotedmethods=pointerSender2
}
func (e *Embedded) sinkFieldOnEmbeddedPointerReceiver() {
sink(e.field) // $f-:promotedmethods=nonPointerSender1 $f-:promotedmethods=pointerSender1 $f-:promotedmethods=nonPointerSender2 $f-:promotedmethods=pointerSender2
}
func (base1 Base1) sinkFieldOnBase1NonPointerReceiver() {
sink(base1.field) // $promotedmethods=nonPointerSender1 $promotedmethods=pointerSender1
}
func (base1 *Base1) sinkFieldOnBase1PointerReceiver() {
sink(base1.field) // $f-:promotedmethods=nonPointerSender1 $promotedmethods=pointerSender1
}
func (base2 Base2) sinkFieldOnBase2NonPointerReceiver() {
sink(base2.field) // $promotedmethods=nonPointerSender2 $promotedmethods=pointerSender2
}
func (base2 *Base2) sinkFieldOnBase2PointerReceiver() {
sink(base2.field) // $f-:promotedmethods=nonPointerSender2 $promotedmethods=pointerSender2
}
func nonPointerSender1() {
var base1 Base1
base1.field = source()
base1.sinkFieldOnEmbeddedNonPointerReceiver()
base1.sinkFieldOnEmbeddedPointerReceiver()
base1.sinkFieldOnBase1NonPointerReceiver()
base1.sinkFieldOnBase1PointerReceiver()
}
func pointerSender1() {
var base1 Base1
base1.field = source()
base1p := &base1
base1p.sinkFieldOnEmbeddedNonPointerReceiver()
base1p.sinkFieldOnEmbeddedPointerReceiver()
base1p.sinkFieldOnBase1NonPointerReceiver()
base1p.sinkFieldOnBase1PointerReceiver()
}
func nonPointerSender2() {
var base2 Base2
base2.field = source()
base2.sinkFieldOnEmbeddedNonPointerReceiver()
base2.sinkFieldOnEmbeddedPointerReceiver()
base2.sinkFieldOnBase2NonPointerReceiver()
base2.sinkFieldOnBase2PointerReceiver()
}
func pointerSender2() {
var base2 Base2
base2.field = source()
base2p := &base2
base2p.sinkFieldOnEmbeddedNonPointerReceiver()
base2p.sinkFieldOnEmbeddedPointerReceiver()
base2p.sinkFieldOnBase2NonPointerReceiver()
base2p.sinkFieldOnBase2PointerReceiver()
}

View File

@@ -15,17 +15,23 @@ edges
| UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string |
| UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate : string |
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | ZipSlip.go:12:24:12:24 | implicit dereference : File |
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader |
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | ZipSlip.go:14:20:14:20 | p |
| ZipSlip.go:12:24:12:24 | implicit dereference : File | ZipSlip.go:12:24:12:24 | implicit dereference : File |
| ZipSlip.go:12:24:12:24 | implicit dereference : File | ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader |
| ZipSlip.go:12:24:12:24 | implicit dereference : File | ZipSlip.go:14:20:14:20 | p |
| ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader | ZipSlip.go:14:20:14:20 | p |
| tarslip.go:15:2:15:30 | ... := ...[0] : pointer type | tarslip.go:16:14:16:34 | call to Dir |
| tarslip.go:15:2:15:30 | ... := ...[0] : pointer type | tarslip.go:16:23:16:28 | implicit dereference : Header |
| tarslip.go:16:23:16:28 | implicit dereference : Header | tarslip.go:16:14:16:34 | call to Dir |
| tarslip.go:16:23:16:28 | implicit dereference : Header | tarslip.go:16:23:16:28 | implicit dereference : Header |
| tst.go:23:2:43:2 | range statement[1] : pointer type | tst.go:24:11:24:11 | implicit dereference : File |
| tst.go:23:2:43:2 | range statement[1] : pointer type | tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader |
| tst.go:23:2:43:2 | range statement[1] : pointer type | tst.go:29:20:29:23 | path |
| tst.go:24:11:24:11 | implicit dereference : File | tst.go:24:11:24:11 | implicit dereference : File |
| tst.go:24:11:24:11 | implicit dereference : File | tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader |
| tst.go:24:11:24:11 | implicit dereference : File | tst.go:29:20:29:23 | path |
| tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader | tst.go:29:20:29:23 | path |
nodes
| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate : string | semmle.label | definition of candidate : string |
| UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | semmle.label | call to Join |
@@ -36,12 +42,14 @@ nodes
| UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string | semmle.label | selection of Name : string |
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | semmle.label | range statement[1] : pointer type |
| ZipSlip.go:12:24:12:24 | implicit dereference : File | semmle.label | implicit dereference : File |
| ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader | semmle.label | implicit read of field FileHeader : FileHeader |
| ZipSlip.go:14:20:14:20 | p | semmle.label | p |
| tarslip.go:15:2:15:30 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
| tarslip.go:16:14:16:34 | call to Dir | semmle.label | call to Dir |
| tarslip.go:16:23:16:28 | implicit dereference : Header | semmle.label | implicit dereference : Header |
| tst.go:23:2:43:2 | range statement[1] : pointer type | semmle.label | range statement[1] : pointer type |
| tst.go:24:11:24:11 | implicit dereference : File | semmle.label | implicit dereference : File |
| tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader | semmle.label | implicit read of field FileHeader : FileHeader |
| tst.go:29:20:29:23 | path | semmle.label | path |
#select
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] : pointer type | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation |