You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
garble/testdata/script/syntax.txtar

271 lines
5.2 KiB
Plaintext

exec garble build
exec ./main$exe
cmp stderr main.stderr
! binsubstr main$exe 'localName' 'globalConst' 'globalVar' 'globalType' 'valuable information' 'private.source' 'remoteIntReturn' 'intReturn' 'neverInlined'
[short] stop # no need to verify this with -short
# Check that the program works as expected without garble.
go build
exec ./main$exe
cmp stderr main.stderr
binsubstr main$exe 'globalVar' # 'globalType' matches on some, but not all, platforms
! binsubstr main$exe 'localName' 'globalConst' 'remoteIntReturn' 'intReturn'
-- extra/go.mod --
module private.source/extra
go 1.20
-- extra/extra.go --
package extra
func Func() string {
return "This is a separate module to obfuscate."
}
-- go.mod --
module test/main
go 1.20
// We include an extra module to obfuscate, included in the same original source
// code via a replace directive.
require private.source/extra v0.0.0-00010101000000-000000000000
replace private.source/extra => ./extra
-- main.go --
package main
import (
"go/ast"
"runtime"
"private.source/extra"
"test/main/sub"
)
// This comment contains valuable information. Ensure it's not in the final binary.
var V any
type T struct {
ast.Node
*ast.Ident
}
type Embedded int
type Embedding struct {
Embedded
}
type embedded int
type embedding struct {
embedded
}
// embedded fields whose type is in the universe scope used to crash garble
type EmbeddingUniverseScope struct {
error
int
string
}
// TODO: test that go:noinline still works without using debugdir
func ensureInlined(wantInlined bool) {
pc := make([]uintptr, 1)
// We skip two caller frames; runtime.Callers, and ensureInlined.
// This way, the frame we get is our caller, like neverInlined.
n := runtime.Callers(2, pc)
if n == 0 {
panic("got zero callers?")
}
pc = pc[:n]
frames := runtime.CallersFrames(pc)
frame, _ := frames.Next()
gotInlined := frame.Func == nil
if wantInlined && !gotInlined {
panic("caller should be inlined but wasn't")
} else if !wantInlined && gotInlined {
panic("caller shouldn't be inlined but was")
}
}
//go:noinline
func neverInlined() {
ensureInlined(false)
println("This func is never inlined.")
}
func alwaysInlined() {
ensureInlined(true)
println("This func is always inlined.")
}
type EmbeddingOuter struct {
EmbeddingInner
}
type EmbeddingInner struct {
SomeField int
}
func main() {
switch V := V.(type) {
case int:
var _ int = V
case nil:
println("nil case")
}
scopesTest()
println(extra.Func())
sub.Test()
neverInlined()
alwaysInlined()
_ = sub.EmbeddingExternalForeignAlias{
ExternalForeignAlias: nil,
Reader: nil,
}
properly record when type aliases are embedded as fields There are two scenarios when it comes to embedding fields. The first is easy, and we always handled it well: type Named struct { Foo int } type T struct { Named } In this scenario, T ends up with an embedded field named "Named", and a promoted field named "Foo". Then there's the form with a type alias: type Named struct { Foo int } type Alias = Named type T struct { Alias } This case is different: T ends up with an embedded field named "Alias", and a promoted field named "Foo". Note how the field gets its name from the referenced type, even if said type is just an alias to another type. This poses two problems. First, we must obfuscate the field T.Alias as the name "Alias", and not as the name "Named" that the alias points to. Second, we must be careful of cases where Named and Alias are declared in different packages, as they will obfuscate the same name differently. Both of those problems compounded in the reported issue. The actual reason is that quic-go has a type alias in the form of: type ConnectionState = qtls.ConnectionState In other words, the entire problem boils down to a type alias which points to a named type in a different package, where both types share the same name. For example: package parent import "parent/p1" type T struct { p1.SameName } [...] package p1 import "parent/p2" type SameName = p2.SameName [...] package p2 type SameName struct { Foo int } This broke garble because we had a heuristic to detect when an embedded field was a type alias: // Instead, detect such a "foreign alias embed". // If we embed a final named type, // but the field name does not match its name, // then it must have been done via an alias. // We dig out the alias's TypeName via locateForeignAlias. if named.Obj().Name() != node.Name { As the reader can deduce, this heuristic would incorrectly assume that the snippet above does not embed a type alias, when in fact it does. When obfuscating the field T.SameName, which uses a type alias, we would correctly obfuscate the name "SameName", but we would incorrectly obfuscate it with the package p2, not p1. This would then result in build errors. To fix this problem for good, we need to get rid of the heuristic. Instead, we now mimic what was done for KnownCannotObfuscate, but for embedded fields which use type aliases. KnownEmbeddedAliasFields is now filled for each package and stored in the cache as part of cachedOutput. We can then detect the "embedded alias" case reliably, even when the field is declared in an imported package. On the plus side, we get to remove locateForeignAlias. We also add a couple of TODOs to record further improvements. Finally, add a test. Fixes #466.
2 years ago
var emb sub.EmbeddingAlias
_ = emb.EmbeddedAlias
_ = emb.Foo
_ = emb.EmbeddedAliasSameName
_ = emb.Bar
}
-- scopes.go --
package main
const globalConst = 1
type globalType int
var (
globalVar = 1
globalVarTyped globalType = 1
)
func scopesTest() {
println(globalVar, globalConst, globalVarTyped)
const localNameConst = 1
localNameShort := 4
type localNameType int
var (
localNameVar = 5
localNameTypeVar localNameType = 1
)
println(localNameConst, localNameShort, localNameVar, localNameTypeVar, input("input"))
}
func input(localNameParam string) (localNameReturn string) { return localNameParam }
-- sub/names.go --
package sub
import (
"io"
"test/main/external"
)
var someGlobalVar0 = "0"
var someGlobalVar1 = "1"
var someGlobalVar2 = "2"
func Test() {
var A, B, C, D, E string
noop(A, B, C, D, E)
if someGlobalVar0 != "0" || someGlobalVar1 != "1" || someGlobalVar2 != "2"{
panic("name collision detected")
}
}
func noop(...any) {}
// Funcs that almost look like test funcs used to make garble panic.
func TestFoo(s string) {}
func TestBar(*struct{}) {}
// If we obfuscate the alias name, we must obfuscate its use here too.
type EmbeddingAlias struct {
EmbeddedAlias
properly record when type aliases are embedded as fields There are two scenarios when it comes to embedding fields. The first is easy, and we always handled it well: type Named struct { Foo int } type T struct { Named } In this scenario, T ends up with an embedded field named "Named", and a promoted field named "Foo". Then there's the form with a type alias: type Named struct { Foo int } type Alias = Named type T struct { Alias } This case is different: T ends up with an embedded field named "Alias", and a promoted field named "Foo". Note how the field gets its name from the referenced type, even if said type is just an alias to another type. This poses two problems. First, we must obfuscate the field T.Alias as the name "Alias", and not as the name "Named" that the alias points to. Second, we must be careful of cases where Named and Alias are declared in different packages, as they will obfuscate the same name differently. Both of those problems compounded in the reported issue. The actual reason is that quic-go has a type alias in the form of: type ConnectionState = qtls.ConnectionState In other words, the entire problem boils down to a type alias which points to a named type in a different package, where both types share the same name. For example: package parent import "parent/p1" type T struct { p1.SameName } [...] package p1 import "parent/p2" type SameName = p2.SameName [...] package p2 type SameName struct { Foo int } This broke garble because we had a heuristic to detect when an embedded field was a type alias: // Instead, detect such a "foreign alias embed". // If we embed a final named type, // but the field name does not match its name, // then it must have been done via an alias. // We dig out the alias's TypeName via locateForeignAlias. if named.Obj().Name() != node.Name { As the reader can deduce, this heuristic would incorrectly assume that the snippet above does not embed a type alias, when in fact it does. When obfuscating the field T.SameName, which uses a type alias, we would correctly obfuscate the name "SameName", but we would incorrectly obfuscate it with the package p2, not p1. This would then result in build errors. To fix this problem for good, we need to get rid of the heuristic. Instead, we now mimic what was done for KnownCannotObfuscate, but for embedded fields which use type aliases. KnownEmbeddedAliasFields is now filled for each package and stored in the cache as part of cachedOutput. We can then detect the "embedded alias" case reliably, even when the field is declared in an imported package. On the plus side, we get to remove locateForeignAlias. We also add a couple of TODOs to record further improvements. Finally, add a test. Fixes #466.
2 years ago
EmbeddedAliasSameName
}
properly record when type aliases are embedded as fields There are two scenarios when it comes to embedding fields. The first is easy, and we always handled it well: type Named struct { Foo int } type T struct { Named } In this scenario, T ends up with an embedded field named "Named", and a promoted field named "Foo". Then there's the form with a type alias: type Named struct { Foo int } type Alias = Named type T struct { Alias } This case is different: T ends up with an embedded field named "Alias", and a promoted field named "Foo". Note how the field gets its name from the referenced type, even if said type is just an alias to another type. This poses two problems. First, we must obfuscate the field T.Alias as the name "Alias", and not as the name "Named" that the alias points to. Second, we must be careful of cases where Named and Alias are declared in different packages, as they will obfuscate the same name differently. Both of those problems compounded in the reported issue. The actual reason is that quic-go has a type alias in the form of: type ConnectionState = qtls.ConnectionState In other words, the entire problem boils down to a type alias which points to a named type in a different package, where both types share the same name. For example: package parent import "parent/p1" type T struct { p1.SameName } [...] package p1 import "parent/p2" type SameName = p2.SameName [...] package p2 type SameName struct { Foo int } This broke garble because we had a heuristic to detect when an embedded field was a type alias: // Instead, detect such a "foreign alias embed". // If we embed a final named type, // but the field name does not match its name, // then it must have been done via an alias. // We dig out the alias's TypeName via locateForeignAlias. if named.Obj().Name() != node.Name { As the reader can deduce, this heuristic would incorrectly assume that the snippet above does not embed a type alias, when in fact it does. When obfuscating the field T.SameName, which uses a type alias, we would correctly obfuscate the name "SameName", but we would incorrectly obfuscate it with the package p2, not p1. This would then result in build errors. To fix this problem for good, we need to get rid of the heuristic. Instead, we now mimic what was done for KnownCannotObfuscate, but for embedded fields which use type aliases. KnownEmbeddedAliasFields is now filled for each package and stored in the cache as part of cachedOutput. We can then detect the "embedded alias" case reliably, even when the field is declared in an imported package. On the plus side, we get to remove locateForeignAlias. We also add a couple of TODOs to record further improvements. Finally, add a test. Fixes #466.
2 years ago
type EmbeddedAlias = external.NamedExternal
properly record when type aliases are embedded as fields There are two scenarios when it comes to embedding fields. The first is easy, and we always handled it well: type Named struct { Foo int } type T struct { Named } In this scenario, T ends up with an embedded field named "Named", and a promoted field named "Foo". Then there's the form with a type alias: type Named struct { Foo int } type Alias = Named type T struct { Alias } This case is different: T ends up with an embedded field named "Alias", and a promoted field named "Foo". Note how the field gets its name from the referenced type, even if said type is just an alias to another type. This poses two problems. First, we must obfuscate the field T.Alias as the name "Alias", and not as the name "Named" that the alias points to. Second, we must be careful of cases where Named and Alias are declared in different packages, as they will obfuscate the same name differently. Both of those problems compounded in the reported issue. The actual reason is that quic-go has a type alias in the form of: type ConnectionState = qtls.ConnectionState In other words, the entire problem boils down to a type alias which points to a named type in a different package, where both types share the same name. For example: package parent import "parent/p1" type T struct { p1.SameName } [...] package p1 import "parent/p2" type SameName = p2.SameName [...] package p2 type SameName struct { Foo int } This broke garble because we had a heuristic to detect when an embedded field was a type alias: // Instead, detect such a "foreign alias embed". // If we embed a final named type, // but the field name does not match its name, // then it must have been done via an alias. // We dig out the alias's TypeName via locateForeignAlias. if named.Obj().Name() != node.Name { As the reader can deduce, this heuristic would incorrectly assume that the snippet above does not embed a type alias, when in fact it does. When obfuscating the field T.SameName, which uses a type alias, we would correctly obfuscate the name "SameName", but we would incorrectly obfuscate it with the package p2, not p1. This would then result in build errors. To fix this problem for good, we need to get rid of the heuristic. Instead, we now mimic what was done for KnownCannotObfuscate, but for embedded fields which use type aliases. KnownEmbeddedAliasFields is now filled for each package and stored in the cache as part of cachedOutput. We can then detect the "embedded alias" case reliably, even when the field is declared in an imported package. On the plus side, we get to remove locateForeignAlias. We also add a couple of TODOs to record further improvements. Finally, add a test. Fixes #466.
2 years ago
type EmbeddedAliasSameName = external.EmbeddedAliasSameName
// We obfuscate the name foreignAlias, but not the name Reader,
// as it's not declared in a private package.
// Both names must do the same when used as struct fields,
// both in the struct declaration and in the composite literal below.
type embeddingForeignAlias struct {
foreignAlias
io.Reader
}
type foreignAlias = io.Reader
var _ = embeddingForeignAlias{
foreignAlias: nil,
Reader: nil,
}
// Similar to embeddingForeignAlias,
// but the alias is declared in a dependency,
// and this type is used in a dependent.
type EmbeddingExternalForeignAlias struct {
external.ExternalForeignAlias
io.Reader
}
// Like the cases above,
// but this time the alias doesn't rename its destination named type.
// We can't tell this apart from "struct { io.Reader }" at the type info level.
// It's fine to ignore the alias entirely, in this case.
type embeddingAliasSameName struct {
external.Reader
}
var _ = embeddingAliasSameName{
Reader: nil,
}
-- external/external.go --
package external
import "io"
properly record when type aliases are embedded as fields There are two scenarios when it comes to embedding fields. The first is easy, and we always handled it well: type Named struct { Foo int } type T struct { Named } In this scenario, T ends up with an embedded field named "Named", and a promoted field named "Foo". Then there's the form with a type alias: type Named struct { Foo int } type Alias = Named type T struct { Alias } This case is different: T ends up with an embedded field named "Alias", and a promoted field named "Foo". Note how the field gets its name from the referenced type, even if said type is just an alias to another type. This poses two problems. First, we must obfuscate the field T.Alias as the name "Alias", and not as the name "Named" that the alias points to. Second, we must be careful of cases where Named and Alias are declared in different packages, as they will obfuscate the same name differently. Both of those problems compounded in the reported issue. The actual reason is that quic-go has a type alias in the form of: type ConnectionState = qtls.ConnectionState In other words, the entire problem boils down to a type alias which points to a named type in a different package, where both types share the same name. For example: package parent import "parent/p1" type T struct { p1.SameName } [...] package p1 import "parent/p2" type SameName = p2.SameName [...] package p2 type SameName struct { Foo int } This broke garble because we had a heuristic to detect when an embedded field was a type alias: // Instead, detect such a "foreign alias embed". // If we embed a final named type, // but the field name does not match its name, // then it must have been done via an alias. // We dig out the alias's TypeName via locateForeignAlias. if named.Obj().Name() != node.Name { As the reader can deduce, this heuristic would incorrectly assume that the snippet above does not embed a type alias, when in fact it does. When obfuscating the field T.SameName, which uses a type alias, we would correctly obfuscate the name "SameName", but we would incorrectly obfuscate it with the package p2, not p1. This would then result in build errors. To fix this problem for good, we need to get rid of the heuristic. Instead, we now mimic what was done for KnownCannotObfuscate, but for embedded fields which use type aliases. KnownEmbeddedAliasFields is now filled for each package and stored in the cache as part of cachedOutput. We can then detect the "embedded alias" case reliably, even when the field is declared in an imported package. On the plus side, we get to remove locateForeignAlias. We also add a couple of TODOs to record further improvements. Finally, add a test. Fixes #466.
2 years ago
type NamedExternal struct {
Foo int
}
type EmbeddedAliasSameName struct {
Bar int
}
type ExternalForeignAlias = io.Reader
type Reader = io.Reader
-- main.stderr --
nil case
1 1 1
1 4 5 1 input
This is a separate module to obfuscate.
This func is never inlined.
This func is always inlined.