declare a type for cachedOutput

To properly make our cache robust, we'll need to be able to compute
cache entries for dependencies as needed if they are missing.
So we'll need to create more of these struct values in the code.

Rename cachedOutput to curPkgCache, to clarify that it relates
to the current package.

While here, remove the "known" prefix on all pkgCache fields.
All of the names still make perfect sense without it.
pull/755/head
Daniel Martí 1 year ago
parent da5ddfa45d
commit e079c0af43

@ -907,7 +907,7 @@ func transformCompile(args []string) ([]string, error) {
} }
tf := &transformer{} tf := &transformer{}
// Even if loadCachedOutput below finds a direct cache hit, // Even if loadPkgCache below finds a direct cache hit,
// other parts of garble still need type information to obfuscate. // other parts of garble still need type information to obfuscate.
// We could potentially avoid this by saving the type info we need in the cache, // We could potentially avoid this by saving the type info we need in the cache,
// although in general that wouldn't help much, since it's rare for Go's cache // although in general that wouldn't help much, since it's rare for Go's cache
@ -916,10 +916,10 @@ func transformCompile(args []string) ([]string, error) {
return nil, err return nil, err
} }
// NOTE: cachedOutput.KnownEmbeddedAliasFields is already filled by typecheck above. // NOTE: curPkgCache.EmbeddedAliasFields is already filled by typecheck above.
// That's needed if loadCachedOutput is a miss, as we need to save the map. // That's needed if loadPkgCache is a miss, as we need to save the map.
// If loadCachedOutput is a hit, then it's still fine, as the map entries are the same. // If loadPkgCache is a hit, then it's still fine, as the map entries are the same.
if err := tf.loadCachedOutput(files); err != nil { if err := tf.loadPkgCache(files); err != nil {
return nil, err return nil, err
} }
@ -1260,38 +1260,40 @@ type (
// TODO: read-write globals like these should probably be inside transformer // TODO: read-write globals like these should probably be inside transformer
// cachedOutput contains information that will be stored in fsCache. // pkgCache contains information that will be stored in fsCache.
// Note that cachedOutput gets loaded from all direct package dependencies, // Note that pkgCache gets loaded from all direct package dependencies,
// and gets filled while obfuscating the current package, so it ends up // and gets filled while obfuscating the current package, so it ends up
// containing entries for the current package and its transitive dependencies. // containing entries for the current package and its transitive dependencies.
var cachedOutput = struct { type pkgCache struct {
// KnownReflectAPIs is a static record of what std APIs use reflection on their // ReflectAPIs is a static record of what std APIs use reflection on their
// parameters, so we can avoid obfuscating types used with them. // parameters, so we can avoid obfuscating types used with them.
// //
// TODO: we're not including fmt.Printf, as it would have many false positives, // TODO: we're not including fmt.Printf, as it would have many false positives,
// unless we were smart enough to detect which arguments get used as %#v or %T. // unless we were smart enough to detect which arguments get used as %#v or %T.
KnownReflectAPIs map[funcFullName]map[int]bool ReflectAPIs map[funcFullName]map[int]bool
// KnownCannotObfuscate is filled with the fully qualified names from each // CannotObfuscate is filled with the fully qualified names from each
// package that we cannot obfuscate. // package that we cannot obfuscate.
// This record is necessary for knowing what names from imported packages // This record is necessary for knowing what names from imported packages
// weren't obfuscated, so we can obfuscate their local uses accordingly. // weren't obfuscated, so we can obfuscate their local uses accordingly.
KnownCannotObfuscate map[objectString]struct{} CannotObfuscate map[objectString]struct{}
// KnownEmbeddedAliasFields records which embedded fields use a type alias. // EmbeddedAliasFields records which embedded fields use a type alias.
// They are the only instance where a type alias matters for obfuscation, // They are the only instance where a type alias matters for obfuscation,
// because the embedded field name is derived from the type alias itself, // because the embedded field name is derived from the type alias itself,
// and not the type that the alias points to. // and not the type that the alias points to.
// In that way, the type alias is obfuscated as a form of named type, // In that way, the type alias is obfuscated as a form of named type,
// bearing in mind that it may be owned by a different package. // bearing in mind that it may be owned by a different package.
KnownEmbeddedAliasFields map[objectString]typeName EmbeddedAliasFields map[objectString]typeName
}{ }
KnownReflectAPIs: map[funcFullName]map[int]bool{
var curPkgCache = pkgCache{
ReflectAPIs: map[funcFullName]map[int]bool{
"reflect.TypeOf": {0: true}, "reflect.TypeOf": {0: true},
"reflect.ValueOf": {0: true}, "reflect.ValueOf": {0: true},
}, },
KnownCannotObfuscate: map[objectString]struct{}{}, CannotObfuscate: map[objectString]struct{}{},
KnownEmbeddedAliasFields: map[objectString]typeName{}, EmbeddedAliasFields: map[objectString]typeName{},
} }
func openCache() (*cache.Cache, error) { func openCache() (*cache.Cache, error) {
@ -1313,7 +1315,7 @@ func openCache() (*cache.Cache, error) {
return cache.Open(dir) return cache.Open(dir)
} }
func (tf *transformer) loadCachedOutput(files []*ast.File) error { func (tf *transformer) loadPkgCache(files []*ast.File) error {
fsCache, err := openCache() fsCache, err := openCache()
if err != nil { if err != nil {
return err return err
@ -1326,7 +1328,7 @@ func (tf *transformer) loadCachedOutput(files []*ast.File) error {
return err return err
} }
defer f.Close() defer f.Close()
if err := gob.NewDecoder(f).Decode(&cachedOutput); err != nil { if err := gob.NewDecoder(f).Decode(&curPkgCache); err != nil {
return fmt.Errorf("gob decode: %w", err) return fmt.Errorf("gob decode: %w", err)
} }
return nil return nil
@ -1366,7 +1368,7 @@ func (tf *transformer) loadCachedOutput(files []*ast.File) error {
} }
defer f.Close() defer f.Close()
// Decode appends new entries to the existing maps // Decode appends new entries to the existing maps
if err := gob.NewDecoder(f).Decode(&cachedOutput); err != nil { if err := gob.NewDecoder(f).Decode(&curPkgCache); err != nil {
return fmt.Errorf("gob decode: %w", err) return fmt.Errorf("gob decode: %w", err)
} }
return nil return nil
@ -1402,7 +1404,7 @@ func (tf *transformer) loadCachedOutput(files []*ast.File) error {
// Unlikely that we could stream the gob encode, as cache.Put wants an io.ReadSeeker. // Unlikely that we could stream the gob encode, as cache.Put wants an io.ReadSeeker.
var buf bytes.Buffer var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(cachedOutput); err != nil { if err := gob.NewEncoder(&buf).Encode(curPkgCache); err != nil {
return err return err
} }
if err := fsCache.PutBytes(curPkg.GarbleActionID, buf.Bytes()); err != nil { if err := fsCache.PutBytes(curPkg.GarbleActionID, buf.Bytes()); err != nil {
@ -1521,7 +1523,7 @@ func (tf *transformer) typecheck(files []*ast.File) error {
} }
tf.recordType(obj.Type(), nil) tf.recordType(obj.Type(), nil)
// Record into KnownEmbeddedAliasFields. // Record into EmbeddedAliasFields.
obj, ok := obj.(*types.TypeName) obj, ok := obj.(*types.TypeName)
if !ok || !obj.IsAlias() { if !ok || !obj.IsAlias() {
continue continue
@ -1538,7 +1540,7 @@ func (tf *transformer) typecheck(files []*ast.File) error {
PkgPath: obj.Pkg().Path(), PkgPath: obj.Pkg().Path(),
Name: obj.Name(), Name: obj.Name(),
} }
cachedOutput.KnownEmbeddedAliasFields[vrStr] = aliasTypeName curPkgCache.EmbeddedAliasFields[vrStr] = aliasTypeName
} }
for _, tv := range tf.info.Types { for _, tv := range tf.info.Types {
tf.recordType(tv.Type, nil) tf.recordType(tv.Type, nil)
@ -1735,7 +1737,7 @@ func (tf *transformer) transformGoFile(file *ast.File) *ast.File {
// Alternatively, if we don't have an alias, we still want to // Alternatively, if we don't have an alias, we still want to
// use the embedded type, not the field. // use the embedded type, not the field.
vrStr := recordedObjectString(vr) vrStr := recordedObjectString(vr)
aliasTypeName, ok := cachedOutput.KnownEmbeddedAliasFields[vrStr] aliasTypeName, ok := curPkgCache.EmbeddedAliasFields[vrStr]
if ok { if ok {
pkg2 := tf.pkg pkg2 := tf.pkg
if path := aliasTypeName.PkgPath; pkg2.Path() != path { if path := aliasTypeName.PkgPath; pkg2.Path() != path {
@ -1751,10 +1753,10 @@ func (tf *transformer) transformGoFile(file *ast.File) *ast.File {
} }
tname, ok := pkg2.Scope().Lookup(aliasTypeName.Name).(*types.TypeName) tname, ok := pkg2.Scope().Lookup(aliasTypeName.Name).(*types.TypeName)
if !ok { if !ok {
panic(fmt.Sprintf("KnownEmbeddedAliasFields pointed %q to a missing type %q", vrStr, aliasTypeName)) panic(fmt.Sprintf("EmbeddedAliasFields pointed %q to a missing type %q", vrStr, aliasTypeName))
} }
if !tname.IsAlias() { if !tname.IsAlias() {
panic(fmt.Sprintf("KnownEmbeddedAliasFields pointed %q to a non-alias type %q", vrStr, aliasTypeName)) panic(fmt.Sprintf("EmbeddedAliasFields pointed %q to a non-alias type %q", vrStr, aliasTypeName))
} }
obj = tname obj = tname
} else { } else {

@ -16,11 +16,11 @@ func (tf *transformer) recordReflection(ssaPkg *ssa.Package) {
return return
} }
lenPrevKnownReflectAPIs := len(cachedOutput.KnownReflectAPIs) lenPrevReflectAPIs := len(curPkgCache.ReflectAPIs)
// find all unchecked APIs to add them to checkedAPIs after the pass // find all unchecked APIs to add them to checkedAPIs after the pass
notCheckedAPIs := make(map[string]bool) notCheckedAPIs := make(map[string]bool)
for _, knownAPI := range maps.Keys(cachedOutput.KnownReflectAPIs) { for _, knownAPI := range maps.Keys(curPkgCache.ReflectAPIs) {
if !tf.reflectCheckedAPIs[knownAPI] { if !tf.reflectCheckedAPIs[knownAPI] {
notCheckedAPIs[knownAPI] = true notCheckedAPIs[knownAPI] = true
} }
@ -33,7 +33,7 @@ func (tf *transformer) recordReflection(ssaPkg *ssa.Package) {
maps.Copy(tf.reflectCheckedAPIs, notCheckedAPIs) maps.Copy(tf.reflectCheckedAPIs, notCheckedAPIs)
// if a new reflectAPI is found we need to Re-evaluate all functions which might be using that API // if a new reflectAPI is found we need to Re-evaluate all functions which might be using that API
if len(cachedOutput.KnownReflectAPIs) > lenPrevKnownReflectAPIs { if len(curPkgCache.ReflectAPIs) > lenPrevReflectAPIs {
tf.recordReflection(ssaPkg) tf.recordReflection(ssaPkg)
} }
} }
@ -123,7 +123,7 @@ func (tf *transformer) checkMethodSignature(reflectParams map[int]bool, sig *typ
func (tf *transformer) checkInterfaceMethod(m *types.Func) { func (tf *transformer) checkInterfaceMethod(m *types.Func) {
reflectParams := make(map[int]bool) reflectParams := make(map[int]bool)
maps.Copy(reflectParams, cachedOutput.KnownReflectAPIs[m.FullName()]) maps.Copy(reflectParams, curPkgCache.ReflectAPIs[m.FullName()])
sig := m.Type().(*types.Signature) sig := m.Type().(*types.Signature)
if m.Exported() { if m.Exported() {
@ -131,9 +131,9 @@ func (tf *transformer) checkInterfaceMethod(m *types.Func) {
} }
if len(reflectParams) > 0 { if len(reflectParams) > 0 {
cachedOutput.KnownReflectAPIs[m.FullName()] = reflectParams curPkgCache.ReflectAPIs[m.FullName()] = reflectParams
/* fmt.Printf("cachedOutput.KnownReflectAPIs: %v\n", cachedOutput.KnownReflectAPIs) */ /* fmt.Printf("curPkgCache.ReflectAPIs: %v\n", curPkgCache.ReflectAPIs) */
} }
} }
@ -148,7 +148,7 @@ func (tf *transformer) checkFunction(fun *ssa.Function) {
reflectParams := make(map[int]bool) reflectParams := make(map[int]bool)
if f != nil { if f != nil {
maps.Copy(reflectParams, cachedOutput.KnownReflectAPIs[f.FullName()]) maps.Copy(reflectParams, curPkgCache.ReflectAPIs[f.FullName()])
if f.Exported() { if f.Exported() {
tf.checkMethodSignature(reflectParams, fun.Signature) tf.checkMethodSignature(reflectParams, fun.Signature)
@ -179,7 +179,7 @@ func (tf *transformer) checkFunction(fun *ssa.Function) {
/* fmt.Printf("callName: %v\n", callName) */ /* fmt.Printf("callName: %v\n", callName) */
// record each call argument passed to a function parameter which is used in reflection // record each call argument passed to a function parameter which is used in reflection
knownParams := cachedOutput.KnownReflectAPIs[callName] knownParams := curPkgCache.ReflectAPIs[callName]
for knownParam := range knownParams { for knownParam := range knownParams {
if len(call.Call.Args) <= knownParam { if len(call.Call.Args) <= knownParam {
continue continue
@ -208,9 +208,9 @@ func (tf *transformer) checkFunction(fun *ssa.Function) {
} }
if len(reflectParams) > 0 { if len(reflectParams) > 0 {
cachedOutput.KnownReflectAPIs[f.FullName()] = reflectParams curPkgCache.ReflectAPIs[f.FullName()] = reflectParams
/* fmt.Printf("cachedOutput.KnownReflectAPIs: %v\n", cachedOutput.KnownReflectAPIs) */ /* fmt.Printf("curPkgCache.ReflectAPIs: %v\n", curPkgCache.ReflectAPIs) */
} }
} }
@ -438,7 +438,7 @@ func recordAsNotObfuscated(obj types.Object) {
// do we need to record it at all? // do we need to record it at all?
return return
} }
cachedOutput.KnownCannotObfuscate[objStr] = struct{}{} curPkgCache.CannotObfuscate[objStr] = struct{}{}
} }
func recordedAsNotObfuscated(obj types.Object) bool { func recordedAsNotObfuscated(obj types.Object) bool {
@ -446,6 +446,6 @@ func recordedAsNotObfuscated(obj types.Object) bool {
if objStr == "" { if objStr == "" {
return false return false
} }
_, ok := cachedOutput.KnownCannotObfuscate[objStr] _, ok := curPkgCache.CannotObfuscate[objStr]
return ok return ok
} }

Loading…
Cancel
Save