From 2d3caf48c1c0911efaa48e1677ef5360b73a97b3 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 24 Feb 2021 16:08:08 +0000 Subject: [PATCH] Add implicit field reads for promoted fields This may not work when the embedded fields are pointer types, as we don't have anything corresponding to MkImplicitDeref --- .../go/controlflow/ControlFlowGraphImpl.qll | 43 +++- ql/src/semmle/go/controlflow/IR.qll | 105 ++++++++-- ql/src/semmle/go/dataflow/SSA.qll | 4 +- .../PromotedFields/DataFlowConfig.expected | 0 .../dataflow/PromotedFields/DataFlowConfig.ql | 38 ++++ .../PromotedFields/LocalFlowStep.expected | 184 ++++++++++++++++++ .../dataflow/PromotedFields/LocalFlowStep.ql | 5 + .../semmle/go/dataflow/PromotedFields/main.go | 151 ++++++++++++++ 8 files changed, 507 insertions(+), 23 deletions(-) create mode 100644 ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.expected create mode 100644 ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.ql create mode 100644 ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected create mode 100644 ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.ql create mode 100644 ql/test/library-tests/semmle/go/dataflow/PromotedFields/main.go diff --git a/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll b/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll index ee5270664f0..63e75655780 100644 --- a/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ b/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll @@ -301,6 +301,22 @@ 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. + */ + MkImplicitFieldSelection(SelectorExpr e, int i, Field implicitField) { + exists(Type baseType, StructType baseStructType, Field eField, int minDepth | + eField.getAReference() = e.getSelector() and + baseType = e.getBase().getType().getUnderlyingType() and + baseStructType = [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] and + baseStructType.getFieldAtDepth(_, minDepth) = eField + | + baseStructType.getFieldAtDepth(_, i) = implicitField and + implicitField.getType().getUnderlyingType().(StructType).getFieldAtDepth(_, minDepth - i - 1) = + eField + ) + } or /** * A control-flow node that represents the start of the execution of a function or file. */ @@ -1708,16 +1724,25 @@ 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 + result = MkImplicitFieldSelection(this, i, _) + or + i = max(int k | k = -1 or exists(MkImplicitFieldSelection(this, k, _))) + 1 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))) + ) } } diff --git a/ql/src/semmle/go/controlflow/IR.qll b/ql/src/semmle/go/controlflow/IR.qll index 1ce30b2593f..13536a085e8 100644 --- a/ql/src/semmle/go/controlflow/IR.qll +++ b/ql/src/semmle/go/controlflow/IR.qll @@ -48,7 +48,8 @@ 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`. */ @@ -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,6 +233,8 @@ module IR { ) or this instanceof ReadResultInstruction + or + this instanceof ImplicitFieldReadInstruction } } @@ -241,9 +246,16 @@ module IR { * `b` if there is no implicit dereferencing. */ private Instruction selectorBase(Expr e) { + exists(Field field | field.getAReference() = e.(SelectorExpr).getSelector() | + result = selectorBase(e, field) + ) + or exists(Expr base | - base = e.(SelectorExpr).getBase() or - base = e.(IndexExpr).getBase() or + base = e.(SelectorExpr).getBase() and + e.(SelectorExpr).getSelector() = any(Method m).getAReference() + or + base = e.(IndexExpr).getBase() + or base = e.(SliceExpr).getBase() | result = MkImplicitDeref(base) @@ -253,21 +265,51 @@ module IR { ) } + private Instruction selectorBase(SelectorExpr se, Field field) { + exists(Type baseType, StructType baseStructType | + baseType = se.getBase().getType().getUnderlyingType() and + baseStructType = [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] + | + if field = baseStructType.getOwnField(_, _) + then + result = MkImplicitDeref(se.getBase()) + or + not exists(MkImplicitDeref(se.getBase())) and + result = evalExprInstruction(se.getBase()) + else + exists(ImplicitFieldReadInstruction ifri | + ifri.getSelectorExpr() = se and + ifri.getField().getType().getUnderlyingType().(StructType).getOwnField(_, _) = field + | + result = ifri + ) + ) + } + /** * An IR instruction that reads a component from a composite object. * * 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 ImplicitFieldReadInstruction } /** Gets the instruction computing the base value on which the field or element is read. */ - Instruction getBase() { result = selectorBase(e) } + Instruction getBase() { + result = selectorBase(this.(EvalInstruction).getExpr()) or + result = + selectorBase(this.(ImplicitFieldReadInstruction).getSelectorExpr(), + this.(ImplicitFieldReadInstruction).getField()) + } } /** @@ -277,12 +319,51 @@ module IR { * misclassified as field reads. */ class FieldReadInstruction extends ComponentReadInstruction { - override SelectorExpr e; + SelectorExpr e; + Field field; + + FieldReadInstruction() { + e = this.(EvalInstruction).getExpr() and + field.getAReference() = this.(EvalInstruction).getExpr().(SelectorExpr).getSelector() + or + e = this.(ImplicitFieldReadInstruction).getSelectorExpr() and + field = this.(ImplicitFieldReadInstruction).getField() + } /** 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() } + override predicate readsField(Instruction base, Field f) { + base = selectorBase(e, f) and f = field + } + } + + /** + * An IR instruction for an implicit field read as part of reading a promoted field. + */ + class ImplicitFieldReadInstruction extends Instruction, MkImplicitFieldSelection { + SelectorExpr e; + Field implicitField; + + ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, _, implicitField) } + + SelectorExpr getSelectorExpr() { result = e } + + Field getField() { result = implicitField } + + override predicate reads(ValueEntity v) { v = implicitField } + + override Type getResultType() { result = implicitField.getType() } + + override ControlFlow::Root getRoot() { result.isRootOf(e) } + + override string toString() { result = "implicit read of field " + implicitField.toString() } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + e.getBase().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** @@ -308,7 +389,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. */ diff --git a/ql/src/semmle/go/dataflow/SSA.qll b/ql/src/semmle/go/dataflow/SSA.qll index 88381133c84..e847d86c72a 100644 --- a/ql/src/semmle/go/dataflow/SSA.qll +++ b/ql/src/semmle/go/dataflow/SSA.qll @@ -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 diff --git a/ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.expected b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.ql b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.ql new file mode 100644 index 00000000000..62ead1d94de --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/DataFlowConfig.ql @@ -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" + ) + } +} diff --git a/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected new file mode 100644 index 00000000000..d61d6be9c5f --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected @@ -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 | diff --git a/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.ql b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.ql new file mode 100644 index 00000000000..979c4fd6700 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.ql @@ -0,0 +1,5 @@ +import go + +from DataFlow::Node nd, DataFlow::Node succ +where DataFlow::localFlowStep(nd, succ) +select nd, succ diff --git a/ql/test/library-tests/semmle/go/dataflow/PromotedFields/main.go b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/main.go new file mode 100644 index 00000000000..97bb5eb8a2c --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/PromotedFields/main.go @@ -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 +}