From 8961e0a39a97e6071d6b52dfeea2ab0e98cef4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Wed, 5 May 2021 10:20:34 +0100 Subject: [PATCH] testdata: split reflection test cases into reflect.txt The detection of reflection usage is tricky and there are plenty of edge cases to test for. We definitely want one script for it, rather than splitting those cases between other scripts like imports.txt and syntax.txt. Moreover, those two were rather generic and large, so this helps keep a balance. --- testdata/scripts/imports.txt | 59 +-------------- testdata/scripts/reflect.txt | 137 +++++++++++++++++++++++++++++++++++ testdata/scripts/syntax.txt | 41 ----------- 3 files changed, 138 insertions(+), 99 deletions(-) create mode 100644 testdata/scripts/reflect.txt diff --git a/testdata/scripts/imports.txt b/testdata/scripts/imports.txt index 41b3248..d252a09 100644 --- a/testdata/scripts/imports.txt +++ b/testdata/scripts/imports.txt @@ -20,8 +20,7 @@ garble build -tags buildtag exec ./main cmp stdout main.stdout -! binsubstr main$exe 'ImportedVar' 'ImportedConst' 'ImportedFunc' 'ImportedType' 'main.go' 'test/main' 'importedpkg.' 'NormalStruct' 'NormalExportedField' 'normalUnexportedField' -binsubstr main$exe 'ReflectInDefined' 'ExportedField2' 'unexportedField2' +! binsubstr main$exe 'ImportedVar' 'ImportedConst' 'ImportedFunc' 'main.go' 'test/main' 'importedpkg.' 'NormalStruct' 'normalUnexportedField' [short] stop # checking that the build is reproducible is slow @@ -65,7 +64,6 @@ package main import ( "fmt" - "reflect" "strings" "test/main/importedpkg" @@ -78,23 +76,9 @@ func main() { fmt.Println(importedpkg.ImportedVar) fmt.Println(importedpkg.ImportedConst) fmt.Println(importedpkg.ImportedFunc('x')) - fmt.Println(importedpkg.ImportedType(3)) - fmt.Println(importedpkg.ReflectInDefinedVar.ExportedField2) - fmt.Println(importedpkg.ReflectInDefined{ExportedField2: 5}) normal := importedpkg.NormalStruct{SharedName: 3} normal.IndirectStruct.Field = 23 fmt.Println(normal) - printfWithoutPackage("%T\n", importedpkg.ReflectTypeOf(2)) - printfWithoutPackage("%T\n", importedpkg.ReflectTypeOfIndirect(4)) - - v := importedpkg.ReflectValueOfVar - printfWithoutPackage("%#v\n", v) - method := reflect.ValueOf(&v).MethodByName("ExportedMethodName") - if method.IsValid() { - fmt.Println(method.Call(nil)) - } else { - fmt.Println("method not found") - } fmt.Println(quote.Go()) garbletest.Test() @@ -133,7 +117,6 @@ var _ = named.Noop package importedpkg import ( - "reflect" "test/main/importedpkg/indirect" ) @@ -145,36 +128,6 @@ func ImportedFunc(param rune) string { return string(param) } -type ReflectTypeOf int - -var _ = reflect.TypeOf(ReflectTypeOf(0)) - -type ReflectTypeOfIndirect int - -var _ = reflect.TypeOf(new([]*ReflectTypeOfIndirect)) - -type ReflectValueOf struct { - ExportedField string - - unexportedField string -} - -func (r *ReflectValueOf) ExportedMethodName() string { return "method: " + r.ExportedField } - -var ReflectValueOfVar = ReflectValueOf{ExportedField: "abc"} - -var _ = reflect.TypeOf(ReflectValueOfVar) - -type ReflectInDefined struct { - ExportedField2 int - - unexportedField2 int -} - -var ReflectInDefinedVar = ReflectInDefined{ExportedField2: 9000} - -var _ = reflect.TypeOf(ReflectInDefinedVar) - const SharedName = 2 type NormalStruct struct { @@ -183,9 +136,6 @@ type NormalStruct struct { normalUnexportedField int } -// ImportedType comes after the calls to reflect, to ensure no false positives. -type ImportedType int - -- importedpkg/indirect/indirect.go -- package indirect @@ -203,12 +153,5 @@ buildtag init func imported var value imported const value x -3 -9000 -{5 0} {3 {23} 0} -ReflectTypeOf -ReflectTypeOfIndirect -ReflectValueOf{ExportedField:"abc", unexportedField:""} -[method: abc] Don't communicate by sharing memory, share memory by communicating. diff --git a/testdata/scripts/reflect.txt b/testdata/scripts/reflect.txt new file mode 100644 index 0000000..bb50515 --- /dev/null +++ b/testdata/scripts/reflect.txt @@ -0,0 +1,137 @@ +env GOPRIVATE=test/main + +garble build +exec ./main +cmp stdout main.stdout + +! binsubstr main$exe 'main.go' 'test/main' 'importedpkg.' +binsubstr main$exe 'ReflectInDefined' 'ExportedField2' 'unexportedField2' + +[short] stop # no need to verify this with -short + +# Check that the program works as expected without garble. +go build +exec ./main +cmp stdout main.stdout + +-- go.mod -- +module test/main + +go 1.16 +-- main.go -- +package main + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + + "test/main/importedpkg" +) + +func main() { + // Fields still work fine when they are not obfuscated. + fmt.Println(importedpkg.ReflectInDefinedVar.ExportedField2) + fmt.Println(importedpkg.ReflectInDefined{ExportedField2: 5}) + + // Type names are not obfuscated either, when reflection is used. + printfWithoutPackage("%T\n", importedpkg.ReflectTypeOf(2)) + printfWithoutPackage("%T\n", importedpkg.ReflectTypeOfIndirect(4)) + + // More complex use of reflect. + v := importedpkg.ReflectValueOfVar + printfWithoutPackage("%#v\n", v) + method := reflect.ValueOf(&v).MethodByName("ExportedMethodName") + if method.IsValid() { + fmt.Println(method.Call(nil)) + } else { + fmt.Println("method not found") + } + + // Use of a common library using reflect, encoding/json. + // TODO: remove the TypeOf hint once we detect json.Marshal. + var _ = reflect.TypeOf(EncodingT{}) + enc, _ := json.Marshal(EncodingT{Foo: 3}) + println(string(enc)) + + // Another complex case, involving embedding and another package. + outer := &importedpkg.EmbeddingOuter{} + outer.InnerField = 3 + enc, _ = json.Marshal(outer) + println(string(enc)) +} + +func printfWithoutPackage(format string, v interface{}) { + fmt.Print(strings.Split(fmt.Sprintf(format, v), ".")[1]) +} + +type EncodingT struct { + Foo int +} + +type RecursiveStruct struct { + *RecursiveStruct + list []RecursiveStruct +} + +// This could crash or hang if we don't deal with loops. +var _ = reflect.TypeOf(RecursiveStruct{}) + +-- importedpkg/imported.go -- +package importedpkg + +import ( + "reflect" +) + +type ReflectTypeOf int + +var _ = reflect.TypeOf(ReflectTypeOf(0)) + +type ReflectTypeOfIndirect int + +var _ = reflect.TypeOf(new([]*ReflectTypeOfIndirect)) + +type ReflectValueOf struct { + ExportedField string + + unexportedField string +} + +func (r *ReflectValueOf) ExportedMethodName() string { return "method: " + r.ExportedField } + +var ReflectValueOfVar = ReflectValueOf{ExportedField: "abc"} + +var _ = reflect.TypeOf(ReflectValueOfVar) + +type ReflectInDefined struct { + ExportedField2 int + + unexportedField2 int +} + +var ReflectInDefinedVar = ReflectInDefined{ExportedField2: 9000} + +var _ = reflect.TypeOf(ReflectInDefinedVar) + +var _ = reflect.TypeOf([]*struct{EmbeddingOuter}{}) + +type EmbeddingOuter struct { + EmbeddingInner + Anon struct { + AnonField int + } +} + +type EmbeddingInner struct { + InnerField int +} + +-- main.stdout -- +9000 +{5 0} +ReflectTypeOf +ReflectTypeOfIndirect +ReflectValueOf{ExportedField:"abc", unexportedField:""} +[method: abc] diff --git a/testdata/scripts/syntax.txt b/testdata/scripts/syntax.txt index 2949fd5..bc8a11a 100644 --- a/testdata/scripts/syntax.txt +++ b/testdata/scripts/syntax.txt @@ -40,9 +40,7 @@ replace private.source/extra => ./extra package main import ( - "encoding/json" "go/ast" - "reflect" "private.source/extra" "test/main/sub" @@ -56,10 +54,6 @@ type T struct { *ast.Ident } -type EncodingT struct { - Foo int -} - type Embedded int type Embedding struct { @@ -102,30 +96,12 @@ func main() { println("nil case") } - var _ = reflect.TypeOf(EncodingT{}) - enc, _ := json.Marshal(EncodingT{Foo: 3}) - println(string(enc)) scopesTest() println(extra.Func()) sub.Test() neverInlined() - - // A harder case of detecting reflection. - // The type is defined in a dependency, - // and the types involved are more complex. - outer := &sub.EmbeddingOuter{} - outer.InnerField = 3 - enc, _ = json.Marshal(outer) - println(string(enc)) -} - -type RecursiveStruct struct { - *RecursiveStruct - list []RecursiveStruct } -var _ = reflect.TypeOf(RecursiveStruct{}) - -- scopes.go -- package main @@ -159,8 +135,6 @@ func input(localNameParam string) (localNameReturn string) { return localNamePar -- sub/names.go -- package sub -import "reflect" - var someGlobalVar0 = "0" var someGlobalVar1 = "1" var someGlobalVar2 = "2" @@ -181,24 +155,9 @@ func TestFoo(s string) {} func TestBar(*struct{}) {} -var _ = reflect.TypeOf([]*struct{EmbeddingOuter}{}) - -type EmbeddingOuter struct { - EmbeddingInner - Anon struct { - AnonField int - } -} - -type EmbeddingInner struct { - InnerField int -} - -- main.stderr -- nil case -{"Foo":3} 1 1 1 1 4 5 1 input This is a separate module to obfuscate. This func is never inlined. -{"InnerField":3,"Anon":{"AnonField":0}}