diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/embedding.go b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/embedding.go new file mode 100644 index 000000000000..fd255bddb460 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/embedding.go @@ -0,0 +1,32 @@ +package test + +import ( + "test.com/basename/pkg1" + "test.com/basename/pkg2" +) + +// Tests that dataflow behaves as expected when loads and stores from a field traverse embeddings that use different type names. + +// pkg2.IntStruct is an alias for pkg1.IntStruct +// Note referring to symbols in different packages is necessary so that Go will assign the fields the same name as well as the same type-- +// for example, if we defined two aliases here named 'IntStruct1' and 'IntStruct2' and embedded them into two types, +// the types would be non-identical due to having a field implicitly named IntStruct1 and IntStruct2 respectively, +// even though syntactically it could be addressed without mentioning the field name. + +type EmbedsPkg1IntStruct = struct{ pkg1.IntStruct } +type EmbedsPkg2IntStruct = struct{ pkg2.IntStruct } + +func FEmbedded() { + + x := source() + pkg1Struct := EmbedsPkg1IntStruct{pkg1.IntStruct{x}} + + GEmbedded(&pkg1Struct) + +} + +func GEmbedded(pkg2Struct *EmbedsPkg2IntStruct) { + + sink(pkg2Struct.Field) // $ hasValueFlow="selection of Field" + +} diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/go.mod b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/go.mod new file mode 100644 index 000000000000..5fb81a66e38e --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/go.mod @@ -0,0 +1,3 @@ +module test.com/basename + +go 1.23.1 diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/pkg1/struct.go b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/pkg1/struct.go new file mode 100644 index 000000000000..a35f894bc505 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/pkg1/struct.go @@ -0,0 +1,5 @@ +package pkg1 + +type IntStruct struct { + Field int +} diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/pkg2/struct.go b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/pkg2/struct.go new file mode 100644 index 000000000000..09f1531c82d6 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/pkg2/struct.go @@ -0,0 +1,7 @@ +package pkg2 + +import ( + "test.com/basename/pkg1" +) + +type IntStruct = pkg1.IntStruct diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/simple.go b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/simple.go new file mode 100644 index 000000000000..6ed30f64ca41 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/simple.go @@ -0,0 +1,26 @@ +package test + +// Tests that dataflow behaves as expected when field loads and stores, and pointer accesses, go via +// types which are identical, but which use different aliases. + +func source() int { return 0 } +func sink(value int) {} + +type IntAlias = int +type IntStruct = struct{ field int } +type IntAliasStruct = struct{ field IntAlias } + +func F() { + + x := source() + intStruct := IntStruct{x} + + G(&intStruct) + +} + +func G(intAliasStruct *IntAliasStruct) { + + sink(intAliasStruct.field) // $ hasValueFlow="selection of field" + +} diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/test.expected b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/test.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/test.ql b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/test.ql new file mode 100644 index 000000000000..1b27b27d6dc2 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/DataflowFields/test.ql @@ -0,0 +1,3 @@ +import go +import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/flow.expected b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/flow.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/flow.ql b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/flow.ql new file mode 100644 index 000000000000..1b27b27d6dc2 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/flow.ql @@ -0,0 +1,3 @@ +import go +import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.expected b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.expected index 408e2951dc68..c39b58ab620a 100644 --- a/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.expected +++ b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.expected @@ -1,15 +1,22 @@ callTargets -| test.go:48:2:48:24 | call to ImplementMe | test.go:12:1:12:69 | function declaration | ImplementMe | -| test.go:48:2:48:24 | call to ImplementMe | test.go:17:1:17:64 | function declaration | ImplementMe | -| test.go:48:2:48:24 | call to ImplementMe | test.go:24:1:24:53 | function declaration | ImplementMe | -| test.go:48:2:48:24 | call to ImplementMe | test.go:31:1:31:59 | function declaration | ImplementMe | -| test.go:48:2:48:24 | call to ImplementMe | test.go:38:1:38:71 | function declaration | ImplementMe | -| test.go:48:2:48:24 | call to ImplementMe | test.go:45:1:45:69 | function declaration | ImplementMe | +| test.go:20:70:20:83 | call to sink | test.go:15:1:15:45 | function declaration | sink | +| test.go:25:65:25:78 | call to sink | test.go:15:1:15:45 | function declaration | sink | +| test.go:32:54:32:67 | call to sink | test.go:15:1:15:45 | function declaration | sink | +| test.go:39:60:39:73 | call to sink | test.go:15:1:15:45 | function declaration | sink | +| test.go:46:72:46:85 | call to sink | test.go:15:1:15:45 | function declaration | sink | +| test.go:53:70:53:83 | call to sink | test.go:15:1:15:45 | function declaration | sink | +| test.go:56:14:56:21 | call to source | test.go:14:1:14:57 | function declaration | source | +| test.go:57:2:57:29 | call to ImplementMe | test.go:20:1:20:85 | function declaration | ImplementMe | +| test.go:57:2:57:29 | call to ImplementMe | test.go:25:1:25:80 | function declaration | ImplementMe | +| test.go:57:2:57:29 | call to ImplementMe | test.go:32:1:32:69 | function declaration | ImplementMe | +| test.go:57:2:57:29 | call to ImplementMe | test.go:39:1:39:75 | function declaration | ImplementMe | +| test.go:57:2:57:29 | call to ImplementMe | test.go:46:1:46:87 | function declaration | ImplementMe | +| test.go:57:2:57:29 | call to ImplementMe | test.go:53:1:53:85 | function declaration | ImplementMe | #select | file://:0:0:0:0 | basic interface type | file://:0:0:0:0 | basic interface type | -| file://:0:0:0:0 | basic interface type | test.go:10:6:10:10 | Impl1 | -| file://:0:0:0:0 | basic interface type | test.go:15:6:15:10 | Impl2 | -| file://:0:0:0:0 | basic interface type | test.go:20:6:20:10 | Impl3 | -| file://:0:0:0:0 | basic interface type | test.go:27:6:27:10 | Impl4 | -| file://:0:0:0:0 | basic interface type | test.go:34:6:34:10 | Impl5 | -| file://:0:0:0:0 | basic interface type | test.go:41:6:41:10 | Impl6 | +| file://:0:0:0:0 | basic interface type | test.go:18:6:18:10 | Impl1 | +| file://:0:0:0:0 | basic interface type | test.go:23:6:23:10 | Impl2 | +| file://:0:0:0:0 | basic interface type | test.go:28:6:28:10 | Impl3 | +| file://:0:0:0:0 | basic interface type | test.go:35:6:35:10 | Impl4 | +| file://:0:0:0:0 | basic interface type | test.go:42:6:42:10 | Impl5 | +| file://:0:0:0:0 | basic interface type | test.go:49:6:49:10 | Impl6 | diff --git a/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.go b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.go index 5803c471fbfc..0d09449514d7 100644 --- a/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.go +++ b/go/ql/test/library-tests/semmle/go/aliases/InterfaceImpls/test.go @@ -1,49 +1,58 @@ package intfs +// Tests that dataflow and interface implementation behave as expected when an interface +// is implemented using an identical type (in the structural sense defined by the go spec) +// where the two types differ at surface level, i.e. aliases must be followed to determine +// that they are identical. + type IntAlias = int type Target = interface { ImplementMe(callable func(struct{ x IntAlias })) } +func source() func(struct{ x IntAlias }) { return nil } +func sink(fptr func(struct{ x IntAlias })) {} + // Simple direct implementation type Impl1 struct{} -func (recv Impl1) ImplementMe(callable func(struct{ x IntAlias })) {} +func (recv Impl1) ImplementMe(callable func(struct{ x IntAlias })) { sink(callable) } // $ hasValueFlow="callable" // Implementation via unaliasing type Impl2 struct{} -func (recv Impl2) ImplementMe(callable func(struct{ x int })) {} +func (recv Impl2) ImplementMe(callable func(struct{ x int })) { sink(callable) } // $ hasValueFlow="callable" // Implementation via top-level aliasing type Impl3 struct{} type Impl3Alias = func(struct{ x IntAlias }) -func (recv Impl3) ImplementMe(callable Impl3Alias) {} +func (recv Impl3) ImplementMe(callable Impl3Alias) { sink(callable) } // $ hasValueFlow="callable" // Implementation via aliasing the struct type Impl4 struct{} type Impl4Alias = struct{ x IntAlias } -func (recv Impl4) ImplementMe(callable func(Impl4Alias)) {} +func (recv Impl4) ImplementMe(callable func(Impl4Alias)) { sink(callable) } // $ hasValueFlow="callable" // Implementation via aliasing the struct member type Impl5 struct{} type Impl5Alias = IntAlias -func (recv Impl5) ImplementMe(callable func(struct{ x Impl5Alias })) {} +func (recv Impl5) ImplementMe(callable func(struct{ x Impl5Alias })) { sink(callable) } // $ hasValueFlow="callable" // Implementation via defining the method on an alias type Impl6 struct{} type Impl6Alias = Impl6 -func (recv Impl6Alias) ImplementMe(callable func(struct{ x int })) {} +func (recv Impl6Alias) ImplementMe(callable func(struct{ x int })) { sink(callable) } // $ hasValueFlow="callable" func Caller(target Target) { - target.ImplementMe(nil) + callable := source() + target.ImplementMe(callable) } diff --git a/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/methods.go b/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/methods.go index 3d94c77e9cad..83287e117ae7 100644 --- a/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/methods.go +++ b/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/methods.go @@ -1,5 +1,7 @@ package aliases +// Tests how interfaces defining identical types are represented in the database. + type IntAlias = int type S1 = struct{ x int } diff --git a/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/test.expected b/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/test.expected index f1a9ae04c09e..71cd13704370 100644 --- a/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/test.expected +++ b/go/ql/test/library-tests/semmle/go/aliases/MethodDefs/test.expected @@ -1,20 +1,20 @@ distinctDefinedFs | 2 | declaredEntities -| methods.go:3:6:3:13 | IntAlias (1 declaration sites) | -| methods.go:5:6:5:7 | S1 (1 declaration sites) | -| methods.go:5:19:5:19 | x (2 declaration sites) | -| methods.go:6:6:6:7 | S2 (1 declaration sites) | -| methods.go:6:19:6:19 | x (2 declaration sites) | -| methods.go:8:6:8:7 | I1 (1 declaration sites) | -| methods.go:8:22:8:22 | F (2 declaration sites) | -| methods.go:9:6:9:7 | I2 (1 declaration sites) | -| methods.go:9:22:9:22 | F (2 declaration sites) | -| methods.go:10:6:10:7 | I3 (1 declaration sites) | +| methods.go:5:6:5:13 | IntAlias (1 declaration sites) | +| methods.go:7:6:7:7 | S1 (1 declaration sites) | +| methods.go:7:19:7:19 | x (2 declaration sites) | +| methods.go:8:6:8:7 | S2 (1 declaration sites) | +| methods.go:8:19:8:19 | x (2 declaration sites) | +| methods.go:10:6:10:7 | I1 (1 declaration sites) | | methods.go:10:22:10:22 | F (2 declaration sites) | -| methods.go:11:6:11:7 | I4 (1 declaration sites) | +| methods.go:11:6:11:7 | I2 (1 declaration sites) | | methods.go:11:22:11:22 | F (2 declaration sites) | -| methods.go:13:6:13:10 | Test1 (1 declaration sites) | -| methods.go:13:12:13:17 | param1 (1 declaration sites) | -| methods.go:13:23:13:28 | param2 (1 declaration sites) | -| methods.go:13:34:13:36 | arg (1 declaration sites) | +| methods.go:12:6:12:7 | I3 (1 declaration sites) | +| methods.go:12:22:12:22 | F (2 declaration sites) | +| methods.go:13:6:13:7 | I4 (1 declaration sites) | +| methods.go:13:22:13:22 | F (2 declaration sites) | +| methods.go:15:6:15:10 | Test1 (1 declaration sites) | +| methods.go:15:12:15:17 | param1 (1 declaration sites) | +| methods.go:15:23:15:28 | param2 (1 declaration sites) | +| methods.go:15:34:15:36 | arg (1 declaration sites) | diff --git a/go/ql/test/library-tests/semmle/go/aliases/defsuses/defsuses.go b/go/ql/test/library-tests/semmle/go/aliases/defsuses/defsuses.go index 4443adc6c6c6..e98f14532bd9 100644 --- a/go/ql/test/library-tests/semmle/go/aliases/defsuses/defsuses.go +++ b/go/ql/test/library-tests/semmle/go/aliases/defsuses/defsuses.go @@ -1,5 +1,8 @@ package aliases +// Tests how defs and uses of fields are represented in the database +// when identical types are used. + type IntAlias = int type S1 = struct{ x int } diff --git a/go/ql/test/library-tests/semmle/go/aliases/defsuses/test.expected b/go/ql/test/library-tests/semmle/go/aliases/defsuses/test.expected index 3a707e43a3a6..aa9c3ef213a8 100644 --- a/go/ql/test/library-tests/semmle/go/aliases/defsuses/test.expected +++ b/go/ql/test/library-tests/semmle/go/aliases/defsuses/test.expected @@ -1,33 +1,33 @@ lowLevelDefs -| defsuses.go:3:6:3:13 | IntAlias | defsuses.go:3:6:3:13 | IntAlias (1 declaration sites) | -| defsuses.go:5:6:5:7 | S1 | defsuses.go:5:6:5:7 | S1 (1 declaration sites) | -| defsuses.go:5:19:5:19 | x | defsuses.go:5:19:5:19 | x (2 declaration sites) | -| defsuses.go:5:19:5:19 | x | defsuses.go:6:19:6:19 | x (2 declaration sites) | -| defsuses.go:6:6:6:7 | S2 | defsuses.go:6:6:6:7 | S2 (1 declaration sites) | -| defsuses.go:6:19:6:19 | x | defsuses.go:5:19:5:19 | x (2 declaration sites) | -| defsuses.go:6:19:6:19 | x | defsuses.go:6:19:6:19 | x (2 declaration sites) | -| defsuses.go:8:6:8:10 | Test1 | defsuses.go:8:6:8:10 | Test1 (1 declaration sites) | -| defsuses.go:9:2:9:4 | obj | defsuses.go:9:2:9:4 | obj (1 declaration sites) | -| defsuses.go:12:6:12:8 | ptr | defsuses.go:12:6:12:8 | ptr (1 declaration sites) | +| defsuses.go:6:6:6:13 | IntAlias | defsuses.go:6:6:6:13 | IntAlias (1 declaration sites) | +| defsuses.go:8:6:8:7 | S1 | defsuses.go:8:6:8:7 | S1 (1 declaration sites) | +| defsuses.go:8:19:8:19 | x | defsuses.go:8:19:8:19 | x (2 declaration sites) | +| defsuses.go:8:19:8:19 | x | defsuses.go:9:19:9:19 | x (2 declaration sites) | +| defsuses.go:9:6:9:7 | S2 | defsuses.go:9:6:9:7 | S2 (1 declaration sites) | +| defsuses.go:9:19:9:19 | x | defsuses.go:8:19:8:19 | x (2 declaration sites) | +| defsuses.go:9:19:9:19 | x | defsuses.go:9:19:9:19 | x (2 declaration sites) | +| defsuses.go:11:6:11:10 | Test1 | defsuses.go:11:6:11:10 | Test1 (1 declaration sites) | +| defsuses.go:12:2:12:4 | obj | defsuses.go:12:2:12:4 | obj (1 declaration sites) | +| defsuses.go:15:6:15:8 | ptr | defsuses.go:15:6:15:8 | ptr (1 declaration sites) | lowLevelUses -| defsuses.go:3:17:3:19 | int | file://:0:0:0:0 | int (0 declaration sites) | -| defsuses.go:5:21:5:23 | int | file://:0:0:0:0 | int (0 declaration sites) | -| defsuses.go:6:21:6:28 | IntAlias | defsuses.go:3:6:3:13 | IntAlias (1 declaration sites) | -| defsuses.go:8:14:8:16 | int | file://:0:0:0:0 | int (0 declaration sites) | -| defsuses.go:9:9:9:10 | S1 | defsuses.go:5:6:5:7 | S1 (1 declaration sites) | -| defsuses.go:10:2:10:4 | obj | defsuses.go:9:2:9:4 | obj (1 declaration sites) | -| defsuses.go:10:6:10:6 | x | defsuses.go:5:19:5:19 | x (2 declaration sites) | -| defsuses.go:10:6:10:6 | x | defsuses.go:6:19:6:19 | x (2 declaration sites) | -| defsuses.go:12:11:12:12 | S2 | defsuses.go:6:6:6:7 | S2 (1 declaration sites) | -| defsuses.go:13:2:13:4 | ptr | defsuses.go:12:6:12:8 | ptr (1 declaration sites) | -| defsuses.go:13:9:13:11 | obj | defsuses.go:9:2:9:4 | obj (1 declaration sites) | -| defsuses.go:15:9:15:11 | ptr | defsuses.go:12:6:12:8 | ptr (1 declaration sites) | -| defsuses.go:15:13:15:13 | x | defsuses.go:5:19:5:19 | x (2 declaration sites) | -| defsuses.go:15:13:15:13 | x | defsuses.go:6:19:6:19 | x (2 declaration sites) | +| defsuses.go:6:17:6:19 | int | file://:0:0:0:0 | int (0 declaration sites) | +| defsuses.go:8:21:8:23 | int | file://:0:0:0:0 | int (0 declaration sites) | +| defsuses.go:9:21:9:28 | IntAlias | defsuses.go:6:6:6:13 | IntAlias (1 declaration sites) | +| defsuses.go:11:14:11:16 | int | file://:0:0:0:0 | int (0 declaration sites) | +| defsuses.go:12:9:12:10 | S1 | defsuses.go:8:6:8:7 | S1 (1 declaration sites) | +| defsuses.go:13:2:13:4 | obj | defsuses.go:12:2:12:4 | obj (1 declaration sites) | +| defsuses.go:13:6:13:6 | x | defsuses.go:8:19:8:19 | x (2 declaration sites) | +| defsuses.go:13:6:13:6 | x | defsuses.go:9:19:9:19 | x (2 declaration sites) | +| defsuses.go:15:11:15:12 | S2 | defsuses.go:9:6:9:7 | S2 (1 declaration sites) | +| defsuses.go:16:2:16:4 | ptr | defsuses.go:15:6:15:8 | ptr (1 declaration sites) | +| defsuses.go:16:9:16:11 | obj | defsuses.go:12:2:12:4 | obj (1 declaration sites) | +| defsuses.go:18:9:18:11 | ptr | defsuses.go:15:6:15:8 | ptr (1 declaration sites) | +| defsuses.go:18:13:18:13 | x | defsuses.go:8:19:8:19 | x (2 declaration sites) | +| defsuses.go:18:13:18:13 | x | defsuses.go:9:19:9:19 | x (2 declaration sites) | distinctDefinedXs | 1 | distinctUsedXs | 1 | fieldUseUsePairs -| defsuses.go:10:6:10:6 | x | defsuses.go:15:13:15:13 | x | -| defsuses.go:15:13:15:13 | x | defsuses.go:10:6:10:6 | x | +| defsuses.go:13:6:13:6 | x | defsuses.go:18:13:18:13 | x | +| defsuses.go:18:13:18:13 | x | defsuses.go:13:6:13:6 | x |