Skip to content

Commit

Permalink
support fetching original package names and import paths via reflection
Browse files Browse the repository at this point in the history
Just like we support fetching original type, field, and method names
via reflection to ensure that no Go package is broken by garble,
do the same for package names and import paths, which are reachable too.

This means we can remove the test function printfWithoutPackage,
which in hindsight should have been a clue as we were working around
a bug in garble.

Updates burrowers#849.
  • Loading branch information
mvdan committed Jan 4, 2025
1 parent 8a3192b commit 92638a6
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 22 deletions.
8 changes: 8 additions & 0 deletions reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ func (ri *reflectInspector) recordReflection(ssaPkg *ssa.Package) {
// if a new reflectAPI is found we need to Re-evaluate all functions which might be using that API
newDone := len(ri.result.ReflectAPIs) + len(ri.result.ReflectObjectNames)
if newDone > prevDone {
// At least one type in this package is used with reflection;
// the package name and import path are reachable via reflection as well.
// Note that package main is compiled as having import path "main" as well.
ri.result.ReflectObjectNames[ri.lpkg.obfuscatedPackageName()] = ri.lpkg.Name
if ri.lpkg.Name != "main" {
ri.result.ReflectObjectNames[ri.lpkg.obfuscatedImportPath()] = ri.lpkg.ImportPath
}

ri.recordReflection(ssaPkg) // TODO: avoid recursing
}
}
Expand Down
3 changes: 3 additions & 0 deletions reflect_abi_patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func reflectMainPostPatch(file []byte, lpkg *listedPackage, pkg pkgCache) []byte

keys := slices.Sorted(maps.Keys(pkg.ReflectObjectNames))
namePairsFilled := bytes.Clone(namePairs)
// TODO: check that there are no repeats on the left side of the pairs;
// entries "foo, bar" and "foo, baz" mean that the second entry is useless.
// TODO: check that there are no unchanged pairs; entry "foo, foo" is useless.
for _, obf := range keys {
namePairsFilled = fmt.Appendf(namePairsFilled, "%q, %q,", obf, pkg.ReflectObjectNames[obf])
}
Expand Down
42 changes: 20 additions & 22 deletions testdata/script/reflect.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ exec garble build
exec ./main
cmp stdout main.stdout

! binsubstr main$exe 'garble_main.go' 'test/main' 'importedpkg.' 'DownstreamObfuscated' 'SiblingObfuscated' 'IndirectObfuscated' 'IndirectNamedWithoutReflect' 'AliasIndirectNamedWithReflect' 'AliasIndirectNamedWithoutReflect' 'FmtTypeField' 'LocalObfuscated'
! binsubstr main$exe 'garble_main.go' 'DownstreamObfuscated' 'SiblingObfuscated' 'IndirectObfuscated' 'IndirectNamedWithoutReflect' 'AliasIndirectNamedWithReflect' 'AliasIndirectNamedWithoutReflect' 'FmtTypeField' 'LocalObfuscated'
binsubstr main$exe 'ReflectInDefined' 'ExportedField2' 'unexportedField2' 'IndirectUnobfuscated' 'IndirectNamedWithReflect'

[short] stop # no need to verify this with -short
Expand Down Expand Up @@ -31,7 +31,6 @@ import (
"os"
"reflect"
"unsafe"
"strings"
"sync"
"text/template"

Expand All @@ -47,19 +46,24 @@ func main() {
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))
fmt.Printf("%T\n", importedpkg.ReflectTypeOf(2))
fmt.Printf("%T\n", importedpkg.ReflectTypeOfIndirect(4))

// More complex use of reflect.
v := importedpkg.ReflectValueOfVar
printfWithoutPackage("%#v\n", v)
method := reflect.ValueOf(&v).MethodByName("ExportedMethodName")
fmt.Printf("%#v\n", v)
rv:= reflect.ValueOf(&v)
method := rv.MethodByName("ExportedMethodName")
if method.IsValid() {
fmt.Println(method.Call(nil))
} else {
fmt.Println("method not found")
}

// Reflection can also access package import paths.
fmt.Println(reflect.TypeOf(EncodingT{}).PkgPath())
fmt.Println(rv.Type().Elem().PkgPath())

// Use of a common library using reflect, encoding/json.
enc, _ := json.Marshal(EncodingT{Foo: 3})
fmt.Println(string(enc))
Expand Down Expand Up @@ -101,10 +105,10 @@ func main() {
emb.Without.IndirectObfuscated = "indirect-without"
emb.Without.DuplicateFieldName = 4
fmt.Printf("%v\n", emb)
printfWithoutPackage("%#v\n", emb.With)
fmt.Printf("%#v\n", emb.With)

// TODO: don't obfuscate the embedded field name here
// printfWithoutPackage("%#v\n", importedpkg.ReflectEmbeddingAlias{})
// fmt.Printf("%#v\n", importedpkg.ReflectEmbeddingAlias{})

indirectReflection(IndirectReflection{})
fmt.Println(FmtType{})
Expand All @@ -115,7 +119,7 @@ func main() {
_ = importedpkg.VariadicReflect(0)
variadic := VariadicReflection{ReflectionField: "variadic"}
_ = importedpkg.VariadicReflect("foo", 1, variadic, false)
printfWithoutPackage("%#v\n", variadic)
fmt.Printf("%#v\n", variadic)

testx509()
testGoSpew()
Expand Down Expand Up @@ -185,14 +189,6 @@ type EmbeddingIndirect struct {
importedpkg.AliasIndirectNamedWithReflect
}

func printfWithoutPackage(format string, v any) {
s := fmt.Sprintf(format, v)
if _, without, found := strings.Cut(s, "."); found {
s = without
}
fmt.Print(s)
}

type EncodingT struct {
Foo int
}
Expand Down Expand Up @@ -622,20 +618,22 @@ type IndirectNamedWithoutReflect struct {
-- main.stdout --
9000
{5 0 {}}
ReflectTypeOf
ReflectTypeOfIndirect
ReflectValueOf{ExportedField:"abc", unexportedField:""}
importedpkg.ReflectTypeOf
importedpkg.ReflectTypeOfIndirect
importedpkg.ReflectValueOf{ExportedField:"abc", unexportedField:""}
[method: abc]
main
test/main/importedpkg
{"Foo":3}
Hello Dave.
{"InnerField":3,"Anon":{"AnonField":0}}
{downstream}
{sibling}
{{indirect-with 3} {indirect-without 4} { 0}}
IndirectNamedWithReflect{IndirectUnobfuscated:"indirect-with", DuplicateFieldName:3}
indirect.IndirectNamedWithReflect{IndirectUnobfuscated:"indirect-with", DuplicateFieldName:3}
ReflectionField
{0}
VariadicReflection{ReflectionField:"variadic"}
main.VariadicReflection{ReflectionField:"variadic"}
*main.StatUser
*main.StatCompUser
struct { UnnamedStructField string }
Expand Down

0 comments on commit 92638a6

Please sign in to comment.