Browse Source

skip literals used in constant expressions

Fixes #39.
lu4p 2 years ago
parent
commit
f1bf6f91ee
4 changed files with 100 additions and 6 deletions
  1. 1 0
      .gitignore
  2. 7 3
      main.go
  3. 56 1
      strings.go
  4. 36 2
      testdata/scripts/strings.txt

+ 1 - 0
.gitignore

@@ -1 +1,2 @@
 /garble
+/test

+ 7 - 3
main.go

@@ -386,8 +386,10 @@ func transformCompile(args []string) ([]string, error) {
 		return nil, fmt.Errorf("typecheck error: %v", err)
 	}
 
+	blacklist := buildBlacklist(files, info, pkg)
+
 	if envGarbleLiterals {
-		files = obfuscateLiterals(files, info)
+		files = obfuscateLiterals(files, info, blacklist)
 		// ast changed so we need to typecheck again
 		pkg, err = origTypesConfig.Check(pkgPath, fset, files, info)
 		if err != nil {
@@ -410,8 +412,6 @@ func transformCompile(args []string) ([]string, error) {
 	// log.Println(flags)
 	args = flags
 
-	blacklist := buildBlacklist(files, info, pkg)
-
 	pkgDebugDir := ""
 	if envGarbleDebugDir != "" {
 		osPkgPath := filepath.FromSlash(pkgPath)
@@ -665,6 +665,10 @@ func buildBlacklist(files []*ast.File, info *types.Info, pkg *types.Package) map
 		}
 	}
 	visit := func(node ast.Node) bool {
+		if envGarbleLiterals {
+			constBlacklist(node, info, blacklist)
+		}
+
 		if node == nil {
 			if level == reflectCallLevel {
 				reflectCallLevel = -1

+ 56 - 1
strings.go

@@ -43,7 +43,7 @@ func containsTypeDefStr(expr ast.Expr, info *types.Info) bool {
 	return false
 }
 
-func obfuscateLiterals(files []*ast.File, info *types.Info) []*ast.File {
+func obfuscateLiterals(files []*ast.File, info *types.Info, blacklist map[types.Object]struct{}) []*ast.File {
 	pre := func(cursor *astutil.Cursor) bool {
 		switch x := cursor.Node().(type) {
 		case *ast.ValueSpec:
@@ -91,6 +91,15 @@ func obfuscateLiterals(files []*ast.File, info *types.Info) []*ast.File {
 					return false
 				}
 
+				for _, name := range spec.Names {
+					obj := info.ObjectOf(name)
+
+					// The object itself is blacklisted, e.g. a value that needs to be constant
+					if _, ok := blacklist[obj]; ok {
+						return false
+					}
+				}
+
 				for _, val := range spec.Values {
 					if v, ok := val.(*ast.BasicLit); !ok || v.Kind != token.STRING {
 						return false // skip the block if it contains non basic literals
@@ -306,3 +315,49 @@ func keyStmt(key []byte) *ast.GenDecl {
 		}},
 	}
 }
+
+func constBlacklist(node ast.Node, info *types.Info, blacklist map[types.Object]struct{}) {
+
+	blacklistObjects := func(node ast.Node) bool {
+		ident, ok := node.(*ast.Ident)
+		if !ok {
+			return true
+		}
+
+		obj := info.ObjectOf(ident)
+		blacklist[obj] = struct{}{}
+
+		return true
+	}
+
+	switch x := node.(type) {
+	// in a slice or array composite literal all explicit keys must be constant representable
+	case *ast.CompositeLit:
+		if _, ok := x.Type.(*ast.ArrayType); !ok {
+			break
+		}
+		for _, elt := range x.Elts {
+			if kv, ok := elt.(*ast.KeyValueExpr); ok {
+				ast.Inspect(kv.Key, blacklistObjects)
+			}
+		}
+	// in an array type the length must be a constant representable
+	case *ast.ArrayType:
+		if x.Len != nil {
+			ast.Inspect(x.Len, blacklistObjects)
+		}
+
+	// in a const declaration all values must be constant representable
+	case *ast.GenDecl:
+		if x.Tok != token.CONST {
+			break
+		}
+		for _, spec := range x.Specs {
+			spec := spec.(*ast.ValueSpec)
+
+			for _, val := range spec.Values {
+				ast.Inspect(val, blacklistObjects)
+			}
+		}
+	}
+}

+ 36 - 2
testdata/scripts/strings.txt

@@ -78,6 +78,7 @@ func main() {
 	println(skip1, skip2)
 	println(i, foo, bar)
 	typedTest()
+	constantTest()
 }
 
 type stringType string
@@ -98,7 +99,7 @@ func typedTest() {
 	println(skipTypedConst, skipTypedVar, skipTypedVarAssign)
 
 	y := stringTypeStruct{
-		str:     "stringTypeField String",     // obfuscate
+		str:     "stringTypeField String",  // obfuscate
 		strType: "stringTypeField strType", // skip
 	}
 	println(y.str, y.strType)
@@ -120,6 +121,37 @@ func typedTest() {
 	println(stringTypeFunc("stringType func param")) // skip
 }
 
+// constantTest tests that string constants which need to be constant are skipped
+func constantTest() {
+	const a = "foo" // skip
+	const length = len(a)
+
+	const b = "bar" // skip
+	type T [len(b)]byte
+
+	const c = "foo" // skip
+	var _ [len(c)]byte
+
+	const d = "foo" // skip
+	var arr = [5]string{len(d): "foo"}
+	for _, elm := range arr {
+		if elm != "" {
+			println(elm)
+		}
+	}
+
+	const e = "foo" // skip
+	var slice = []string{len(e): "foo"}
+	for _, elm := range slice {
+		if elm != "" {
+			println(elm)
+		}
+	}
+
+	const f = "foo" // skip
+	const i = length + len(f)
+}
+
 func stringTypeFunc(s stringType) stringType {
 	println(s)
 	return "stringType return" // skip
@@ -141,4 +173,6 @@ skip typed const skip typed var skip typed var assign
 stringTypeField String stringTypeField strType
 stringType lambda func return
 stringType func param
-stringType return
+stringType return
+foo
+foo