mirror of
https://github.com/burrowers/garble
synced 2026-06-06 15:24:28 +00:00
30357af923
This lets us start taking advantage of featurs from Go 1.23, particularly tracking aliases in go/types and iterators. Note that we need to add code to properly handle or skip over the new *types.Alias type which go/types produces for Go type aliases. Also note that we actually turn this mode off entirely for now, due to the bug reported at https://go.dev/issue/70394. We don't yet remove our own alias tracking code yet due to the above. We hope to be able to remove it very soon.
234 lines
6.6 KiB
Plaintext
234 lines
6.6 KiB
Plaintext
# Note that in this test we use "! bincmp" on plaintext output files,
|
|
# as a workaround for "cmp" not supporting "! cmp".
|
|
# TODO: now that obfuscation with -seed is deterministic,
|
|
# can we just rely on the regular "cmp" with fixed output files?
|
|
|
|
# TODO: consider setting these seeds globally,
|
|
# so we can reuse them across tests and make better use of the shared build cache.
|
|
env SEED1=OQg9kACEECQ
|
|
env SEED2=NruiDmVz6/s
|
|
|
|
# Check the binary with a given base64 encoded seed.
|
|
exec garble -seed=${SEED1} build
|
|
exec ./main$exe
|
|
cmp stderr main.stderr
|
|
binsubstr main$exe 'teststring' 'imported var value'
|
|
! binsubstr main$exe 'ImportedVar' ${SEED1}
|
|
|
|
[short] stop # the extra checks are relatively expensive
|
|
|
|
exec ./main$exe test/main/imported
|
|
cp stderr importedpkg-seed-static-1
|
|
|
|
# Also check that the binary is reproducible.
|
|
# No packages should be rebuilt either, thanks to the build cache.
|
|
cp main$exe main_seed1$exe
|
|
rm main$exe
|
|
exec garble -seed=${SEED1}= build -v
|
|
! stderr .
|
|
bincmp main$exe main_seed1$exe
|
|
|
|
exec ./main$exe test/main/imported
|
|
cmp stderr importedpkg-seed-static-1
|
|
|
|
# Even if we use the same seed, the same names in a different package
|
|
# should still be obfuscated in a different way.
|
|
exec ./main$exe test/main
|
|
cp stderr mainpkg-seed-static-1
|
|
! bincmp mainpkg-seed-static-1 importedpkg-seed-static-1
|
|
|
|
# Using different flags which affect the build, such as -literals or -tiny,
|
|
# should result in the same obfuscation as long as the seed is constant.
|
|
# TODO: also test that changing non-garble build parameters,
|
|
# such as GOARCH or -tags, still results in the same hashing via the seed.
|
|
|
|
exec garble -seed=${SEED1} -literals build
|
|
exec ./main$exe test/main/imported
|
|
cmp stderr importedpkg-seed-static-1
|
|
|
|
exec garble -seed=${SEED1} -tiny build
|
|
exec ./main$exe test/main/imported
|
|
cmp stderr importedpkg-seed-static-1
|
|
|
|
# Also check that a different seed leads to a different binary.
|
|
# We can't know if caching happens here, because of previous test runs.
|
|
cp main$exe main_seed2$exe
|
|
rm main$exe
|
|
exec garble -seed=${SEED2} build
|
|
! bincmp main$exe main_seed2$exe
|
|
|
|
exec ./main$exe test/main/imported
|
|
cp stderr importedpkg-seed-static-2
|
|
! bincmp importedpkg-seed-static-2 importedpkg-seed-static-1
|
|
|
|
# Use a random seed, which should always trigger a full build.
|
|
exec garble -seed=random build -v
|
|
stderr -count=1 '^-seed chosen at random: .+'
|
|
stderr -count=1 '^runtime$'
|
|
stderr -count=1 '^test/main$'
|
|
exec ./main$exe
|
|
cmp stderr main.stderr
|
|
binsubstr main$exe 'teststring' 'imported var value'
|
|
! binsubstr main$exe 'ImportedVar'
|
|
|
|
exec ./main$exe test/main/imported
|
|
cp stderr importedpkg-seed-random-1
|
|
! bincmp importedpkg-seed-random-1 importedpkg-seed-static-1
|
|
|
|
# Also check that the random binary is not reproducible.
|
|
cp main$exe main_random$exe
|
|
rm main$exe
|
|
exec garble -seed=random build -v
|
|
stderr .
|
|
! bincmp main$exe main_random$exe
|
|
|
|
exec ./main$exe test/main/imported
|
|
cp stderr importedpkg-seed-random-2
|
|
! bincmp importedpkg-seed-random-2 importedpkg-seed-random-1
|
|
|
|
# Finally, ensure that our runtime and reflect test code does what we think.
|
|
go build
|
|
exec ./main$exe
|
|
cmp stderr main.stderr
|
|
exec ./main$exe test/main
|
|
cmp stderr mainpkg.stderr
|
|
exec ./main$exe test/main/imported
|
|
cmp stderr importedpkg.stderr
|
|
-- go.mod --
|
|
module test/main
|
|
|
|
go 1.23
|
|
-- main.go --
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
|
|
"test/main/imported"
|
|
)
|
|
|
|
var teststringVar = "teststring"
|
|
|
|
func main() { mainFunc() }
|
|
|
|
func mainFunc() {
|
|
if len(os.Args) > 1 {
|
|
switch os.Args[1] {
|
|
case "test/main":
|
|
imported.PrintNames(NamedTypeValue, NamedFunc)
|
|
case "test/main/imported":
|
|
imported.PrintNames(imported.NamedType{}, imported.NamedFunc)
|
|
default:
|
|
panic("unknown package")
|
|
}
|
|
} else {
|
|
println(teststringVar)
|
|
println(imported.ImportedVar)
|
|
|
|
// When we're obfuscating, check that the obfuscated name lengths vary.
|
|
// With sixteen hashed names, and a range of 7 (between 6 and 12),
|
|
// the chances of fourteen repeats are incredibly minuscule.
|
|
// If that happens, then our randomness is clearly broken.
|
|
if hashedNames[0] != "main.hashed_00" {
|
|
var count [16]int
|
|
for _, name := range hashedNames {
|
|
name = name[len("main."):]
|
|
if len(name) < 6 {
|
|
panic("ended up with a hashed name that's too short: "+name)
|
|
}
|
|
if len(name) > 12 {
|
|
panic("ended up with a hashed name that's too long: "+name)
|
|
}
|
|
count[len(name)]++
|
|
if count[len(name)] >= 14 {
|
|
for _, name := range hashedNames {
|
|
println(name)
|
|
}
|
|
panic("six or more hashed names with the same length")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A workaround to fool garble's reflect detection,
|
|
// because we want it to show us the obfuscated NamedType.
|
|
var NamedTypeValue any = NamedType{}
|
|
|
|
type NamedType struct {
|
|
NamedField int
|
|
}
|
|
|
|
func NamedFunc() string {
|
|
return imported.CallerFuncName()
|
|
}
|
|
|
|
var hashedNames = []string{
|
|
hashed_00(), hashed_01(), hashed_02(), hashed_03(),
|
|
hashed_04(), hashed_05(), hashed_06(), hashed_07(),
|
|
hashed_08(), hashed_09(), hashed_10(), hashed_11(),
|
|
hashed_12(), hashed_13(), hashed_14(), hashed_15(),
|
|
}
|
|
|
|
func hashed_00() string { return imported.CallerFuncName() }
|
|
func hashed_01() string { return imported.CallerFuncName() }
|
|
func hashed_02() string { return imported.CallerFuncName() }
|
|
func hashed_03() string { return imported.CallerFuncName() }
|
|
func hashed_04() string { return imported.CallerFuncName() }
|
|
func hashed_05() string { return imported.CallerFuncName() }
|
|
func hashed_06() string { return imported.CallerFuncName() }
|
|
func hashed_07() string { return imported.CallerFuncName() }
|
|
func hashed_08() string { return imported.CallerFuncName() }
|
|
func hashed_09() string { return imported.CallerFuncName() }
|
|
func hashed_10() string { return imported.CallerFuncName() }
|
|
func hashed_11() string { return imported.CallerFuncName() }
|
|
func hashed_12() string { return imported.CallerFuncName() }
|
|
func hashed_13() string { return imported.CallerFuncName() }
|
|
func hashed_14() string { return imported.CallerFuncName() }
|
|
func hashed_15() string { return imported.CallerFuncName() }
|
|
|
|
-- imported/imported.go --
|
|
package imported
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
)
|
|
|
|
var ImportedVar = "imported var value"
|
|
|
|
type NamedType struct {
|
|
NamedField int
|
|
}
|
|
|
|
func NamedFunc() string {
|
|
return CallerFuncName()
|
|
}
|
|
|
|
func PrintNames(v any, fn func() string) {
|
|
typ := reflect.TypeOf(v)
|
|
println("path:", typ.PkgPath())
|
|
println("type:", typ.Name())
|
|
println("field:", typ.Field(0).Name)
|
|
println("func: ", fn())
|
|
}
|
|
|
|
func CallerFuncName() string {
|
|
pc, _, _, _ := runtime.Caller(1)
|
|
fn := runtime.FuncForPC(pc)
|
|
return fn.Name()
|
|
}
|
|
-- main.stderr --
|
|
teststring
|
|
imported var value
|
|
-- mainpkg.stderr --
|
|
path: main
|
|
type: NamedType
|
|
field: NamedField
|
|
func: main.NamedFunc
|
|
-- importedpkg.stderr --
|
|
path: test/main/imported
|
|
type: NamedType
|
|
field: NamedField
|
|
func: test/main/imported.NamedFunc
|