Obfuscate more packages of the standard library (#312)

Also update linkname directives of public packages,
to allow the package where something is linknamed to to be
obfuscated regardless.

Public packages can now depend on private packages.
pull/407/head
lu4p 3 years ago committed by GitHub
parent e7320ec9c0
commit 88f238e558
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -622,13 +622,8 @@ func transformCompile(args []string) ([]string, error) {
return nil, err
}
if curPkg.ImportPath == "runtime" && opts.Tiny {
// When using -tiny, we call stripRuntime below.
// We don't want -literals and -debugdir to apply, though.
if runtimeAndDeps[curPkg.ImportPath] {
opts.GarbleLiterals = false
opts.DebugDir = ""
} else if !curPkg.Private {
return append(flags, paths...), nil
}
// Literal obfuscation uses math/rand, so seed it deterministically.
@ -707,9 +702,6 @@ func transformCompile(args []string) ([]string, error) {
// Right now, this means recording what local names are used with go:linkname,
// and rewriting those directives to use obfuscated name from other packages.
func (tf *transformer) handleDirectives(comments []*ast.CommentGroup) {
if !curPkg.Private {
return
}
for _, group := range comments {
for _, comment := range group.List {
if !strings.HasPrefix(comment.Text, "//go:linkname ") {
@ -722,8 +714,10 @@ func (tf *transformer) handleDirectives(comments []*ast.CommentGroup) {
}
// This directive has two arguments: "go:linkname localName newName"
// obfuscate the local name.
fields[1] = hashWith(curPkg.GarbleActionID, fields[1])
// obfuscate the local name, if the current package is obfuscated
if curPkg.Private {
fields[1] = hashWith(curPkg.GarbleActionID, fields[1])
}
// If the new name is of the form "pkgpath.Name", and
// we've obfuscated "Name" in that package, rewrite the
@ -770,84 +764,56 @@ func (tf *transformer) handleDirectives(comments []*ast.CommentGroup) {
}
}
// runtimeRelated is a snapshot of all the packages runtime depends on, or
// cannotObfuscate is a list of some packages the runtime depends on, or
// packages which the runtime points to via go:linkname.
//
// Once we support go:linkname well and once we can obfuscate the runtime
// package, this entire map can likely go away.
//
// The list was obtained via scripts/runtime-related.sh on Go 1.17.
var runtimeRelated = map[string]bool{
"bufio": true,
"bytes": true,
"compress/flate": true,
"compress/gzip": true,
"context": true,
"crypto/x509/internal/macos": true,
"encoding/binary": true,
"errors": true,
"fmt": true,
"hash": true,
"hash/crc32": true,
"internal/abi": true,
"internal/bytealg": true,
"internal/cpu": true,
"internal/fmtsort": true,
"internal/goexperiment": true,
"internal/itoa": true,
"internal/nettrace": true,
"internal/oserror": true,
"internal/poll": true,
"internal/race": true,
"internal/reflectlite": true,
"internal/singleflight": true,
"internal/syscall/execenv": true,
"internal/syscall/unix": true,
"internal/syscall/windows": true,
"internal/syscall/windows/registry": true,
"internal/syscall/windows/sysdll": true,
"internal/testlog": true,
"internal/unsafeheader": true,
"io": true,
"io/fs": true,
"math": true,
"math/bits": true,
"net": true,
"os": true,
"os/signal": true,
"path": true,
"plugin": true,
"reflect": true,
"runtime": true,
"runtime/cgo": true,
"runtime/debug": true,
"runtime/internal/atomic": true,
"runtime/internal/math": true,
"runtime/internal/sys": true,
"runtime/metrics": true,
"runtime/pprof": true,
"runtime/trace": true,
"sort": true,
"strconv": true,
"strings": true,
"sync": true,
"sync/atomic": true,
"syscall": true,
"text/tabwriter": true,
"time": true,
"unicode": true,
"unicode/utf16": true,
"unicode/utf8": true,
"unsafe": true,
"vendor/golang.org/x/net/dns/dnsmessage": true,
"vendor/golang.org/x/net/route": true,
// TODO: investigate and resolve each one of these
var cannotObfuscate = map[string]bool{
// not a "real" package
"unsafe": true,
// some linkname failure
"time": true,
"runtime/pprof": true,
// all kinds of stuff breaks when obfuscating the runtime
"syscall": true,
"internal/abi": true,
// rebuilds don't work
"os/signal": true,
// cgo breaks otherwise
"runtime/cgo": true,
// garble reverse breaks otherwise
"runtime/debug": true,
// cgo heavy net doesn't like to be obfuscated
"net": true,
// some linkname failure
"crypto/x509/internal/macos": true,
}
// We can't obfuscate literals in the runtime and its dependencies,
// because obfuscated literals sometimes escape to heap,
// and that's not allowed in the runtime itself.
var runtimeAndDeps = map[string]bool{
"runtime": true,
"runtime/internal/sys": true,
"internal/cpu": true,
"runtime/internal/atomic": true,
}
// isPrivate checks if a package import path should be considered private,
// meaning that it should be obfuscated.
func isPrivate(path string) bool {
// We don't support obfuscating these yet.
if runtimeRelated[path] {
if cannotObfuscate[path] || runtimeAndDeps[path] {
return false
}
// These are main packages, so we must always obfuscate them.

@ -1,19 +0,0 @@
#!/bin/bash
# This script is hacky, but lets us list all packages depended on by runtime, or
# related to runtime via go:linkname.
#
# Once we can obfuscate the runtime package, this script can probably be
# deleted.
go version
echo
for GOOS in linux darwin windows; do
skip="|macos"
if [[ $GOOS == "darwin" ]]; then
skip=""
fi
GOOS=$GOOS go list -deps $(sed -rn 's@//go:linkname .* ([^.]*)\.[^.]*@\1@p' $(go env GOROOT)/src/runtime/*.go | grep -vE '^main|^runtime\.|js'$skip) runtime || exit 1
done | sort -u

@ -249,17 +249,6 @@ func setListedPackages(patterns []string) error {
if !anyPrivate {
return fmt.Errorf("GOPRIVATE=%q does not match any packages to be built", os.Getenv("GOPRIVATE"))
}
for path, pkg := range cache.ListedPackages {
if pkg.Private {
continue
}
for _, depPath := range pkg.Deps {
if cache.ListedPackages[depPath].Private {
return fmt.Errorf("public package %q can't depend on obfuscated package %q (matched via GOPRIVATE=%q)",
path, depPath, os.Getenv("GOPRIVATE"))
}
}
}
return nil
}

@ -3,8 +3,7 @@ env GOPRIVATE=match-absolutely/nothing
stderr '^GOPRIVATE="match-absolutely/nothing" does not match any packages to be built$'
env GOPRIVATE=test/main/imported
! garble build ./importer
stderr '^public package "test/main/importer" can''t depend on obfuscated package "test/main/imported" \(matched via GOPRIVATE="test/main/imported"\)$'
garble build ./importer
[short] stop # rebuilding std is slow

@ -0,0 +1,55 @@
# test that we used all necessary dependencies
[linux] exec bash runtime-related-tested.sh
env GOPRIVATE=*
garble build
-- runtime-related.sh --
for GOOS in linux darwin windows; do
skip="|macos"
if [[ $GOOS == "darwin" ]]; then
skip=""
fi
GOOS=$GOOS go list -deps $(sed -rn 's@//go:linkname .* ([^.]*)\.[^.]*@\1@p' $(go env GOROOT)/src/runtime/*.go | grep -vE '^main|^runtime\.|js'$skip) runtime || exit 1
done | sort -u
-- runtime-related-tested.sh --
# get all runtime-related deps
related=$(bash runtime-related.sh) || exit 1
# get all tested deps
tested=$(for GOOS in linux darwin windows; do GOOS=$GOOS go list -deps || exit 1; done | sort -u)
# remove all tested deps from the runtime-related deps
output=$(echo "$related" | grep -Fvx -e "$tested")
# output should be empty if all runtime-related deps are tested
[[ -z "$output" ]] || (echo "$output" && exit 1)
-- go.mod --
module test/main
go 1.17
-- main.go --
package main
import (
"net/http/pprof"
"os/signal"
"plugin"
"runtime/debug"
"runtime/metrics"
"text/tabwriter"
)
// This program imports all runtime-related dependencies (proven by runtime-related-tested.sh)
func main() {
_ = tabwriter.AlignRight
signal.Ignore()
_ = plugin.Plugin{}
_ = pprof.Handler("")
_ = debug.GCStats{}
metrics.All()
}
Loading…
Cancel
Save