diff options
Diffstat (limited to 'libgo/go/cmd')
-rw-r--r-- | libgo/go/cmd/cgo/doc.go | 16 | ||||
-rw-r--r-- | libgo/go/cmd/cgo/gcc.go | 6 | ||||
-rw-r--r-- | libgo/go/cmd/go/alldocs.go | 31 | ||||
-rw-r--r-- | libgo/go/cmd/go/go_test.go | 245 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/cache/default.go | 8 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/envcmd/env.go | 7 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/help/helpdoc.go | 31 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/load/pkg.go | 104 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/test/test.go | 18 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/work/exec.go | 90 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/work/security.go | 160 | ||||
-rw-r--r-- | libgo/go/cmd/go/internal/work/security_test.go | 240 |
12 files changed, 881 insertions, 75 deletions
diff --git a/libgo/go/cmd/cgo/doc.go b/libgo/go/cmd/cgo/doc.go index 8e4cd88b37c..c16b63a313b 100644 --- a/libgo/go/cmd/cgo/doc.go +++ b/libgo/go/cmd/cgo/doc.go @@ -45,8 +45,8 @@ For example: // #include <png.h> import "C" -Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config -tool using a '#cgo pkg-config:' directive followed by the package names. +Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config tool +using a '#cgo pkg-config:' directive followed by the package names. For example: // #cgo pkg-config: png cairo @@ -55,11 +55,21 @@ For example: The default pkg-config tool may be changed by setting the PKG_CONFIG environment variable. +For security reasons, only a limited set of flags are allowed, notably -D, -I, and -l. +To allow additional flags, set CGO_CFLAGS_ALLOW to a regular expression +matching the new flags. To disallow flags that would otherwise be allowed, +set CGO_CFLAGS_DISALLOW to a regular expression matching arguments +that must be disallowed. In both cases the regular expression must match +a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*', +not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control +the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS. + When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and CGO_LDFLAGS environment variables are added to the flags derived from these directives. Package-specific flags should be set using the directives, not the environment variables, so that builds work in -unmodified environments. +unmodified environments. Flags obtained from environment variables +are not subject to the security limitations described above. All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and used to compile C files in that package. All the CPPFLAGS and CXXFLAGS diff --git a/libgo/go/cmd/cgo/gcc.go b/libgo/go/cmd/cgo/gcc.go index fcf334ec44e..534fba17eb4 100644 --- a/libgo/go/cmd/cgo/gcc.go +++ b/libgo/go/cmd/cgo/gcc.go @@ -2345,12 +2345,6 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type { break } - // If we already know the typedef for t just use that. - // See issue 19832. - if def := typedef[t.Go.(*ast.Ident).Name]; def != nil { - break - } - t = c.Type(ptr, pos) if t == nil { return nil diff --git a/libgo/go/cmd/go/alldocs.go b/libgo/go/cmd/go/alldocs.go index 85b9d2584f2..5e1ac5aaf57 100644 --- a/libgo/go/cmd/go/alldocs.go +++ b/libgo/go/cmd/go/alldocs.go @@ -1227,17 +1227,26 @@ // CGO_CFLAGS // Flags that cgo will pass to the compiler when compiling // C code. -// CGO_CPPFLAGS -// Flags that cgo will pass to the compiler when compiling -// C or C++ code. -// CGO_CXXFLAGS -// Flags that cgo will pass to the compiler when compiling -// C++ code. -// CGO_FFLAGS -// Flags that cgo will pass to the compiler when compiling -// Fortran code. -// CGO_LDFLAGS -// Flags that cgo will pass to the compiler when linking. +// CGO_CFLAGS_ALLOW +// A regular expression specifying additional flags to allow +// to appear in #cgo CFLAGS source code directives. +// Does not apply to the CGO_CFLAGS environment variable. +// CGO_CFLAGS_DISALLOW +// A regular expression specifying flags that must be disallowed +// from appearing in #cgo CFLAGS source code directives. +// Does not apply to the CGO_CFLAGS environment variable. +// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the C preprocessor. +// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the C++ compiler. +// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the Fortran compiler. +// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the linker. // CXX // The command to use to compile C++ code. // PKG_CONFIG diff --git a/libgo/go/cmd/go/go_test.go b/libgo/go/cmd/go/go_test.go index 41a37a575b6..c5a3d7b36bc 100644 --- a/libgo/go/cmd/go/go_test.go +++ b/libgo/go/cmd/go/go_test.go @@ -81,6 +81,13 @@ func init() { skipExternal = true canRun = false } + case "plan9": + switch runtime.GOARCH { + case "arm": + // many plan9/arm machines are too slow to run + // the full set of external tests. + skipExternal = true + } case "windows": exeSuffix = ".exe" } @@ -2815,7 +2822,7 @@ func TestCgoHandlesWlORIGIN(t *testing.T) { defer tg.cleanup() tg.parallel() tg.tempFile("src/origin/origin.go", `package origin - // #cgo !darwin LDFLAGS: -Wl,-rpath -Wl,$ORIGIN + // #cgo !darwin LDFLAGS: -Wl,-rpath,$ORIGIN // void f(void) {} import "C" func f() { C.f() }`) @@ -5421,6 +5428,30 @@ func TestTestCacheInputs(t *testing.T) { } } +func TestNoCache(t *testing.T) { + switch runtime.GOOS { + case "windows": + t.Skipf("no unwritable directories on %s", runtime.GOOS) + } + if os.Getuid() == 0 { + t.Skip("skipping test because running as root") + } + + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("triv.go", `package main; func main() {}`) + tg.must(os.MkdirAll(tg.path("unwritable"), 0555)) + home := "HOME" + if runtime.GOOS == "plan9" { + home = "home" + } + tg.setenv(home, tg.path(filepath.Join("unwritable", "home"))) + tg.unsetenv("GOCACHE") + tg.run("build", "-o", tg.path("triv"), tg.path("triv.go")) + tg.grepStderr("disabling cache", "did not disable cache") +} + func TestTestVet(t *testing.T) { tooSlow(t) tg := testgo(t) @@ -5463,6 +5494,44 @@ func TestTestVet(t *testing.T) { tg.grepStdout(`ok\s+vetfail/p2`, "did not run vetfail/p2") } +func TestTestRebuild(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + + // golang.org/issue/23701. + // b_test imports b with augmented method from export_test.go. + // b_test also imports a, which imports b. + // Must not accidentally see un-augmented b propagate through a to b_test. + tg.tempFile("src/a/a.go", `package a + import "b" + type Type struct{} + func (*Type) M() b.T {return 0} + `) + tg.tempFile("src/b/b.go", `package b + type T int + type I interface {M() T} + `) + tg.tempFile("src/b/export_test.go", `package b + func (*T) Method() *T { return nil } + `) + tg.tempFile("src/b/b_test.go", `package b_test + import ( + "testing" + "a" + . "b" + ) + func TestBroken(t *testing.T) { + x := new(T) + x.Method() + _ = new(a.Type) + } + `) + + tg.setenv("GOPATH", tg.path(".")) + tg.run("test", "b") +} + func TestInstallDeps(t *testing.T) { tooSlow(t) tg := testgo(t) @@ -5712,3 +5781,177 @@ func TestCpuprofileTwice(t *testing.T) { tg.run("test", "-o="+bin, "-cpuprofile="+out, "x") tg.mustExist(out) } + +// Issue 23694. +func TestAtomicCoverpkgAll(t *testing.T) { + skipIfGccgo(t, "gccgo has no cover tool") + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + + tg.tempFile("src/x/x.go", `package x; import _ "sync/atomic"; func F() {}`) + tg.tempFile("src/x/x_test.go", `package x; import "testing"; func TestF(t *testing.T) { F() }`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("test", "-coverpkg=all", "-covermode=atomic", "x") + if canRace { + tg.run("test", "-coverpkg=all", "-race", "x") + } +} + +func TestBadCommandLines(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + + tg.tempFile("src/x/x.go", "package x\n") + tg.setenv("GOPATH", tg.path(".")) + + tg.run("build", "x") + + tg.tempFile("src/x/@y.go", "package x\n") + tg.runFail("build", "x") + tg.grepStderr("invalid input file name \"@y.go\"", "did not reject @y.go") + tg.must(os.Remove(tg.path("src/x/@y.go"))) + + tg.tempFile("src/x/-y.go", "package x\n") + tg.runFail("build", "x") + tg.grepStderr("invalid input file name \"-y.go\"", "did not reject -y.go") + tg.must(os.Remove(tg.path("src/x/-y.go"))) + + if runtime.Compiler == "gccgo" { + tg.runFail("build", "-gccgoflags=all=@x", "x") + } else { + tg.runFail("build", "-gcflags=all=@x", "x") + } + tg.grepStderr("invalid command-line argument @x in command", "did not reject @x during exec") + + tg.tempFile("src/@x/x.go", "package x\n") + tg.setenv("GOPATH", tg.path(".")) + tg.runFail("build", "@x") + tg.grepStderr("invalid input directory name \"@x\"", "did not reject @x directory") + + tg.tempFile("src/@x/y/y.go", "package y\n") + tg.setenv("GOPATH", tg.path(".")) + tg.runFail("build", "@x/y") + tg.grepStderr("invalid import path \"@x/y\"", "did not reject @x/y import path") + + tg.tempFile("src/-x/x.go", "package x\n") + tg.setenv("GOPATH", tg.path(".")) + tg.runFail("build", "--", "-x") + tg.grepStderr("invalid input directory name \"-x\"", "did not reject -x directory") + + tg.tempFile("src/-x/y/y.go", "package y\n") + tg.setenv("GOPATH", tg.path(".")) + tg.runFail("build", "--", "-x/y") + tg.grepStderr("invalid import path \"-x/y\"", "did not reject -x/y import path") +} + +func TestBadCgoDirectives(t *testing.T) { + if !canCgo { + t.Skip("no cgo") + } + tg := testgo(t) + defer tg.cleanup() + + tg.tempFile("src/x/x.go", "package x\n") + tg.setenv("GOPATH", tg.path(".")) + + if runtime.Compiler == "gc" { + tg.tempFile("src/x/x.go", `package x + + //go:cgo_ldflag "-fplugin=foo.so" + + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("//go:cgo_ldflag .* only allowed in cgo-generated code", "did not reject //go:cgo_ldflag directive") + } + + tg.must(os.Remove(tg.path("src/x/x.go"))) + tg.runFail("build", "x") + tg.grepStderr("no Go files", "did not report missing source code") + tg.tempFile("src/x/_cgo_yy.go", `package x + + //go:cgo_ldflag "-fplugin=foo.so" + + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("no Go files", "did not report missing source code") // _* files are ignored... + + if runtime.Compiler == "gc" { + tg.runFail("build", tg.path("src/x/_cgo_yy.go")) // ... but if forced, the comment is rejected + // Actually, today there is a separate issue that _ files named + // on the command-line are ignored. Once that is fixed, + // we want to see the cgo_ldflag error. + tg.grepStderr("//go:cgo_ldflag only allowed in cgo-generated code|no Go files", "did not reject //go:cgo_ldflag directive") + } + + tg.must(os.Remove(tg.path("src/x/_cgo_yy.go"))) + + tg.tempFile("src/x/x.go", "package x\n") + tg.tempFile("src/x/y.go", `package x + // #cgo CFLAGS: -fplugin=foo.so + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid flag in #cgo CFLAGS: -fplugin=foo.so", "did not reject -fplugin") + + tg.tempFile("src/x/y.go", `package x + // #cgo CFLAGS: -Ibar -fplugin=foo.so + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid flag in #cgo CFLAGS: -fplugin=foo.so", "did not reject -fplugin") + + tg.tempFile("src/x/y.go", `package x + // #cgo pkg-config: -foo + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid pkg-config package name: -foo", "did not reject pkg-config: -foo") + + tg.tempFile("src/x/y.go", `package x + // #cgo pkg-config: @foo + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid pkg-config package name: @foo", "did not reject pkg-config: -foo") + + tg.tempFile("src/x/y.go", `package x + // #cgo CFLAGS: @foo + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid flag in #cgo CFLAGS: @foo", "did not reject @foo flag") + + tg.tempFile("src/x/y.go", `package x + // #cgo CFLAGS: -D + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid flag in #cgo CFLAGS: -D without argument", "did not reject trailing -I flag") + + // Note that -I @foo is allowed because we rewrite it into -I /path/to/src/@foo + // before the check is applied. There's no such rewrite for -D. + + tg.tempFile("src/x/y.go", `package x + // #cgo CFLAGS: -D @foo + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid flag in #cgo CFLAGS: -D @foo", "did not reject -D @foo flag") + + tg.tempFile("src/x/y.go", `package x + // #cgo CFLAGS: -D@foo + import "C" + `) + tg.runFail("build", "x") + tg.grepStderr("invalid flag in #cgo CFLAGS: -D@foo", "did not reject -D@foo flag") + + tg.setenv("CGO_CFLAGS", "-D@foo") + tg.tempFile("src/x/y.go", `package x + import "C" + `) + tg.run("build", "-n", "x") + tg.grepStderr("-D@foo", "did not find -D@foo in commands") +} diff --git a/libgo/go/cmd/go/internal/cache/default.go b/libgo/go/cmd/go/internal/cache/default.go index 8285f787d4c..97283762258 100644 --- a/libgo/go/cmd/go/internal/cache/default.go +++ b/libgo/go/cmd/go/internal/cache/default.go @@ -5,7 +5,7 @@ package cache import ( - "cmd/go/internal/base" + "fmt" "io/ioutil" "os" "path/filepath" @@ -40,7 +40,8 @@ func initDefaultCache() { return } if err := os.MkdirAll(dir, 0777); err != nil { - base.Fatalf("initializing cache in $GOCACHE: %s", err) + fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) + return } if _, err := os.Stat(filepath.Join(dir, "README")); err != nil { // Best effort. @@ -49,7 +50,8 @@ func initDefaultCache() { c, err := Open(dir) if err != nil { - base.Fatalf("initializing cache in $GOCACHE: %s", err) + fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err) + return } defaultCache = c } diff --git a/libgo/go/cmd/go/internal/envcmd/env.go b/libgo/go/cmd/go/internal/envcmd/env.go index fa19bebe218..603f7b5060c 100644 --- a/libgo/go/cmd/go/internal/envcmd/env.go +++ b/libgo/go/cmd/go/internal/envcmd/env.go @@ -113,7 +113,12 @@ func findEnv(env []cfg.EnvVar, name string) string { func ExtraEnvVars() []cfg.EnvVar { var b work.Builder b.Init() - cppflags, cflags, cxxflags, fflags, ldflags := b.CFlags(&load.Package{}) + cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{}) + if err != nil { + // Should not happen - b.CFlags was given an empty package. + fmt.Fprintf(os.Stderr, "go: invalid cflags: %v\n", err) + return nil + } cmd := b.GccCmd(".", "") return []cfg.EnvVar{ // Note: Update the switch in runEnv below when adding to this list. diff --git a/libgo/go/cmd/go/internal/help/helpdoc.go b/libgo/go/cmd/go/internal/help/helpdoc.go index 126e8663fb4..9a9fc4e9448 100644 --- a/libgo/go/cmd/go/internal/help/helpdoc.go +++ b/libgo/go/cmd/go/internal/help/helpdoc.go @@ -487,17 +487,26 @@ Environment variables for use with cgo: CGO_CFLAGS Flags that cgo will pass to the compiler when compiling C code. - CGO_CPPFLAGS - Flags that cgo will pass to the compiler when compiling - C or C++ code. - CGO_CXXFLAGS - Flags that cgo will pass to the compiler when compiling - C++ code. - CGO_FFLAGS - Flags that cgo will pass to the compiler when compiling - Fortran code. - CGO_LDFLAGS - Flags that cgo will pass to the compiler when linking. + CGO_CFLAGS_ALLOW + A regular expression specifying additional flags to allow + to appear in #cgo CFLAGS source code directives. + Does not apply to the CGO_CFLAGS environment variable. + CGO_CFLAGS_DISALLOW + A regular expression specifying flags that must be disallowed + from appearing in #cgo CFLAGS source code directives. + Does not apply to the CGO_CFLAGS environment variable. + CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW + Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, + but for the C preprocessor. + CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW + Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, + but for the C++ compiler. + CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW + Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, + but for the Fortran compiler. + CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW + Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, + but for the linker. CXX The command to use to compile C++ code. PKG_CONFIG diff --git a/libgo/go/cmd/go/internal/load/pkg.go b/libgo/go/cmd/go/internal/load/pkg.go index 609a47db4bc..ac764b4d028 100644 --- a/libgo/go/cmd/go/internal/load/pkg.go +++ b/libgo/go/cmd/go/internal/load/pkg.go @@ -17,6 +17,7 @@ import ( "sort" "strings" "unicode" + "unicode/utf8" "cmd/go/internal/base" "cmd/go/internal/cfg" @@ -55,6 +56,8 @@ type PackagePublic struct { StaleReason string `json:",omitempty"` // why is Stale true? // Source files + // If you add to this list you MUST add to p.AllFiles (below) too. + // Otherwise file name security lists will not apply to any new additions. GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) CgoFiles []string `json:",omitempty"` // .go sources files that import "C" IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints @@ -86,12 +89,38 @@ type PackagePublic struct { DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies // Test information + // If you add to this list you MUST add to p.AllFiles (below) too. + // Otherwise file name security lists will not apply to any new additions. TestGoFiles []string `json:",omitempty"` // _test.go files in package TestImports []string `json:",omitempty"` // imports from TestGoFiles XTestGoFiles []string `json:",omitempty"` // _test.go files outside package XTestImports []string `json:",omitempty"` // imports from XTestGoFiles } +// AllFiles returns the names of all the files considered for the package. +// This is used for sanity and security checks, so we include all files, +// even IgnoredGoFiles, because some subcommands consider them. +// The go/build package filtered others out (like foo_wrongGOARCH.s) +// and that's OK. +func (p *Package) AllFiles() []string { + return str.StringList( + p.GoFiles, + p.CgoFiles, + p.IgnoredGoFiles, + p.CFiles, + p.CXXFiles, + p.MFiles, + p.HFiles, + p.FFiles, + p.SFiles, + p.SwigFiles, + p.SwigCXXFiles, + p.SysoFiles, + p.TestGoFiles, + p.XTestGoFiles, + ) +} + type PackageInternal struct { // Unexported fields are not part of the public API. Build *build.Package @@ -420,6 +449,9 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo var err error if debugDeprecatedImportcfgDir != "" { bp, err = cfg.BuildContext.ImportDir(debugDeprecatedImportcfgDir, 0) + } else if DebugDeprecatedImportcfg.enabled { + bp = new(build.Package) + err = fmt.Errorf("unknown import path %q: not in import cfg", importPath) } else { buildMode := build.ImportComment if mode&UseVendor == 0 || path != origPath { @@ -518,6 +550,13 @@ func isDir(path string) bool { // x/vendor/path, vendor/path, or else stay path if none of those exist. // VendoredImportPath returns the expanded path or, if no expansion is found, the original. func VendoredImportPath(parent *Package, path string) (found string) { + if DebugDeprecatedImportcfg.enabled { + if d, i := DebugDeprecatedImportcfg.lookup(parent, path); d != "" { + return i + } + return path + } + if parent == nil || parent.Root == "" { return path } @@ -1010,22 +1049,8 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { // To avoid problems on case-insensitive files, we reject any package // where two different input files have equal names under a case-insensitive // comparison. - f1, f2 := str.FoldDup(str.StringList( - p.GoFiles, - p.CgoFiles, - p.IgnoredGoFiles, - p.CFiles, - p.CXXFiles, - p.MFiles, - p.HFiles, - p.FFiles, - p.SFiles, - p.SysoFiles, - p.SwigFiles, - p.SwigCXXFiles, - p.TestGoFiles, - p.XTestGoFiles, - )) + inputs := p.AllFiles() + f1, f2 := str.FoldDup(inputs) if f1 != "" { p.Error = &PackageError{ ImportStack: stk.Copy(), @@ -1034,6 +1059,37 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { return } + // If first letter of input file is ASCII, it must be alphanumeric. + // This avoids files turning into flags when invoking commands, + // and other problems we haven't thought of yet. + // Also, _cgo_ files must be generated by us, not supplied. + // They are allowed to have //go:cgo_ldflag directives. + // The directory scan ignores files beginning with _, + // so we shouldn't see any _cgo_ files anyway, but just be safe. + for _, file := range inputs { + if !SafeArg(file) || strings.HasPrefix(file, "_cgo_") { + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: fmt.Sprintf("invalid input file name %q", file), + } + return + } + } + if name := pathpkg.Base(p.ImportPath); !SafeArg(name) { + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: fmt.Sprintf("invalid input directory name %q", name), + } + return + } + if !SafeArg(p.ImportPath) { + p.Error = &PackageError{ + ImportStack: stk.Copy(), + Err: fmt.Sprintf("invalid import path %q", p.ImportPath), + } + return + } + // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) for i, path := range importPaths { @@ -1160,6 +1216,22 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { } } +// SafeArg reports whether arg is a "safe" command-line argument, +// meaning that when it appears in a command-line, it probably +// doesn't have some special meaning other than its own name. +// Obviously args beginning with - are not safe (they look like flags). +// Less obviously, args beginning with @ are not safe (they look like +// GNU binutils flagfile specifiers, sometimes called "response files"). +// To be conservative, we reject almost any arg beginning with non-alphanumeric ASCII. +// We accept leading . _ and / as likely in file system paths. +func SafeArg(name string) bool { + if name == "" { + return false + } + c := name[0] + return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf +} + // LinkerDeps returns the list of linker-induced dependencies for main package p. func LinkerDeps(p *Package) []string { // Everything links runtime. diff --git a/libgo/go/cmd/go/internal/test/test.go b/libgo/go/cmd/go/internal/test/test.go index e77e834cb23..97854707964 100644 --- a/libgo/go/cmd/go/internal/test/test.go +++ b/libgo/go/cmd/go/internal/test/test.go @@ -662,6 +662,15 @@ func runTest(cmd *base.Command, args []string) { haveMatch = true } } + + // Silently ignore attempts to run coverage on + // sync/atomic when using atomic coverage mode. + // Atomic coverage mode uses sync/atomic, so + // we can't also do coverage on it. + if testCoverMode == "atomic" && p.Standard && p.ImportPath == "sync/atomic" { + continue + } + if haveMatch { testCoverPkgs = append(testCoverPkgs, p) } @@ -894,7 +903,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin t.ImportXtest = true } - if ptest != p && localCover { + if ptest != p { // We have made modifications to the package p being tested // and are rebuilding p (as ptest). // Arrange to rebuild all packages q such that @@ -903,13 +912,6 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin // Strictly speaking, the rebuild is only necessary if the // modifications to p change its export metadata, but // determining that is a bit tricky, so we rebuild always. - // TODO(rsc): Once we get export metadata changes - // handled properly, look into the expense of dropping - // "&& localCover" above. - // - // This will cause extra compilation, so for now we only do it - // when testCover is set. The conditions are more general, though, - // and we may find that we need to do it always in the future. recompileForTest(pmain, p, ptest, pxtest) } diff --git a/libgo/go/cmd/go/internal/work/exec.go b/libgo/go/cmd/go/internal/work/exec.go index b5874982708..5527e90756d 100644 --- a/libgo/go/cmd/go/internal/work/exec.go +++ b/libgo/go/cmd/go/internal/work/exec.go @@ -197,7 +197,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix) if len(p.CgoFiles)+len(p.SwigFiles) > 0 { fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo")) - cppflags, cflags, cxxflags, fflags, _ := b.CFlags(p) + cppflags, cflags, cxxflags, fflags, _, _ := b.CFlags(p) fmt.Fprintf(h, "CC=%q %q %q\n", b.ccExe(), cppflags, cflags) if len(p.CXXFiles)+len(p.SwigFiles) > 0 { fmt.Fprintf(h, "CXX=%q %q\n", b.cxxExe(), cxxflags) @@ -521,7 +521,14 @@ func (b *Builder) build(a *Action) (err error) { } // Prepare Go import config. + // We start it off with a comment so it can't be empty, so icfg.Bytes() below is never nil. + // It should never be empty anyway, but there have been bugs in the past that resulted + // in empty configs, which then unfortunately turn into "no config passed to compiler", + // and the compiler falls back to looking in pkg itself, which mostly works, + // except when it doesn't. var icfg bytes.Buffer + fmt.Fprintf(&icfg, "# import config\n") + for i, raw := range a.Package.Internal.RawImports { final := a.Package.Imports[i] if final != raw { @@ -938,28 +945,38 @@ func splitPkgConfigOutput(out []byte) []string { // Calls pkg-config if needed and returns the cflags/ldflags needed to build the package. func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, err error) { if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { + for _, pkg := range pkgs { + if !load.SafeArg(pkg) { + return nil, nil, fmt.Errorf("invalid pkg-config package name: %s", pkg) + } + } var out []byte - out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", pkgs) + out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", "--", pkgs) if err != nil { b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pkgs, " "), string(out)) b.Print(err.Error() + "\n") - err = errPrintedOutput - return + return nil, nil, errPrintedOutput } if len(out) > 0 { cflags = splitPkgConfigOutput(out) + if err := checkCompilerFlags("CFLAGS", "pkg-config --cflags", cflags); err != nil { + return nil, nil, err + } } - out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", pkgs) + out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", "--", pkgs) if err != nil { b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pkgs, " "), string(out)) b.Print(err.Error() + "\n") - err = errPrintedOutput - return + return nil, nil, errPrintedOutput } if len(out) > 0 { ldflags = strings.Fields(string(out)) + if err := checkLinkerFlags("CFLAGS", "pkg-config --cflags", ldflags); err != nil { + return nil, nil, err + } } } + return } @@ -1464,6 +1481,17 @@ func (b *Builder) processOutput(out []byte) string { // It returns the command output and any errors that occurred. func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) { cmdline := str.StringList(cmdargs...) + + for _, arg := range cmdline { + // GNU binutils commands, including gcc and gccgo, interpret an argument + // @foo anywhere in the command line (even following --) as meaning + // "read and insert arguments from the file named foo." + // Don't say anything that might be misinterpreted that way. + if strings.HasPrefix(arg, "@") { + return nil, fmt.Errorf("invalid command-line argument %s in command: %s", arg, joinUnambiguously(cmdline)) + } + } + if cfg.BuildN || cfg.BuildX { var envcmdline string for _, e := range env { @@ -1916,22 +1944,44 @@ func envList(key, def string) []string { } // CFlags returns the flags to use when invoking the C, C++ or Fortran compilers, or cgo. -func (b *Builder) CFlags(p *load.Package) (cppflags, cflags, cxxflags, fflags, ldflags []string) { +func (b *Builder) CFlags(p *load.Package) (cppflags, cflags, cxxflags, fflags, ldflags []string, err error) { defaults := "-g -O2" - cppflags = str.StringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) - cflags = str.StringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) - cxxflags = str.StringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) - fflags = str.StringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS) - ldflags = str.StringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) + if cppflags, err = buildFlags("CPPFLAGS", "", p.CgoCPPFLAGS, checkCompilerFlags); err != nil { + return + } + if cflags, err = buildFlags("CFLAGS", defaults, p.CgoCFLAGS, checkCompilerFlags); err != nil { + return + } + if cxxflags, err = buildFlags("CXXFLAGS", defaults, p.CgoCXXFLAGS, checkCompilerFlags); err != nil { + return + } + if fflags, err = buildFlags("FFLAGS", defaults, p.CgoFFLAGS, checkCompilerFlags); err != nil { + return + } + if ldflags, err = buildFlags("LDFLAGS", defaults, p.CgoLDFLAGS, checkLinkerFlags); err != nil { + return + } + return } +func buildFlags(name, defaults string, fromPackage []string, check func(string, string, []string) error) ([]string, error) { + if err := check(name, "#cgo "+name, fromPackage); err != nil { + return nil, err + } + return str.StringList(envList("CGO_"+name, defaults), fromPackage), nil +} + var cgoRe = regexp.MustCompile(`[/\\:]`) func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) { p := a.Package - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.CFlags(p) + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS, err := b.CFlags(p) + if err != nil { + return nil, nil, err + } + cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) // If we are compiling Objective-C code, then we need to link against libobjc @@ -1981,6 +2031,12 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo } // Update $CGO_LDFLAGS with p.CgoLDFLAGS. + // These flags are recorded in the generated _cgo_gotypes.go file + // using //go:cgo_ldflag directives, the compiler records them in the + // object file for the package, and then the Go linker passes them + // along to the host linker. At this point in the code, cgoLDFLAGS + // consists of the original $CGO_LDFLAGS (unchecked) and all the + // flags put together from source code (checked). var cgoenv []string if len(cgoLDFLAGS) > 0 { flags := make([]string, len(cgoLDFLAGS)) @@ -2272,7 +2328,11 @@ func (b *Builder) swigIntSize(objdir string) (intsize string, err error) { // Run SWIG on one SWIG input file. func (b *Builder) swigOne(a *Action, p *load.Package, file, objdir string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) { - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.CFlags(p) + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _, err := b.CFlags(p) + if err != nil { + return "", "", err + } + var cflags []string if cxx { cflags = str.StringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS) diff --git a/libgo/go/cmd/go/internal/work/security.go b/libgo/go/cmd/go/internal/work/security.go new file mode 100644 index 00000000000..fee5beeb15f --- /dev/null +++ b/libgo/go/cmd/go/internal/work/security.go @@ -0,0 +1,160 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Checking of compiler and linker flags. +// We must avoid flags like -fplugin=, which can allow +// arbitrary code execution during the build. +// Do not make changes here without carefully +// considering the implications. +// (That's why the code is isolated in a file named security.go.) +// +// Note that -Wl,foo means split foo on commas and pass to +// the linker, so that -Wl,-foo,bar means pass -foo bar to +// the linker. Similarly -Wa,foo for the assembler and so on. +// If any of these are permitted, the wildcard portion must +// disallow commas. +// +// Note also that GNU binutils accept any argument @foo +// as meaning "read more flags from the file foo", so we must +// guard against any command-line argument beginning with @, +// even things like "-I @foo". +// We use load.SafeArg (which is even more conservative) +// to reject these. +// +// Even worse, gcc -I@foo (one arg) turns into cc1 -I @foo (two args), +// so although gcc doesn't expand the @foo, cc1 will. +// So out of paranoia, we reject @ at the beginning of every +// flag argument that might be split into its own argument. + +package work + +import ( + "cmd/go/internal/load" + "fmt" + "os" + "regexp" +) + +var re = regexp.MustCompile + +var validCompilerFlags = []*regexp.Regexp{ + re(`-D([A-Za-z_].*)`), + re(`-I([^@\-].*)`), + re(`-O`), + re(`-O([^@\-].*)`), + re(`-W`), + re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. + re(`-f(no-)?objc-arc`), + re(`-f(no-)?omit-frame-pointer`), + re(`-f(no-)?(pic|PIC|pie|PIE)`), + re(`-f(no-)?split-stack`), + re(`-f(no-)?stack-(.+)`), + re(`-f(no-)?strict-aliasing`), + re(`-fsanitize=(.+)`), + re(`-g([^@\-].*)?`), + re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), + re(`-m(no-)?stack-(.+)`), + re(`-mmacosx-(.+)`), + re(`-mnop-fun-dllimport`), + re(`-pthread`), + re(`-std=([^@\-].*)`), + re(`-x([^@\-].*)`), +} + +var validCompilerFlagsWithNextArg = []string{ + "-D", + "-I", + "-framework", + "-x", +} + +var validLinkerFlags = []*regexp.Regexp{ + re(`-F([^@\-].*)`), + re(`-l([^@\-].*)`), + re(`-L([^@\-].*)`), + re(`-f(no-)?(pic|PIC|pie|PIE)`), + re(`-fsanitize=([^@\-].*)`), + re(`-g([^@\-].*)?`), + re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), + re(`-(pic|PIC|pie|PIE)`), + re(`-pthread`), + + // Note that any wildcards in -Wl need to exclude comma, + // since -Wl splits its argument at commas and passes + // them all to the linker uninterpreted. Allowing comma + // in a wildcard would allow tunnelling arbitrary additional + // linker arguments through one of these. + re(`-Wl,-rpath,([^,@\-][^,]+)`), + re(`-Wl,--(no-)?warn-([^,]+)`), + + re(`[a-zA-Z0-9_].*\.(o|obj|dll|dylib|so)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o) +} + +var validLinkerFlagsWithNextArg = []string{ + "-F", + "-l", + "-L", + "-framework", +} + +func checkCompilerFlags(name, source string, list []string) error { + return checkFlags(name, source, list, validCompilerFlags, validCompilerFlagsWithNextArg) +} + +func checkLinkerFlags(name, source string, list []string) error { + return checkFlags(name, source, list, validLinkerFlags, validLinkerFlagsWithNextArg) +} + +func checkFlags(name, source string, list []string, valid []*regexp.Regexp, validNext []string) error { + // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc. + var ( + allow *regexp.Regexp + disallow *regexp.Regexp + ) + if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" { + r, err := regexp.Compile(env) + if err != nil { + return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) + } + allow = r + } + if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" { + r, err := regexp.Compile(env) + if err != nil { + return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) + } + disallow = r + } + +Args: + for i := 0; i < len(list); i++ { + arg := list[i] + if disallow != nil && disallow.FindString(arg) == arg { + goto Bad + } + if allow != nil && allow.FindString(arg) == arg { + continue Args + } + for _, re := range valid { + if re.FindString(arg) == arg { // must be complete match + continue Args + } + } + for _, x := range validNext { + if arg == x { + if i+1 < len(list) && load.SafeArg(list[i+1]) { + i++ + continue Args + } + if i+1 < len(list) { + return fmt.Errorf("invalid flag in %s: %s %s", source, arg, list[i+1]) + } + return fmt.Errorf("invalid flag in %s: %s without argument", source, arg) + } + } + Bad: + return fmt.Errorf("invalid flag in %s: %s", source, arg) + } + return nil +} diff --git a/libgo/go/cmd/go/internal/work/security_test.go b/libgo/go/cmd/go/internal/work/security_test.go new file mode 100644 index 00000000000..739ab5a6ee6 --- /dev/null +++ b/libgo/go/cmd/go/internal/work/security_test.go @@ -0,0 +1,240 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package work + +import ( + "os" + "testing" +) + +var goodCompilerFlags = [][]string{ + {"-DFOO"}, + {"-Dfoo=bar"}, + {"-I/"}, + {"-I/etc/passwd"}, + {"-I."}, + {"-O"}, + {"-O2"}, + {"-Osmall"}, + {"-W"}, + {"-Wall"}, + {"-fobjc-arc"}, + {"-fno-objc-arc"}, + {"-fomit-frame-pointer"}, + {"-fno-omit-frame-pointer"}, + {"-fpic"}, + {"-fno-pic"}, + {"-fPIC"}, + {"-fno-PIC"}, + {"-fpie"}, + {"-fno-pie"}, + {"-fPIE"}, + {"-fno-PIE"}, + {"-fsplit-stack"}, + {"-fno-split-stack"}, + {"-fstack-xxx"}, + {"-fno-stack-xxx"}, + {"-fsanitize=hands"}, + {"-g"}, + {"-ggdb"}, + {"-march=souza"}, + {"-mcpu=123"}, + {"-mfpu=123"}, + {"-mtune=happybirthday"}, + {"-mstack-overflow"}, + {"-mno-stack-overflow"}, + {"-mmacosx-version"}, + {"-mnop-fun-dllimport"}, + {"-pthread"}, + {"-std=c99"}, + {"-xc"}, + {"-D", "FOO"}, + {"-D", "foo=bar"}, + {"-I", "."}, + {"-I", "/etc/passwd"}, + {"-I", "世界"}, + {"-framework", "Chocolate"}, + {"-x", "c"}, +} + +var badCompilerFlags = [][]string{ + {"-D@X"}, + {"-D-X"}, + {"-I@dir"}, + {"-I-dir"}, + {"-O@1"}, + {"-Wa,-foo"}, + {"-W@foo"}, + {"-g@gdb"}, + {"-g-gdb"}, + {"-march=@dawn"}, + {"-march=-dawn"}, + {"-std=@c99"}, + {"-std=-c99"}, + {"-x@c"}, + {"-x-c"}, + {"-D", "@foo"}, + {"-D", "-foo"}, + {"-I", "@foo"}, + {"-I", "-foo"}, + {"-framework", "-Caffeine"}, + {"-framework", "@Home"}, + {"-x", "--c"}, + {"-x", "@obj"}, +} + +func TestCheckCompilerFlags(t *testing.T) { + for _, f := range goodCompilerFlags { + if err := checkCompilerFlags("test", "test", f); err != nil { + t.Errorf("unexpected error for %q: %v", f, err) + } + } + for _, f := range badCompilerFlags { + if err := checkCompilerFlags("test", "test", f); err == nil { + t.Errorf("missing error for %q", f) + } + } +} + +var goodLinkerFlags = [][]string{ + {"-Fbar"}, + {"-lbar"}, + {"-Lbar"}, + {"-fpic"}, + {"-fno-pic"}, + {"-fPIC"}, + {"-fno-PIC"}, + {"-fpie"}, + {"-fno-pie"}, + {"-fPIE"}, + {"-fno-PIE"}, + {"-fsanitize=hands"}, + {"-g"}, + {"-ggdb"}, + {"-march=souza"}, + {"-mcpu=123"}, + {"-mfpu=123"}, + {"-mtune=happybirthday"}, + {"-pic"}, + {"-pthread"}, + {"-Wl,-rpath,foo"}, + {"-Wl,-rpath,$ORIGIN/foo"}, + {"-Wl,--warn-error"}, + {"-Wl,--no-warn-error"}, + {"foo.so"}, + {"_世界.dll"}, + {"libcgosotest.dylib"}, + {"-F", "framework"}, + {"-l", "."}, + {"-l", "/etc/passwd"}, + {"-l", "世界"}, + {"-L", "framework"}, + {"-framework", "Chocolate"}, +} + +var badLinkerFlags = [][]string{ + {"-DFOO"}, + {"-Dfoo=bar"}, + {"-O"}, + {"-O2"}, + {"-Osmall"}, + {"-W"}, + {"-Wall"}, + {"-fobjc-arc"}, + {"-fno-objc-arc"}, + {"-fomit-frame-pointer"}, + {"-fno-omit-frame-pointer"}, + {"-fsplit-stack"}, + {"-fno-split-stack"}, + {"-fstack-xxx"}, + {"-fno-stack-xxx"}, + {"-mstack-overflow"}, + {"-mno-stack-overflow"}, + {"-mmacosx-version"}, + {"-mnop-fun-dllimport"}, + {"-std=c99"}, + {"-xc"}, + {"-D", "FOO"}, + {"-D", "foo=bar"}, + {"-I", "FOO"}, + {"-L", "@foo"}, + {"-L", "-foo"}, + {"-x", "c"}, + {"-D@X"}, + {"-D-X"}, + {"-I@dir"}, + {"-I-dir"}, + {"-O@1"}, + {"-Wa,-foo"}, + {"-W@foo"}, + {"-g@gdb"}, + {"-g-gdb"}, + {"-march=@dawn"}, + {"-march=-dawn"}, + {"-std=@c99"}, + {"-std=-c99"}, + {"-x@c"}, + {"-x-c"}, + {"-D", "@foo"}, + {"-D", "-foo"}, + {"-I", "@foo"}, + {"-I", "-foo"}, + {"-l", "@foo"}, + {"-l", "-foo"}, + {"-framework", "-Caffeine"}, + {"-framework", "@Home"}, + {"-x", "--c"}, + {"-x", "@obj"}, + {"-Wl,-rpath,@foo"}, +} + +func TestCheckLinkerFlags(t *testing.T) { + for _, f := range goodLinkerFlags { + if err := checkLinkerFlags("test", "test", f); err != nil { + t.Errorf("unexpected error for %q: %v", f, err) + } + } + for _, f := range badLinkerFlags { + if err := checkLinkerFlags("test", "test", f); err == nil { + t.Errorf("missing error for %q", f) + } + } +} + +func TestCheckFlagAllowDisallow(t *testing.T) { + if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err == nil { + t.Fatalf("missing error for -disallow") + } + os.Setenv("CGO_TEST_ALLOW", "-disallo") + if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err == nil { + t.Fatalf("missing error for -disallow with CGO_TEST_ALLOW=-disallo") + } + os.Setenv("CGO_TEST_ALLOW", "-disallow") + if err := checkCompilerFlags("TEST", "test", []string{"-disallow"}); err != nil { + t.Fatalf("unexpected error for -disallow with CGO_TEST_ALLOW=-disallow: %v", err) + } + os.Unsetenv("CGO_TEST_ALLOW") + + if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err != nil { + t.Fatalf("unexpected error for -Wall: %v", err) + } + os.Setenv("CGO_TEST_DISALLOW", "-Wall") + if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err == nil { + t.Fatalf("missing error for -Wall with CGO_TEST_DISALLOW=-Wall") + } + os.Setenv("CGO_TEST_ALLOW", "-Wall") // disallow wins + if err := checkCompilerFlags("TEST", "test", []string{"-Wall"}); err == nil { + t.Fatalf("missing error for -Wall with CGO_TEST_DISALLOW=-Wall and CGO_TEST_ALLOW=-Wall") + } + + os.Setenv("CGO_TEST_ALLOW", "-fplugin.*") + os.Setenv("CGO_TEST_DISALLOW", "-fplugin=lint.so") + if err := checkCompilerFlags("TEST", "test", []string{"-fplugin=faster.so"}); err != nil { + t.Fatalf("unexpected error for -fplugin=faster.so: %v", err) + } + if err := checkCompilerFlags("TEST", "test", []string{"-fplugin=lint.so"}); err == nil { + t.Fatalf("missing error for -fplugin=lint.so: %v", err) + } +} |