Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

default to GOGARBLE=*, stop using GOPRIVATE #616

Merged
merged 2 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and adds initial support for the upcoming Go 1.20.

Noteworthy changes include:

* `GOGARBLE=*` is now the default to obfuscate all packages - [#594]
* `GOPRIVATE` is no longer used, being deprecated in [v0.5.0]
* Obfuscate assembly source code filenames - [#605]
* Randomize the lengths of obfuscated names
* Support obfuscating `time` and `syscall`
Expand Down Expand Up @@ -157,6 +159,7 @@ Known bugs:
* obfuscating the standard library with `GOPRIVATE=*` is not well supported yet
* `garble test` is temporarily disabled, as it is currently broken

[#594]: https://github.com/burrowers/garble/issues/594
[#605]: https://github.com/burrowers/garble/issues/605

[v0.7.2]: https://github.com/burrowers/garble/releases/tag/v0.7.2
Expand Down
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,10 @@ order to:
* [Obfuscate literals](#literal-obfuscation), if the `-literals` flag is given
* Remove [extra information](#tiny-mode), if the `-tiny` flag is given

The tool obfuscates the packages matching `GOGARBLE`, a comma-separated list of
glob patterns of module path prefixes, as documented in `go help private`.
To obfuscate all the packages in a build, use `GOGARBLE=*`.
When `GOGARBLE` is empty, it assumes the value of `GOPRIVATE`.
When `GOPRIVATE` is also empty, then `GOGARBLE` assumes the value of the current
module path, to obfuscate all packages under the current module.
By default, the tool obfuscates all the packages being built.
You can manually specify which packages to obfuscate via `GOGARBLE`,
a comma-separated list of glob patterns matching package path prefixes.
This format is borrowed from `GOPRIVATE`; see `go help private`.

Note that commands like `garble build` will use the `go` version found in your
`$PATH`. To use different versions of Go, you can
Expand Down
1 change: 0 additions & 1 deletion bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ func BenchmarkBuild(b *testing.B) {
gocache, err := os.MkdirTemp(b.TempDir(), "gocache-*")
qt.Assert(b, err, qt.IsNil)
env := append(os.Environ(),
"GOGARBLE=*",
"GOCACHE="+gocache,
"GARBLE_WRITE_ALLOCS=true",
)
Expand Down
4 changes: 1 addition & 3 deletions hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ func addGarbleToHash(inputHash []byte) []byte {
// We also need to add the selected options to the full version string,
// because all of them result in different output. We use spaces to
// separate the env vars and flags, to reduce the chances of collisions.
if cache.GOGARBLE != "" {
fmt.Fprintf(hasher, " GOGARBLE=%s", cache.GOGARBLE)
}
fmt.Fprintf(hasher, " GOGARBLE=%s", cache.GOGARBLE)
appendFlags(hasher, true)
// addGarbleToHash returns the sum buffer, so we need a new copy.
// Otherwise the next use of the global sumBuffer would conflict.
Expand Down
57 changes: 31 additions & 26 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import (

"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
"golang.org/x/tools/go/ast/astutil"
Expand Down Expand Up @@ -998,7 +997,20 @@ func (tf *transformer) transformLinkname(localName, newName string) (string, str

lpkg, err := listPackage(pkgPath)
if err != nil {
// Probably a made up name like above, but with a dot.
// TODO(mvdan): use errors.As or errors.Is instead
if strings.Contains(err.Error(), "path not found") {
// Probably a made up name like above, but with a dot.
return localName, newName
}
if strings.Contains(err.Error(), "refusing to list") {
fmt.Fprintf(os.Stderr,
"//go:linkname refers to %s - add `import _ %q` so garble can find the package",
newName, pkgPath)
return localName, newName
}
if err != nil {
panic(err) // shouldn't happen
}
return localName, newName
}
if lpkg.ToObfuscate {
Expand Down Expand Up @@ -1729,12 +1741,19 @@ func (tf *transformer) transformGo(file *ast.File) *ast.File {
return true // universe scope
}

if pkg.Path() == "embed" {
// The Go compiler needs to detect types such as embed.FS.
// That will fail if we change the import path or type name.
// Leave it as is.
// Luckily, the embed package just declares the FS type.
return true
// The Go toolchain needs to detect symbols from these packages,
// so we are not obfuscating their package paths or declared names.
switch pkg.Path() {
case "embed":
// FS is detected by the compiler for //go:embed.
return name == "FS"
case "reflect":
// Per the linker's deadcode.go docs,
// the Method and MethodByName methods are what drive the logic.
switch name {
case "Method", "MethodByName":
return true
}
}

// The package that declared this object did not obfuscate it.
Expand Down Expand Up @@ -2185,7 +2204,8 @@ func flagSetValue(flags []string, name, value string) []string {

func fetchGoEnv() error {
out, err := exec.Command("go", "env", "-json",
"GOOS", "GOPRIVATE", "GOMOD", "GOVERSION", "GOCACHE",
// Keep in sync with sharedCache.GoEnv.
"GOOS", "GOMOD", "GOVERSION",
).CombinedOutput()
if err != nil {
// TODO: cover this in the tests.
Expand All @@ -2201,23 +2221,8 @@ To install Go, see: https://go.dev/doc/install
return fmt.Errorf(`cannot unmarshal from "go env -json": %w`, err)
}
cache.GOGARBLE = os.Getenv("GOGARBLE")
if cache.GOGARBLE != "" {
// GOGARBLE is non-empty; nothing to do.
} else if cache.GoEnv.GOPRIVATE != "" {
// GOGARBLE is empty and GOPRIVATE is non-empty.
// Set GOGARBLE to GOPRIVATE's value.
cache.GOGARBLE = cache.GoEnv.GOPRIVATE
} else {
// If GOPRIVATE isn't set and we're in a module, use its module
// path as a GOPRIVATE default. Include a _test variant too.
// TODO(mvdan): we shouldn't need the _test variant here,
// as the import path should not include it; only the package name.
if mod, err := os.ReadFile(cache.GoEnv.GOMOD); err == nil {
modpath := modfile.ModulePath(mod)
if modpath != "" {
cache.GOGARBLE = modpath + "," + modpath + "_test"
}
}
if cache.GOGARBLE == "" {
cache.GOGARBLE = "*" // we default to obfuscating everything
}
return nil
}
2 changes: 0 additions & 2 deletions scripts/check-third-party.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ modules=(

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

export GOGARBLE="*"

exit_code=0

show() {
Expand Down
18 changes: 12 additions & 6 deletions shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,12 @@ type sharedCache struct {
GOGARBLE string

// Filled directly from "go env".
// Remember to update the exec call when adding or removing names.
// Keep in sync with fetchGoEnv.
GoEnv struct {
GOOS string // i.e. the GOOS build target

GOPRIVATE string
GOMOD string
GOVERSION string
GOCACHE string
}
}

Expand Down Expand Up @@ -160,9 +158,17 @@ type listedPackage struct {
}

func (p *listedPackage) obfuscatedImportPath() string {
// We can't obfuscate the embed package's import path,
// as the toolchain expects to recognize the package by it.
if p.ImportPath == "embed" || !p.ToObfuscate {
// We can't obfuscate these standard library import paths,
// as the toolchain expects to recognize the packages by them:
//
// * runtime: it is special in many ways
// * reflect: its presence turns down dead code elimination
// * embed: its presence enables using //go:embed
switch p.ImportPath {
case "runtime", "reflect", "embed":
return p.ImportPath
}
if !p.ToObfuscate {
return p.ImportPath
}
newPath := hashWithPackage(p, p.ImportPath)
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/asm.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
# TODO: support arm64, at least
[!amd64] skip 'the assembly is only written for amd64'

env GOGARBLE=test/main

garble build
exec ./main
cmp stderr main.stderr
Expand Down
8 changes: 0 additions & 8 deletions testdata/script/cgo.txtar
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
[!cgo] skip 'this test requires cgo to be enabled'

env GOGARBLE=test/main

garble build
! stderr 'warning' # check that the C toolchain is happy
exec ./main
Expand All @@ -19,12 +17,6 @@ garble reverse .
cmp stdout reversed.stdout
env GARBLE_TEST_REVERSING=false

env GOGARBLE=*
garble build
exec ./main
cmp stdout main.stdout
env GOGARBLE=test/main

garble -tiny build
exec ./main
cmp stdout main.stdout
Expand Down
1 change: 0 additions & 1 deletion testdata/script/crossbuild.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
[arm] env GOARCH=arm64

# A fairly average Go build, importing some std libraries.
env GOGARBLE='*'
garble build
-- go.mod --
module test/main
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/debugdir.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=*

garble -debugdir ./debug1 build
exists 'debug1/test/main/imported/imported.go' 'debug1/test/main/main.go' 'debug1/reflect/type.go'
exists 'debug1/runtime/map.go' 'debug1/runtime/funcdata.h' 'debug1/runtime/asm.s'
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/embed.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=*

garble build

exec ./main
Expand Down
8 changes: 2 additions & 6 deletions testdata/script/gogarble.txtar
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
# Ensure that "does not match any packages" works with GOPRIVATE and GOGARBLE.
# Ensure that "does not match any packages" works.
env GOGARBLE=match-absolutely/nothing
! garble build -o=out ./standalone
stderr '^GOGARBLE="match-absolutely/nothing" does not match any packages to be built$'

env GOGARBLE=
env GOPRIVATE=match-absolutely/nothing
! garble build -o=out ./standalone
stderr '^GOGARBLE="match-absolutely/nothing" does not match any packages to be built$'

# A build where just some packages are obfuscated.
env GOGARBLE=test/main/imported
garble -literals build -o=out ./importer
Expand All @@ -23,6 +18,7 @@ garble build -o=out ./stdimporter

[short] stop # rebuilding std is slow

# Go back to the default of obfuscating all packages.
env GOGARBLE='*'

# Try garbling all of std, given some std packages.
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/help.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=*

! garble
stderr 'Garble obfuscates Go code'
stderr 'garble \[garble flags\] command'
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/implement.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

garble build
exec ./main
cmp stdout main.stdout
Expand Down
6 changes: 2 additions & 4 deletions testdata/script/imports.txtar
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Note that this is the only test with a module where we rely on the detection
# of GOGARBLE.
# Also note that, since this is the only test using "real" external modules
# fetched via GOPROXY, go.mod and go.sum should declare the dependencies.
# Since this is the only test using "real" external modules fetched via GOPROXY,
# go.mod and go.sum should declare the dependencies.

# For now, use a throwaway module download cache instead of the host machine's.
# Usually it would be fine to reuse the host's, since we expose exact copies of
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/ldflags.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=*

# Note the proper domain, since the dot adds an edge case.
#
# Also note that there are three forms of -X allowed:
Expand Down
3 changes: 1 addition & 2 deletions testdata/script/linkname.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main,big.chungus/meme

garble build
exec ./main
cmp stderr main.stderr
Expand Down Expand Up @@ -76,6 +74,7 @@ func main() {
package imported

import (
_ "strings"
_ "unsafe"
)

Expand Down
3 changes: 0 additions & 3 deletions testdata/script/literals.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=*

garble -literals build
exec ./main$exe
cmp stderr main.stderr
Expand Down Expand Up @@ -52,7 +50,6 @@ grep '^\s+\w+ = .*\bappend\(\w+,(\s+\w+\[\d+\][\^\-+]\w+\[\d+\],?)+\)$' debug1/t

# Finally, sanity check that we can build all of std with -literals.
# Analogous to gogarble.txt.
env GOGARBLE='*'
garble -literals build std
-- go.mod --
module test/main
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/modinfo.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

[exec:git] exec git init -q
[exec:git] exec git config user.name "name"
[exec:git] exec git config user.email "[email protected]"
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/plugin.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ skip # TODO: get plugins working properly. See issue #87

[windows] skip 'Go plugins are not supported on Windows'

env GOGARBLE=test/main

garble build -buildmode=plugin ./plugin
binsubstr plugin.so 'PublicVar' 'PublicFunc'
! binsubstr plugin.so 'privateFunc'
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/position.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

garble build
exec ./main
! stdout 'garble_main\.go|garble_other_filename|is sorted'
Expand Down
4 changes: 2 additions & 2 deletions testdata/script/reflect.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

garble build
exec ./main
cmp stdout main.stdout
Expand Down Expand Up @@ -32,6 +30,8 @@ import (
"test/main/importedpkg2"
)

var Sink interface{}

func main() {
// Fields still work fine when they are not obfuscated.
fmt.Println(importedpkg.ReflectInDefinedVar.ExportedField2)
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/reverse.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

# Unknown build flags should result in errors.
! garble reverse -badflag=foo .
stderr 'flag provided but not defined'
Expand Down
1 change: 0 additions & 1 deletion testdata/script/seed-cache.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
# In the past, this threw off garble's extra cached gob files.

env SEED1=OQg9kACEECQ
env GOGARBLE=*

# First, ensure that mod1's garbletest.v2 is in the cache.
cd mod1
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/seed.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

# 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,
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/syntax.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE='test/main,private.source'

garble build
exec ./main$exe
cmp stderr main.stderr
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/tiny.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=test/main

# Tiny mode
garble -tiny build
! binsubstr main$exe 'garble_main.go' 'fmt/print.go'
Expand Down
2 changes: 0 additions & 2 deletions testdata/script/typeparams.txtar
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
env GOGARBLE=*

garble build
! binsubstr main$exe ${WORK} 'garble_main.go' 'GenericFunc' 'GenericVector' 'PredeclaredSignedInteger' 'StringableSignedInteger' 'CombineEmbeds' 'GenericParam'
-- go.mod --
Expand Down