diff --git a/eng/_util/cmd/build/build.go b/eng/_util/cmd/build/build.go index 923d89e04ce..83afa58f741 100644 --- a/eng/_util/cmd/build/build.go +++ b/eng/_util/cmd/build/build.go @@ -222,6 +222,13 @@ func build(o *options) (err error) { } if o.Test { + if err := runCommandLine( + filepath.Join(goRootDir, "bin", "go"+executableExtension), + "-C", filepath.Join(rootDir, "toolchaintest"), "test", "-count=1", + ); err != nil { + return err + } + // Normally, use the dev script to build. testCommandLine := append( shellPrefix, diff --git a/eng/_util/cmd/run-builder/run-builder.go b/eng/_util/cmd/run-builder/run-builder.go index 70f08ef6de4..e8fbb201238 100644 --- a/eng/_util/cmd/run-builder/run-builder.go +++ b/eng/_util/cmd/run-builder/run-builder.go @@ -179,6 +179,8 @@ func main() { buildutil.SetEnv("GO_BUILDER_NAME", goos+"-"+goarch) } + runOrPanic("go/bin/go", "-C", "toolchaintest", "test", "-count=1") + cmdline := []string{ // Use the dist test command directly, because 'src/run.bash' isn't compatible with // longtest. 'src/run.bash' sets 'GOPATH=/nonexist-gopath', which breaks modconv tests diff --git a/patches/0002-Add-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch index 1207c3561c7..866f56d5c35 100644 --- a/patches/0002-Add-crypto-backends.patch +++ b/patches/0002-Add-crypto-backends.patch @@ -7,21 +7,20 @@ Subject: [PATCH] Add crypto backends .gitignore | 2 + src/cmd/api/boring_test.go | 2 +- .../compile/internal/logopt/logopt_test.go | 5 + - src/cmd/compile/script_test.go | 8 + + src/cmd/compile/script_test.go | 8 ++ src/cmd/compile/testdata/script/README | 2 + - src/cmd/dist/build.go | 65 ++++ + src/cmd/dist/build.go | 65 ++++++++++ src/cmd/dist/buildtool.go | 1 + - src/cmd/dist/test.go | 46 ++- + src/cmd/dist/test.go | 46 +++++-- src/cmd/go/alldocs.go | 3 + src/cmd/go/go_boring_test.go | 2 +- - src/cmd/go/go_test.go | 11 + - src/cmd/go/internal/cfg/cfg.go | 32 +- + src/cmd/go/go_test.go | 11 ++ + src/cmd/go/internal/cfg/cfg.go | 32 ++++- src/cmd/go/internal/envcmd/env.go | 3 +- src/cmd/go/internal/help/helpdoc.go | 3 + - src/cmd/go/internal/load/pkg.go | 50 ++- + src/cmd/go/internal/load/pkg.go | 50 +++++++- src/cmd/go/internal/tool/tool.go | 9 +- src/cmd/go/script_test.go | 3 + - src/cmd/go/systemcrypto_test.go | 298 ++++++++++++++++++ src/cmd/go/testdata/script/README | 2 + src/cmd/go/testdata/script/darwin_no_cgo.txt | 1 + src/cmd/go/testdata/script/env_changed.txt | 3 + @@ -34,30 +33,30 @@ Subject: [PATCH] Add crypto backends src/cmd/internal/moddeps/moddeps_test.go | 3 + .../internal/script/scripttest/conditions.go | 10 +- src/cmd/internal/testdir/testdir_test.go | 6 + - src/cmd/link/internal/ld/config.go | 7 + + src/cmd/link/internal/ld/config.go | 7 ++ src/cmd/link/internal/ld/lib.go | 6 + - src/cmd/link/link_test.go | 8 + + src/cmd/link/link_test.go | 8 ++ src/cmd/link/testdata/script/README | 2 + src/cmd/nm/testdata/script/README | 2 + src/crypto/aes/aes.go | 3 +- src/crypto/aes/aes_test.go | 2 +- src/crypto/boring/boring.go | 4 +- - src/crypto/cipher/cbc.go | 16 + - src/crypto/cipher/ctr.go | 7 + + src/crypto/cipher/cbc.go | 16 +++ + src/crypto/cipher/ctr.go | 7 ++ src/crypto/cipher/ctr_aes_test.go | 2 +- - src/crypto/cipher/gcm.go | 59 +++- + src/crypto/cipher/gcm.go | 59 ++++++++- src/crypto/cipher/gcm_test.go | 9 +- - src/crypto/des/cipher.go | 9 + - src/crypto/dsa/boring.go | 114 +++++++ - src/crypto/dsa/dsa.go | 47 +++ - src/crypto/dsa/dsa_test.go | 8 + - src/crypto/dsa/notboring.go | 16 + + src/crypto/des/cipher.go | 9 ++ + src/crypto/dsa/boring.go | 114 ++++++++++++++++++ + src/crypto/dsa/dsa.go | 47 ++++++++ + src/crypto/dsa/dsa_test.go | 8 ++ + src/crypto/dsa/notboring.go | 16 +++ src/crypto/ecdh/ecdh.go | 3 +- src/crypto/ecdh/nist.go | 3 +- - src/crypto/ecdh/x25519.go | 43 +-- - src/crypto/ecdsa/ecdsa.go | 19 +- + src/crypto/ecdh/x25519.go | 43 ++++--- + src/crypto/ecdsa/ecdsa.go | 19 ++- src/crypto/ed25519/ed25519.go | 12 +- - src/crypto/ed25519/ed25519_test.go | 18 +- + src/crypto/ed25519/ed25519_test.go | 18 ++- src/crypto/fips140/enforcement_test.go | 4 + src/crypto/fips140/fips140.go | 3 +- src/crypto/hkdf/hkdf.go | 9 +- @@ -66,80 +65,73 @@ Subject: [PATCH] Add crypto backends src/crypto/hmac/hmac_test.go | 2 +- src/crypto/hpke/aead.go | 2 +- src/crypto/internal/cryptotest/allocations.go | 6 - - src/crypto/internal/cryptotest/fips140.go | 8 + + src/crypto/internal/cryptotest/fips140.go | 8 ++ src/crypto/internal/cryptotest/hash.go | 3 +- .../internal/cryptotest/implementations.go | 2 +- - src/crypto/internal/fips140hash/hash.go | 16 +- + src/crypto/internal/fips140hash/hash.go | 16 ++- .../internal/fips140only/fips140only.go | 11 +- - .../internal/fips140only/fips140only_test.go | 27 +- + .../internal/fips140only/fips140only_test.go | 27 ++++- src/crypto/internal/fips140test/acvp_test.go | 6 + src/crypto/internal/fips140test/cast_test.go | 2 + src/crypto/internal/fips140test/fips_test.go | 3 +- src/crypto/internal/rand/rand.go | 3 +- - src/crypto/md5/md5.go | 12 + - src/crypto/md5/md5_test.go | 18 +- - src/crypto/mldsa/mldsa_fips140v1.26.go | 15 +- - src/crypto/mldsa/mldsa_test.go | 56 +++- + src/crypto/md5/md5.go | 12 ++ + src/crypto/md5/md5_test.go | 18 ++- + src/crypto/mldsa/mldsa_fips140v1.26.go | 15 ++- + src/crypto/mldsa/mldsa_test.go | 56 ++++++++- src/crypto/mlkem/mlkem.go | 3 +- src/crypto/pbkdf2/pbkdf2.go | 3 +- src/crypto/pbkdf2/pbkdf2_test.go | 6 +- src/crypto/purego_test.go | 2 +- src/crypto/rand/rand.go | 3 +- src/crypto/rand/rand_test.go | 9 +- - src/crypto/rc4/rc4.go | 20 ++ + src/crypto/rc4/rc4.go | 20 +++ src/crypto/rsa/fips.go | 3 +- src/crypto/rsa/pkcs1v15.go | 3 +- - src/crypto/rsa/rsa.go | 36 +-- + src/crypto/rsa/rsa.go | 36 +++--- src/crypto/rsa/rsa_test.go | 13 +- src/crypto/sha1/sha1.go | 13 +- - src/crypto/sha1/sha1_test.go | 14 +- - src/crypto/sha256/sha256.go | 15 +- - src/crypto/sha256/sha256_test.go | 42 ++- - src/crypto/sha3/sha3.go | 36 +-- - src/crypto/sha3/sha3_test.go | 32 +- - src/crypto/sha512/sha512.go | 27 +- - src/crypto/sha512/sha512_test.go | 33 +- - src/crypto/systemcrypto_nocgo_freebsd.go | 17 + - src/crypto/systemcrypto_nocgo_linux.go | 18 ++ + src/crypto/sha1/sha1_test.go | 14 ++- + src/crypto/sha256/sha256.go | 15 +-- + src/crypto/sha256/sha256_test.go | 42 ++++++- + src/crypto/sha3/sha3.go | 36 ++---- + src/crypto/sha3/sha3_test.go | 32 ++++- + src/crypto/sha512/sha512.go | 27 +---- + src/crypto/sha512/sha512_test.go | 33 ++++- + src/crypto/systemcrypto_nocgo_freebsd.go | 17 +++ + src/crypto/systemcrypto_nocgo_linux.go | 18 +++ src/crypto/tls/bogo_shim_test.go | 12 +- src/crypto/tls/certificates_generator_test.go | 4 + src/crypto/tls/cipher_suites.go | 9 +- src/crypto/tls/fipsonly/fipsonly.go | 2 +- src/crypto/tls/fipsonly/fipsonly_test.go | 2 +- - src/crypto/tls/handshake_client.go | 18 +- + src/crypto/tls/handshake_client.go | 18 ++- src/crypto/tls/handshake_client_tls13.go | 3 +- - src/crypto/tls/handshake_server.go | 15 +- + src/crypto/tls/handshake_server.go | 15 ++- src/crypto/tls/handshake_server_tls13.go | 6 +- src/crypto/tls/handshake_test.go | 3 +- src/crypto/tls/key_schedule.go | 3 +- - src/crypto/tls/prf.go | 19 +- + src/crypto/tls/prf.go | 19 ++- src/crypto/x509/verify_test.go | 2 +- - src/go/build/buildbackend_test.go | 50 +++ - src/go/build/deps_test.go | 98 +++++- - .../build/testdata/backendtags_system/main.go | 3 + - .../backendtags_system/systemcrypto.go | 3 + + src/go/build/deps_test.go | 98 ++++++++++++--- src/hash/example_test.go | 2 + src/hash/marshal_test.go | 4 + src/internal/buildcfg/cfg.go | 2 + - src/internal/buildcfg/cfg_test.go | 49 +++ - src/internal/buildcfg/exp.go | 22 ++ + src/internal/buildcfg/cfg_test.go | 49 ++++++++ + src/internal/buildcfg/exp.go | 22 ++++ src/internal/cfg/cfg.go | 1 + - src/internal/platform/supported.go | 10 + - src/internal/systemcrypto/systemcrypto.go | 20 ++ - .../systemcrypto/systemcrypto_test.go | 60 ++++ + src/internal/platform/supported.go | 10 ++ + src/internal/systemcrypto/systemcrypto.go | 20 +++ + .../systemcrypto/systemcrypto_test.go | 60 +++++++++ src/net/lookup_test.go | 3 + - src/os/exec/exec_test.go | 9 + + src/os/exec/exec_test.go | 9 ++ src/runtime/runtime_boring.go | 5 + src/syscall/syscall_windows.go | 3 + - 127 files changed, 1750 insertions(+), 254 deletions(-) - create mode 100644 src/cmd/go/systemcrypto_test.go + 123 files changed, 1396 insertions(+), 254 deletions(-) create mode 100644 src/crypto/dsa/boring.go create mode 100644 src/crypto/dsa/notboring.go create mode 100644 src/crypto/systemcrypto_nocgo_freebsd.go create mode 100644 src/crypto/systemcrypto_nocgo_linux.go - create mode 100644 src/go/build/buildbackend_test.go - create mode 100644 src/go/build/testdata/backendtags_system/main.go - create mode 100644 src/go/build/testdata/backendtags_system/systemcrypto.go create mode 100644 src/internal/systemcrypto/systemcrypto.go create mode 100644 src/internal/systemcrypto/systemcrypto_test.go @@ -738,310 +730,6 @@ index ca1492f78a4b4c..0f1087de98bc3b 100644 } // updateSum runs 'go mod tidy', 'go list -mod=mod -m all', or -diff --git a/src/cmd/go/systemcrypto_test.go b/src/cmd/go/systemcrypto_test.go -new file mode 100644 -index 00000000000000..448a3c853277a5 ---- /dev/null -+++ b/src/cmd/go/systemcrypto_test.go -@@ -0,0 +1,298 @@ -+// Copyright 2025 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 main_test -+ -+import ( -+ "fmt" -+ "internal/testenv" -+ "os" -+ "path/filepath" -+ "strings" -+ "testing" -+) -+ -+const fileWithCrypto = ` -+package main -+ -+import ( -+ "crypto/sha256" -+ "fmt" -+) -+ -+func main() { -+ fmt.Println(sha256.Sum256([]byte("Hello, World!"))) -+} -+` -+ -+const fileWithoutCrypto = ` -+package main -+ -+import ( -+ "fmt" -+) -+ -+func main() { -+ fmt.Println("Hello, World!") -+} -+` -+ -+func execGoTool(t *testing.T, allowFail bool, env []string, args ...string) (string, bool) { -+ return execGoToolInDir(t, "", allowFail, env, args...) -+} -+ -+func execGoToolInDir(t *testing.T, dir string, allowFail bool, env []string, args ...string) (string, bool) { -+ t.Helper() -+ cmd := testenv.Command(t, testenv.GoToolPath(t), args...) -+ cmd = testenv.CleanCmdEnv(cmd) -+ cmd.Dir = dir -+ cmd.Env = append(cmd.Env, env...) -+ out, err := cmd.CombinedOutput() -+ sout := strings.TrimSpace(string(out)) -+ if err != nil { -+ if allowFail { -+ return sout, false -+ } -+ t.Fatalf("go build failed: %v\n%s", err, out) -+ } -+ return sout, true -+} -+ -+// writeFile creates a temporary file with the given content and returns its path. -+func writeFile(t *testing.T, content string) string { -+ t.Helper() -+ name := "main.go" -+ if strings.Contains(content, "testing") { -+ name = "main_test.go" -+ } -+ name = filepath.Join(t.TempDir(), name) -+ if err := os.WriteFile(name, []byte(content), 0o644); err != nil { -+ t.Fatal(err) -+ } -+ return name -+} -+ -+func outPath(t *testing.T) string { -+ t.Helper() -+ return filepath.Join(t.TempDir(), "out") -+} -+ -+func TestSystemCryptoNoCgoChecks(t *testing.T) { -+ t.Parallel() -+ -+ cryptoFile := writeFile(t, fileWithCrypto) -+ -+ tt := []struct { -+ goos string -+ goarch string -+ }{ -+ {"linux", "386"}, -+ {"linux", "amd64"}, -+ {"linux", "arm64"}, -+ {"linux", "loong64"}, -+ {"linux", "ppc64le"}, -+ {"linux", "riscv64"}, -+ {"linux", "s390x"}, -+ {"linux", "arm"}, -+ {"freebsd", "amd64"}, -+ {"freebsd", "arm64"}, -+ {"darwin", "amd64"}, -+ {"darwin", "arm64"}, -+ {"windows", "386"}, -+ {"windows", "amd64"}, -+ {"windows", "arm64"}, -+ } -+ for _, tc := range tt { -+ t.Run(tc.goos+"/"+tc.goarch, func(t *testing.T) { -+ t.Parallel() -+ env := []string{"CGO_ENABLED=0", "GOOS=" + tc.goos, "GOARCH=" + tc.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} -+ if out, ok := execGoTool(t, true, env, "build", "-o", outPath(t), cryptoFile); !ok { -+ t.Fatalf("expected success, got failure: %s", out) -+ } -+ }) -+ } -+} -+ -+func TestSystemCryptoFreeBSDNoCgo(t *testing.T) { -+ t.Parallel() -+ -+ cryptoFile := writeFile(t, fileWithCrypto) -+ env := []string{"CGO_ENABLED=0", "GOOS=freebsd", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"} -+ out, ok := execGoTool(t, true, env, "build", "-o", outPath(t), cryptoFile) -+ if ok { -+ t.Fatal("expected failure, got success") -+ } -+ if !strings.Contains(out, "Using system crypto on FreeBSD requires CGO_ENABLED=1 on architectures other than amd64 and arm64") { -+ t.Fatalf("expected cgo requirement error, got: %s", out) -+ } -+} -+ -+func TestSystemCryptoFIPS(t *testing.T) { -+ // Test different go commands with GODEBUG=fips140=on -+ // to exercise the ms_skipfipscheck build tag. -+ t.Parallel() -+ env := []string{"GODEBUG=fips140=on"} -+ -+ cryptoFile := writeFile(t, fileWithCrypto) -+ nonCryptoFile := writeFile(t, fileWithoutCrypto) -+ -+ // Build should always succeed given that the go toolchain -+ // is built with the ms_skipfipscheck build tag. -+ execGoTool(t, false, env, "build", "-o", outPath(t), cryptoFile) -+ execGoTool(t, false, env, "build", "-o", outPath(t), nonCryptoFile) -+ -+ // Run should always succeed if the target go package -+ // doesn't use crypto. -+ execGoTool(t, false, env, "run", nonCryptoFile) -+ -+ // Run may or may not fail if the target go package uses crypto, -+ // because while the toolchain was built with ms_skipfipscheck, the -+ // target program was not. Failure depends on system crypto mode -+ // and presence of system-provided crypto, and it can't be tested here. -+} -+ -+func TestSystemCryptoDefault(t *testing.T) { -+ t.Parallel() -+ cryptoFile := writeFile(t, fileWithCrypto) -+ -+ // Add here all the OS/ARCH combinations that enable systemcrypto by default. -+ type testCase struct { -+ goos string -+ goarch string -+ } -+ test := []testCase{ -+ {"linux", "amd64"}, -+ {"linux", "arm64"}, -+ {"freebsd", "amd64"}, -+ {"freebsd", "arm64"}, -+ {"darwin", "amd64"}, -+ {"darwin", "arm64"}, -+ {"windows", "amd64"}, -+ {"windows", "arm64"}, -+ } -+ for _, tt := range test { -+ t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { -+ t.Parallel() -+ out := outPath(t) -+ env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} -+ execGoTool(t, false, env, "build", "-o", out, cryptoFile) -+ // Check that the binary has the correct settings. -+ settings, _ := execGoTool(t, false, nil, "version", "-m", out) -+ if !strings.Contains(settings, "microsoft_systemcrypto=1") { -+ t.Errorf("expected microsoft_systemcrypto=1 in settings, got %v", settings) -+ } -+ }) -+ } -+} -+ -+func TestSystemCryptoDisabled(t *testing.T) { -+ t.Parallel() -+ cryptoFile := writeFile(t, fileWithCrypto) -+ -+ tests := []struct { -+ goos string -+ goarch string -+ }{ -+ {"linux", "amd64"}, -+ {"linux", "arm64"}, -+ {"freebsd", "amd64"}, -+ {"freebsd", "arm64"}, -+ {"darwin", "amd64"}, -+ {"darwin", "arm64"}, -+ {"windows", "amd64"}, -+ {"windows", "arm64"}, -+ } -+ for _, tt := range tests { -+ t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { -+ t.Parallel() -+ out := outPath(t) -+ env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=1"} -+ execGoTool(t, false, env, "build", "-o", out, cryptoFile) -+ settings, _ := execGoTool(t, false, nil, "version", "-m", out) -+ if strings.Contains(settings, "microsoft_systemcrypto=1") { -+ t.Errorf("expected microsoft_systemcrypto=1 not to be in settings, got %v", settings) -+ } -+ }) -+ } -+} -+ -+func TestSystemCryptoBuildTag(t *testing.T) { -+ t.Parallel() -+ -+ dir := t.TempDir() -+ files := map[string]string{ -+ "go.mod": "module example.com/systemcrypto-tag\n\ngo 1.27\n", -+ "always.go": "package main\n", -+ "with_system.go": "//go:build goexperiment.systemcrypto\n\npackage main\n", -+ "with_openssl.go": "//go:build goexperiment.opensslcrypto\n\npackage main\n", -+ "with_cng.go": "//go:build goexperiment.cngcrypto\n\npackage main\n", -+ "with_darwin.go": "//go:build goexperiment.darwincrypto\n\npackage main\n", -+ "without_system.go": "//go:build !goexperiment.systemcrypto\n\npackage main\n", -+ } -+ for name, content := range files { -+ if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { -+ t.Fatal(err) -+ } -+ } -+ -+ tests := []struct { -+ name string -+ env []string -+ wantFiles []string -+ badFiles []string -+ }{ -+ { -+ name: "linux_supported", -+ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFiles: []string{"with_system.go", "with_openssl.go"}, -+ badFiles: []string{"without_system.go", "with_cng.go", "with_darwin.go"}, -+ }, -+ { -+ name: "darwin_supported", -+ env: []string{"GOOS=darwin", "GOARCH=arm64", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFiles: []string{"with_system.go", "with_darwin.go"}, -+ badFiles: []string{"without_system.go", "with_openssl.go", "with_cng.go"}, -+ }, -+ { -+ name: "windows_supported", -+ env: []string{"GOOS=windows", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFiles: []string{"with_system.go", "with_cng.go"}, -+ badFiles: []string{"without_system.go", "with_openssl.go", "with_darwin.go"}, -+ }, -+ { -+ name: "freebsd_supported", -+ env: []string{"GOOS=freebsd", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFiles: []string{"with_system.go", "with_openssl.go"}, -+ badFiles: []string{"without_system.go", "with_cng.go", "with_darwin.go"}, -+ }, -+ { -+ name: "disabled", -+ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=1"}, -+ wantFiles: []string{"without_system.go"}, -+ badFiles: []string{"with_system.go", "with_openssl.go", "with_cng.go", "with_darwin.go"}, -+ }, -+ { -+ name: "unsupported", -+ env: []string{"GOOS=plan9", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFiles: []string{"without_system.go"}, -+ badFiles: []string{"with_system.go", "with_openssl.go", "with_cng.go", "with_darwin.go"}, -+ }, -+ } -+ for _, tt := range tests { -+ t.Run(tt.name, func(t *testing.T) { -+ t.Parallel() -+ out, _ := execGoToolInDir(t, dir, false, tt.env, "list", "-f", "{{.GoFiles}}", ".") -+ for _, wantFile := range tt.wantFiles { -+ if !strings.Contains(out, wantFile) { -+ t.Fatalf("go list .GoFiles = %q, want %s", out, wantFile) -+ } -+ } -+ for _, badFile := range tt.badFiles { -+ if strings.Contains(out, badFile) { -+ t.Fatalf("go list .GoFiles = %q, did not want %s", out, badFile) -+ } -+ } -+ }) -+ } -+} diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README index cd0484243fdeed..62e821defaf12c 100644 --- a/src/cmd/go/testdata/script/README @@ -4270,62 +3958,6 @@ index 198692f5aaa6e5..5c1a31f1c09f3b 100644 } var dsaPriv dsa.PrivateKey -diff --git a/src/go/build/buildbackend_test.go b/src/go/build/buildbackend_test.go -new file mode 100644 -index 00000000000000..ffb835ce34a2f7 ---- /dev/null -+++ b/src/go/build/buildbackend_test.go -@@ -0,0 +1,50 @@ -+// Copyright 2023 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 build -+ -+import ( -+ "reflect" -+ "testing" -+) -+ -+// Check that the systemcrypto tag works and collects AllTags correctly. -+// This is based on the TestAllTags test. -+func TestCryptoBackendAllTags(t *testing.T) { -+ ctxt := Default -+ // Remove tool tags so these tests behave the same regardless of the -+ // goexperiments that happen to be set during the run. -+ ctxt.ToolTags = []string{} -+ ctxt.GOARCH = "amd64" -+ ctxt.GOOS = "linux" -+ ctxt.BuildTags = []string{"goexperiment.systemcrypto"} -+ -+ p, err := ctxt.ImportDir("testdata/backendtags_system", 0) -+ if err != nil { -+ t.Fatal(err) -+ } -+ want := []string{"goexperiment.systemcrypto"} -+ if !reflect.DeepEqual(p.AllTags, want) { -+ t.Errorf("AllTags = %v, want %v", p.AllTags, want) -+ } -+ wantFiles := []string{"main.go", "systemcrypto.go"} -+ if !reflect.DeepEqual(p.GoFiles, wantFiles) { -+ t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) -+ } -+ -+ // Test without systemcrypto - only main.go should be included -+ ctxt.BuildTags = []string{} -+ p, err = ctxt.ImportDir("testdata/backendtags_system", 0) -+ if err != nil { -+ t.Fatal(err) -+ } -+ want = []string{"goexperiment.systemcrypto"} -+ if !reflect.DeepEqual(p.AllTags, want) { -+ t.Errorf("AllTags = %v, want %v", p.AllTags, want) -+ } -+ wantFiles = []string{"main.go"} -+ if !reflect.DeepEqual(p.GoFiles, wantFiles) { -+ t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) -+ } -+} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 926a533c9c1fb8..2e86f85ffc2da1 100644 --- a/src/go/build/deps_test.go @@ -4500,24 +4132,6 @@ index 926a533c9c1fb8..2e86f85ffc2da1 100644 < crypto/x509/internal/macos < crypto/x509/pkix < crypto/x509 -diff --git a/src/go/build/testdata/backendtags_system/main.go b/src/go/build/testdata/backendtags_system/main.go -new file mode 100644 -index 00000000000000..38dd16da61accb ---- /dev/null -+++ b/src/go/build/testdata/backendtags_system/main.go -@@ -0,0 +1,3 @@ -+package main -+ -+func main() {} -diff --git a/src/go/build/testdata/backendtags_system/systemcrypto.go b/src/go/build/testdata/backendtags_system/systemcrypto.go -new file mode 100644 -index 00000000000000..eb8a026982259c ---- /dev/null -+++ b/src/go/build/testdata/backendtags_system/systemcrypto.go -@@ -0,0 +1,3 @@ -+//go:build goexperiment.systemcrypto -+ -+package main diff --git a/src/hash/example_test.go b/src/hash/example_test.go index f07b9aaa2c4898..b380537215634d 100644 --- a/src/hash/example_test.go diff --git a/toolchaintest/README.md b/toolchaintest/README.md new file mode 100644 index 00000000000..a8ea8ee96df --- /dev/null +++ b/toolchaintest/README.md @@ -0,0 +1,21 @@ +# Toolchain Tests + +Tests for Microsoft-specific Go toolchain behavior, kept outside the upstream Go patch set. The repository's build and CI test runners run this module alongside the upstream tests. + +After [building the toolchain](../eng/doc/DeveloperGuide.md#build-the-go-toolchain), run from this directory: + +```sh +../go/bin/go test -count=1 +``` + +On Windows, use `..\go\bin\go.exe`. + +The CLI tests default to the Go executable in the running test binary's GOROOT. To use a different toolchain, including when running the tests with a bootstrap Go installation: + +```sh +go test -count=1 -go=/path/to/microsoft-go/bin/go +``` + +The public `go/build` API tests use the toolchain that compiles the test binary. Run the module with the built Microsoft Go executable to test both APIs and CLI behavior with that toolchain. + +The module has no external dependencies. It covers system-crypto build tags, cross-compilation, build information, and FIPS-mode command behavior. diff --git a/toolchaintest/buildbackend_test.go b/toolchaintest/buildbackend_test.go new file mode 100644 index 00000000000..6cea1907477 --- /dev/null +++ b/toolchaintest/buildbackend_test.go @@ -0,0 +1,51 @@ +// Copyright 2023 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 toolchaintest + +import ( + "go/build" + "reflect" + "testing" +) + +// Check that the systemcrypto tag works and collects AllTags correctly. +// This is based on the TestAllTags test. +func TestCryptoBackendAllTags(t *testing.T) { + ctxt := build.Default + // Remove tool tags so these tests behave the same regardless of the + // goexperiments that happen to be set during the run. + ctxt.ToolTags = []string{} + ctxt.GOARCH = "amd64" + ctxt.GOOS = "linux" + ctxt.BuildTags = []string{"goexperiment.systemcrypto"} + + p, err := ctxt.ImportDir("testdata/backendtags_system", 0) + if err != nil { + t.Fatal(err) + } + want := []string{"goexperiment.systemcrypto"} + if !reflect.DeepEqual(p.AllTags, want) { + t.Errorf("AllTags = %v, want %v", p.AllTags, want) + } + wantFiles := []string{"main.go", "systemcrypto.go"} + if !reflect.DeepEqual(p.GoFiles, wantFiles) { + t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) + } + + // Test without systemcrypto - only main.go should be included + ctxt.BuildTags = []string{} + p, err = ctxt.ImportDir("testdata/backendtags_system", 0) + if err != nil { + t.Fatal(err) + } + want = []string{"goexperiment.systemcrypto"} + if !reflect.DeepEqual(p.AllTags, want) { + t.Errorf("AllTags = %v, want %v", p.AllTags, want) + } + wantFiles = []string{"main.go"} + if !reflect.DeepEqual(p.GoFiles, wantFiles) { + t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) + } +} diff --git a/toolchaintest/go.mod b/toolchaintest/go.mod new file mode 100644 index 00000000000..9cb286e35b0 --- /dev/null +++ b/toolchaintest/go.mod @@ -0,0 +1,3 @@ +module github.com/microsoft/go/toolchaintest + +go 1.26.0 diff --git a/toolchaintest/systemcrypto_test.go b/toolchaintest/systemcrypto_test.go new file mode 100644 index 00000000000..cb83c61a569 --- /dev/null +++ b/toolchaintest/systemcrypto_test.go @@ -0,0 +1,327 @@ +// Copyright 2025 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 toolchaintest + +import ( + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var goExecutable = flag.String("go", "", "Go executable to test; defaults to the running toolchain's GOROOT/bin/go") + +const fileWithCrypto = ` +package main + +import ( + "crypto/sha256" + "fmt" +) + +func main() { + fmt.Println(sha256.Sum256([]byte("Hello, World!"))) +} +` + +const fileWithoutCrypto = ` +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("Hello, World!") +} +` + +func goToolPath(t *testing.T) string { + t.Helper() + path := *goExecutable + if path == "" { + name := "go" + if runtime.GOOS == "windows" { + name += ".exe" + } + path = filepath.Join(runtime.GOROOT(), "bin", name) + } + path, err := filepath.Abs(path) + if err != nil { + t.Fatal(err) + } + return path +} + +func execGoTool(t *testing.T, allowFail bool, env []string, args ...string) (string, bool) { + t.Helper() + return execGoToolInDir(t, "", allowFail, env, args...) +} + +func execGoToolInDir(t *testing.T, dir string, allowFail bool, env []string, args ...string) (string, bool) { + t.Helper() + cmd := exec.CommandContext(t.Context(), goToolPath(t), args...) + cmd.Dir = dir + for _, entry := range cmd.Environ() { + name, _, _ := strings.Cut(entry, "=") + switch name { + case "GOROOT", "GODEBUG", "GOTRACEBACK": + continue + } + cmd.Env = append(cmd.Env, entry) + } + cmd.Env = append(cmd.Env, "GOTOOLCHAIN=local") + cmd.Env = append(cmd.Env, env...) + out, err := cmd.CombinedOutput() + sout := strings.TrimSpace(string(out)) + if err != nil { + if allowFail { + return sout, false + } + t.Fatalf("go %s failed: %v\n%s", strings.Join(args, " "), err, out) + } + return sout, true +} + +func writeFile(t *testing.T, content string) string { + t.Helper() + name := "main.go" + if strings.Contains(content, "testing") { + name = "main_test.go" + } + name = filepath.Join(t.TempDir(), name) + if err := os.WriteFile(name, []byte(content), 0o644); err != nil { + t.Fatal(err) + } + return name +} + +func outPath(t *testing.T) string { + t.Helper() + return filepath.Join(t.TempDir(), "out") +} + +func TestSystemCryptoNoCgoChecks(t *testing.T) { + t.Parallel() + + cryptoFile := writeFile(t, fileWithCrypto) + + tt := []struct { + goos string + goarch string + }{ + {"linux", "386"}, + {"linux", "amd64"}, + {"linux", "arm64"}, + {"linux", "loong64"}, + {"linux", "ppc64le"}, + {"linux", "riscv64"}, + {"linux", "s390x"}, + {"linux", "arm"}, + {"freebsd", "amd64"}, + {"freebsd", "arm64"}, + {"darwin", "amd64"}, + {"darwin", "arm64"}, + {"windows", "386"}, + {"windows", "amd64"}, + {"windows", "arm64"}, + } + for _, tc := range tt { + t.Run(tc.goos+"/"+tc.goarch, func(t *testing.T) { + t.Parallel() + env := []string{"CGO_ENABLED=0", "GOOS=" + tc.goos, "GOARCH=" + tc.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} + if out, ok := execGoTool(t, true, env, "build", "-o", outPath(t), cryptoFile); !ok { + t.Fatalf("expected success, got failure: %s", out) + } + }) + } +} + +func TestSystemCryptoFreeBSDNoCgo(t *testing.T) { + t.Parallel() + + cryptoFile := writeFile(t, fileWithCrypto) + env := []string{"CGO_ENABLED=0", "GOOS=freebsd", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"} + out, ok := execGoTool(t, true, env, "build", "-o", outPath(t), cryptoFile) + if ok { + t.Fatal("expected failure, got success") + } + if !strings.Contains(out, "Using system crypto on FreeBSD requires CGO_ENABLED=1 on architectures other than amd64 and arm64") { + t.Fatalf("expected cgo requirement error, got: %s", out) + } +} + +func TestSystemCryptoFIPS(t *testing.T) { + // Test different go commands with GODEBUG=fips140=on + // to exercise the ms_skipfipscheck build tag. + t.Parallel() + env := []string{"GODEBUG=fips140=on"} + + cryptoFile := writeFile(t, fileWithCrypto) + nonCryptoFile := writeFile(t, fileWithoutCrypto) + + // Build should always succeed given that the go toolchain + // is built with the ms_skipfipscheck build tag. + execGoTool(t, false, env, "build", "-o", outPath(t), cryptoFile) + execGoTool(t, false, env, "build", "-o", outPath(t), nonCryptoFile) + + // Run should always succeed if the target go package + // doesn't use crypto. + execGoTool(t, false, env, "run", nonCryptoFile) + + // Run may or may not fail if the target go package uses crypto, + // because while the toolchain was built with ms_skipfipscheck, the + // target program was not. Failure depends on system crypto mode + // and presence of system-provided crypto, and it can't be tested here. +} + +func TestSystemCryptoDefault(t *testing.T) { + t.Parallel() + cryptoFile := writeFile(t, fileWithCrypto) + + // Add here all the OS/ARCH combinations that enable systemcrypto by default. + type testCase struct { + goos string + goarch string + } + test := []testCase{ + {"linux", "amd64"}, + {"linux", "arm64"}, + {"freebsd", "amd64"}, + {"freebsd", "arm64"}, + {"darwin", "amd64"}, + {"darwin", "arm64"}, + {"windows", "amd64"}, + {"windows", "arm64"}, + } + for _, tt := range test { + t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { + t.Parallel() + out := outPath(t) + env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} + execGoTool(t, false, env, "build", "-o", out, cryptoFile) + // Check that the binary has the correct settings. + settings, _ := execGoTool(t, false, nil, "version", "-m", out) + if !strings.Contains(settings, "microsoft_systemcrypto=1") { + t.Errorf("expected microsoft_systemcrypto=1 in settings, got %v", settings) + } + }) + } +} + +func TestSystemCryptoDisabled(t *testing.T) { + t.Parallel() + cryptoFile := writeFile(t, fileWithCrypto) + + tests := []struct { + goos string + goarch string + }{ + {"linux", "amd64"}, + {"linux", "arm64"}, + {"freebsd", "amd64"}, + {"freebsd", "arm64"}, + {"darwin", "amd64"}, + {"darwin", "arm64"}, + {"windows", "amd64"}, + {"windows", "arm64"}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { + t.Parallel() + out := outPath(t) + env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=1"} + execGoTool(t, false, env, "build", "-o", out, cryptoFile) + settings, _ := execGoTool(t, false, nil, "version", "-m", out) + if strings.Contains(settings, "microsoft_systemcrypto=1") { + t.Errorf("expected microsoft_systemcrypto=1 not to be in settings, got %v", settings) + } + }) + } +} + +func TestSystemCryptoBuildTag(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + files := map[string]string{ + "go.mod": "module example.com/systemcrypto-tag\n\ngo 1.27\n", + "always.go": "package main\n", + "with_system.go": "//go:build goexperiment.systemcrypto\n\npackage main\n", + "with_openssl.go": "//go:build goexperiment.opensslcrypto\n\npackage main\n", + "with_cng.go": "//go:build goexperiment.cngcrypto\n\npackage main\n", + "with_darwin.go": "//go:build goexperiment.darwincrypto\n\npackage main\n", + "without_system.go": "//go:build !goexperiment.systemcrypto\n\npackage main\n", + } + for name, content := range files { + if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { + t.Fatal(err) + } + } + + tests := []struct { + name string + env []string + wantFiles []string + badFiles []string + }{ + { + name: "linux_supported", + env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, + wantFiles: []string{"with_system.go", "with_openssl.go"}, + badFiles: []string{"without_system.go", "with_cng.go", "with_darwin.go"}, + }, + { + name: "darwin_supported", + env: []string{"GOOS=darwin", "GOARCH=arm64", "MS_GO_NOSYSTEMCRYPTO=0"}, + wantFiles: []string{"with_system.go", "with_darwin.go"}, + badFiles: []string{"without_system.go", "with_openssl.go", "with_cng.go"}, + }, + { + name: "windows_supported", + env: []string{"GOOS=windows", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"}, + wantFiles: []string{"with_system.go", "with_cng.go"}, + badFiles: []string{"without_system.go", "with_openssl.go", "with_darwin.go"}, + }, + { + name: "freebsd_supported", + env: []string{"GOOS=freebsd", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, + wantFiles: []string{"with_system.go", "with_openssl.go"}, + badFiles: []string{"without_system.go", "with_cng.go", "with_darwin.go"}, + }, + { + name: "disabled", + env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=1"}, + wantFiles: []string{"without_system.go"}, + badFiles: []string{"with_system.go", "with_openssl.go", "with_cng.go", "with_darwin.go"}, + }, + { + name: "unsupported", + env: []string{"GOOS=plan9", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, + wantFiles: []string{"without_system.go"}, + badFiles: []string{"with_system.go", "with_openssl.go", "with_cng.go", "with_darwin.go"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + out, _ := execGoToolInDir(t, dir, false, tt.env, "list", "-f", "{{.GoFiles}}", ".") + for _, wantFile := range tt.wantFiles { + if !strings.Contains(out, wantFile) { + t.Fatalf("go list .GoFiles = %q, want %s", out, wantFile) + } + } + for _, badFile := range tt.badFiles { + if strings.Contains(out, badFile) { + t.Fatalf("go list .GoFiles = %q, did not want %s", out, badFile) + } + } + }) + } +} diff --git a/toolchaintest/testdata/backendtags_system/main.go b/toolchaintest/testdata/backendtags_system/main.go new file mode 100644 index 00000000000..38dd16da61a --- /dev/null +++ b/toolchaintest/testdata/backendtags_system/main.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/toolchaintest/testdata/backendtags_system/systemcrypto.go b/toolchaintest/testdata/backendtags_system/systemcrypto.go new file mode 100644 index 00000000000..eb8a0269822 --- /dev/null +++ b/toolchaintest/testdata/backendtags_system/systemcrypto.go @@ -0,0 +1,3 @@ +//go:build goexperiment.systemcrypto + +package main