From 78fbf606bc2084b6bf2ba79af5c19bd13f587511 Mon Sep 17 00:00:00 2001 From: Kevin Paul Herbert Date: Wed, 9 Sep 2020 13:54:40 -0400 Subject: [PATCH] Import Upstream version 1.14.6 --- VERSION | 2 +- doc/go1.14.html | 10 +- src/cmd/compile/internal/ssa/deadstore.go | 16 +- src/cmd/compile/internal/ssa/debug_test.go | 20 +- src/cmd/go.mod | 2 +- src/cmd/go.sum | 4 +- src/cmd/go/go_test.go | 39 - .../testdata/script/build_cache_arch_mode.txt | 27 + .../script/test_benchmark_chatty_fail.txt | 32 + .../script/test_benchmark_chatty_success.txt | 29 + .../go/testdata/script/test_chatty_fail.txt | 32 + .../script/test_chatty_parallel_fail.txt | 58 ++ .../script/test_chatty_parallel_success.txt | 52 + .../test_chatty_parallel_success_sleepy.txt | 39 + .../testdata/script/test_chatty_success.txt | 27 + src/cmd/go/testdata/script/test_regexps.txt | 14 +- src/cmd/link/internal/arm/asm.go | 6 +- src/cmd/link/internal/ld/pcln.go | 2 +- src/cmd/link/internal/ppc64/asm.go | 6 +- src/cmd/link/internal/sym/attribute.go | 6 +- src/cmd/link/link_test.go | 63 ++ .../tools/go/analysis/passes/printf/printf.go | 1 + src/cmd/vendor/modules.txt | 2 +- src/crypto/x509/root_windows.go | 46 +- src/crypto/x509/verify.go | 43 +- src/crypto/x509/verify_test.go | 888 ++++++++---------- src/database/sql/driver/driver.go | 9 +- src/database/sql/fakedb_test.go | 45 +- src/database/sql/sql.go | 200 ++-- src/database/sql/sql_test.go | 219 +++++ src/encoding/json/decode.go | 74 +- src/encoding/json/decode_test.go | 16 + src/net/http/fs.go | 1 + src/net/http/fs_test.go | 9 + src/net/http/server.go | 43 +- src/reflect/all_test.go | 6 + src/reflect/deepequal.go | 16 +- src/reflect/type.go | 1 + src/reflect/value.go | 8 + src/runtime/stack.go | 1 + src/testing/benchmark.go | 3 + src/testing/sub_test.go | 126 +-- src/testing/testing.go | 80 +- test/fixedbugs/issue39459.go | 22 + test/fixedbugs/issue39541.go | 33 + 45 files changed, 1532 insertions(+), 846 deletions(-) create mode 100644 src/cmd/go/testdata/script/build_cache_arch_mode.txt create mode 100644 src/cmd/go/testdata/script/test_benchmark_chatty_fail.txt create mode 100644 src/cmd/go/testdata/script/test_benchmark_chatty_success.txt create mode 100644 src/cmd/go/testdata/script/test_chatty_fail.txt create mode 100644 src/cmd/go/testdata/script/test_chatty_parallel_fail.txt create mode 100644 src/cmd/go/testdata/script/test_chatty_parallel_success.txt create mode 100644 src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt create mode 100644 src/cmd/go/testdata/script/test_chatty_success.txt create mode 100644 test/fixedbugs/issue39459.go create mode 100644 test/fixedbugs/issue39541.go diff --git a/VERSION b/VERSION index 2008a80..a1f17d1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -go1.14.4 \ No newline at end of file +go1.14.6 \ No newline at end of file diff --git a/doc/go1.14.html b/doc/go1.14.html index 4780353..35a9f3c 100644 --- a/doc/go1.14.html +++ b/doc/go1.14.html @@ -466,7 +466,15 @@

Minor changes to the library

certificate, and letting the package automatically select the best one. Note that the performance of this selection is going to be poor unless the Certificate.Leaf - field is set. + field is set. The + Config.NameToCertificate + field, which only supports associating a single certificate with + a give name, is now deprecated and should be left as nil. + Similarly the + Config.BuildNameToCertificate + method, which builds the NameToCertificate field + from the leaf certificates, is now deprecated and should not be + called.

diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index 88af7a6..0664013 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -73,9 +73,11 @@ func dse(f *Func) { } // Walk backwards looking for dead stores. Keep track of shadowed addresses. - // An "address" is an SSA Value which encodes both the address and size of - // the write. This code will not remove dead stores to the same address - // of different types. + // A "shadowed address" is a pointer and a size describing a memory region that + // is known to be written. We keep track of shadowed addresses in the shadowed + // map, mapping the ID of the address to the size of the shadowed region. + // Since we're walking backwards, writes to a shadowed region are useless, + // as they will be immediately overwritten. shadowed.clear() v := last @@ -93,17 +95,13 @@ func dse(f *Func) { sz = v.AuxInt } if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz { - // Modify store into a copy + // Modify the store/zero into a copy of the memory state, + // effectively eliding the store operation. if v.Op == OpStore { // store addr value mem v.SetArgs1(v.Args[2]) } else { // zero addr mem - typesz := v.Args[0].Type.Elem().Size() - if sz != typesz { - f.Fatalf("mismatched zero/store sizes: %d and %d [%s]", - sz, typesz, v.LongString()) - } v.SetArgs1(v.Args[1]) } v.Aux = nil diff --git a/src/cmd/compile/internal/ssa/debug_test.go b/src/cmd/compile/internal/ssa/debug_test.go index 28bb88a..25802fd 100644 --- a/src/cmd/compile/internal/ssa/debug_test.go +++ b/src/cmd/compile/internal/ssa/debug_test.go @@ -49,11 +49,11 @@ var gogcflags = os.Getenv("GO_GCFLAGS") // optimizedLibs usually means "not running in a noopt test builder". var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gogcflags, "-l")) -// TestNexting go-builds a file, then uses a debugger (default gdb, optionally delve) +// TestNexting go-builds a file, then uses a debugger (default delve, optionally gdb) // to next through the generated executable, recording each line landed at, and // then compares those lines with reference file(s). // Flag -u updates the reference file(s). -// Flag -d changes the debugger to delve (and uses delve-specific reference files) +// Flag -g changes the debugger to gdb (and uses gdb-specific reference files) // Flag -v is ever-so-slightly verbose. // Flag -n is for dry-run, and prints the shell and first debug commands. // @@ -83,9 +83,9 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog // to indicate normalization of Strings, (hex) addresses, and numbers. // "O" is an explicit indication that we expect it to be optimized out. // For example: -/* - if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A) -*/ +// +// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A) +// // TODO: not implemented for Delve yet, but this is the plan // // After a compiler change that causes a difference in the debug behavior, check @@ -93,7 +93,7 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog // go test debug_test.go -args -u // (for Delve) // go test debug_test.go -args -u -d - +// func TestNexting(t *testing.T) { skipReasons := "" // Many possible skip reasons, list all that apply if testing.Short() { @@ -108,7 +108,13 @@ func TestNexting(t *testing.T) { // Various architectures tend to differ slightly sometimes, and keeping them // all in sync is a pain for people who don't have them all at hand, // so limit testing to amd64 (for now) - skipReasons += "not run when testing gdb (-g) unless forced (-f) or linux-amd64" + skipReasons += "not run when testing gdb (-g) unless forced (-f) or linux-amd64; " + } + + if !*useGdb && !*force && testenv.Builder() == "linux-386-longtest" { + // The latest version of Delve does support linux/386. However, the version currently + // installed in the linux-386-longtest builder does not. See golang.org/issue/39309. + skipReasons += "not run when testing delve on linux-386-longtest builder unless forced (-f); " } if *useGdb { diff --git a/src/cmd/go.mod b/src/cmd/go.mod index da8d6b8..7b6ada8 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -9,5 +9,5 @@ require ( golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d golang.org/x/mod v0.2.0 golang.org/x/sys v0.0.0-20200131233351-9e5cf931a04b // indirect - golang.org/x/tools v0.0.0-20200131233409-575de47986ce + golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0 ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index a4c29e2..f31342b 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -22,8 +22,8 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200131233409-575de47986ce h1:Uglradbb4KfUWaYasZhlsDsGRwHHvRsHoNAEONef0W8= -golang.org/x/tools v0.0.0-20200131233409-575de47986ce/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0 h1:6txNFSnY+tteYoO+hf01EpdYcYZiurdC9MDIrcUzEu4= +golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 8ca34d4..de0f595 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -3955,45 +3955,6 @@ func TestCgoFlagContainsSpace(t *testing.T) { tg.grepStderrNot(`"-L[^"]+c flags".*"-L[^"]+c flags"`, "found too many quoted ld flags") } -// Issue 9737: verify that GOARM and GO386 affect the computed build ID. -func TestBuildIDContainsArchModeEnv(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - var tg *testgoData - testWith := func(before, after func()) func(*testing.T) { - return func(t *testing.T) { - tg = testgo(t) - defer tg.cleanup() - tg.tempFile("src/mycmd/x.go", `package main -func main() {}`) - tg.setenv("GOPATH", tg.path(".")) - - tg.cd(tg.path("src/mycmd")) - tg.setenv("GOOS", "linux") - before() - tg.run("install", "mycmd") - after() - tg.wantStale("mycmd", "stale dependency", "should be stale after environment variable change") - } - } - - t.Run("386", testWith(func() { - tg.setenv("GOARCH", "386") - tg.setenv("GO386", "387") - }, func() { - tg.setenv("GO386", "sse2") - })) - - t.Run("arm", testWith(func() { - tg.setenv("GOARCH", "arm") - tg.setenv("GOARM", "5") - }, func() { - tg.setenv("GOARM", "7") - })) -} - func TestListTests(t *testing.T) { tooSlow(t) var tg *testgoData diff --git a/src/cmd/go/testdata/script/build_cache_arch_mode.txt b/src/cmd/go/testdata/script/build_cache_arch_mode.txt new file mode 100644 index 0000000..7e751d0 --- /dev/null +++ b/src/cmd/go/testdata/script/build_cache_arch_mode.txt @@ -0,0 +1,27 @@ +# Issue 9737: verify that GOARM and GO386 affect the computed build ID + +[short] skip + +# 386 +cd $GOPATH/src/mycmd +env GOOS=linux +env GOARCH=386 +env GO386=387 +go install mycmd +env GO386=sse2 +stale mycmd + +# arm +cd $GOPATH/src/mycmd +env GOOS=linux +env GOARCH=arm +env GOARM=5 +go install mycmd +env GOARM=7 +stale mycmd + + +-- mycmd/x.go -- +package main + +func main() {} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_benchmark_chatty_fail.txt b/src/cmd/go/testdata/script/test_benchmark_chatty_fail.txt new file mode 100644 index 0000000..6031ead --- /dev/null +++ b/src/cmd/go/testdata/script/test_benchmark_chatty_fail.txt @@ -0,0 +1,32 @@ +# Run chatty tests. Assert on CONT lines. +! go test chatty_test.go -v -bench . chatty_bench + +# Sanity check that output occurs. +stdout -count=2 'this is sub-0' +stdout -count=2 'this is sub-1' +stdout -count=2 'this is sub-2' +stdout -count=1 'error from sub-0' +stdout -count=1 'error from sub-1' +stdout -count=1 'error from sub-2' + +# Benchmarks should not print CONT. +! stdout CONT + +-- chatty_test.go -- +package chatty_bench + +import ( + "testing" + "fmt" +) + +func BenchmarkChatty(b *testing.B) { + for i := 0; i < 3; i++ { + b.Run(fmt.Sprintf("sub-%d", i), func(b *testing.B) { + for j := 0; j < 2; j++ { + b.Logf("this is sub-%d", i) + } + b.Errorf("error from sub-%d", i) + }) + } +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_benchmark_chatty_success.txt b/src/cmd/go/testdata/script/test_benchmark_chatty_success.txt new file mode 100644 index 0000000..a1c0d65 --- /dev/null +++ b/src/cmd/go/testdata/script/test_benchmark_chatty_success.txt @@ -0,0 +1,29 @@ +# Run chatty tests. Assert on CONT lines. +go test chatty_test.go -v -bench . chatty_bench + +# Sanity check that output happens. We don't provide -count because the amount +# of output is variable. +stdout 'this is sub-0' +stdout 'this is sub-1' +stdout 'this is sub-2' + +# Benchmarks should not print CONT. +! stdout CONT + +-- chatty_test.go -- +package chatty_bench + +import ( + "testing" + "fmt" +) + +func BenchmarkChatty(b *testing.B) { + for i := 0; i < 3; i++ { + b.Run(fmt.Sprintf("sub-%d", i), func(b *testing.B) { + for j := 0; j < 2; j++ { + b.Logf("this is sub-%d", i) + } + }) + } +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_chatty_fail.txt b/src/cmd/go/testdata/script/test_chatty_fail.txt new file mode 100644 index 0000000..a5ef559 --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_fail.txt @@ -0,0 +1,32 @@ +# Run chatty tests. Assert on CONT lines. +! go test chatty_test.go -v + +# Sanity check that output occurs. +stdout -count=2 'this is sub-0' +stdout -count=2 'this is sub-1' +stdout -count=2 'this is sub-2' +stdout -count=1 'error from sub-0' +stdout -count=1 'error from sub-1' +stdout -count=1 'error from sub-2' + +# Non-parallel tests should not print CONT. +! stdout CONT + +-- chatty_test.go -- +package chatty_test + +import ( + "testing" + "fmt" +) + +func TestChatty(t *testing.T) { + for i := 0; i < 3; i++ { + t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) { + for j := 0; j < 2; j++ { + t.Logf("this is sub-%d", i) + } + t.Errorf("error from sub-%d", i) + }) + } +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_fail.txt b/src/cmd/go/testdata/script/test_chatty_parallel_fail.txt new file mode 100644 index 0000000..3f7360b --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_parallel_fail.txt @@ -0,0 +1,58 @@ +# Run parallel chatty tests. Assert on CONT lines. This test makes sure that +# multiple parallel outputs have the appropriate CONT lines between them. +! go test -parallel 3 chatty_parallel_test.go -v + +stdout -count=1 '^=== CONT TestChattyParallel/sub-0\n chatty_parallel_test.go:38: error from sub-0$' +stdout -count=1 '^=== CONT TestChattyParallel/sub-1\n chatty_parallel_test.go:38: error from sub-1$' +stdout -count=1 '^=== CONT TestChattyParallel/sub-2\n chatty_parallel_test.go:38: error from sub-2$' + +# Run parallel chatty tests with -json. Assert on CONT lines as above - make +# sure there are CONT lines before each output line. +! go test -json -parallel 3 chatty_parallel_test.go -v +stdout -count=1 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":"=== CONT TestChattyParallel/sub-0\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":" chatty_parallel_test.go:38: error from sub-0\\n"}' +stdout -count=1 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":"=== CONT TestChattyParallel/sub-1\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":" chatty_parallel_test.go:38: error from sub-1\\n"}' +stdout -count=1 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":"=== CONT TestChattyParallel/sub-2\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":" chatty_parallel_test.go:38: error from sub-2\\n"}' + +-- chatty_parallel_test.go -- +package chatty_paralell_test + +import ( + "testing" + "fmt" + "flag" +) + +// This test ensures the the order of CONT lines in parallel chatty tests. +func TestChattyParallel(t *testing.T) { + t.Parallel() + + // The number of concurrent tests running. This is closely tied to the + // -parallel test flag, so we grab it from the flag rather than setting it + // to some constant. + parallel := flag.Lookup("test.parallel").Value.(flag.Getter).Get().(int) + + // ready is a synchronization mechanism that causes subtests to execute + // round robin. + ready := make([]chan bool, parallel) + for i := range ready { + ready[i] = make(chan bool, 1) + } + ready[0] <- true + + for i := range ready { + i := i + t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) { + t.Parallel() + + // Some basic log output to precede the failures. + <-ready[i] + t.Logf("this is sub-%d", i) + ready[(i+1)%len(ready)] <- true + + // The actual failure messages we care about. + <-ready[i] + t.Errorf("error from sub-%d", i) + ready[(i+1)%len(ready)] <- true + }) + } +} diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_success.txt b/src/cmd/go/testdata/script/test_chatty_parallel_success.txt new file mode 100644 index 0000000..4a86d74 --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_parallel_success.txt @@ -0,0 +1,52 @@ +# Run parallel chatty tests. Assert on CONT lines. This test makes sure that +# multiple parallel outputs have the appropriate CONT lines between them. +go test -parallel 3 chatty_parallel_test.go -v +stdout -count=2 '^=== CONT TestChattyParallel/sub-0\n chatty_parallel_test.go:32: this is sub-0$' +stdout -count=2 '^=== CONT TestChattyParallel/sub-1\n chatty_parallel_test.go:32: this is sub-1$' +stdout -count=2 '^=== CONT TestChattyParallel/sub-2\n chatty_parallel_test.go:32: this is sub-2$' + +# Run parallel chatty tests with -json. Assert on CONT lines as above - make +# sure there are CONT lines before each output line. +go test -json -parallel 3 chatty_parallel_test.go -v +stdout -count=2 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":"=== CONT TestChattyParallel/sub-0\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-0","Output":" chatty_parallel_test.go:32: this is sub-0\\n"}' +stdout -count=2 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":"=== CONT TestChattyParallel/sub-1\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-1","Output":" chatty_parallel_test.go:32: this is sub-1\\n"}' +stdout -count=2 '{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":"=== CONT TestChattyParallel/sub-2\\n"}\n{"Time":"[0-9TZ:.+-]{20,40}","Action":"output","Package":"command-line-arguments","Test":"TestChattyParallel/sub-2","Output":" chatty_parallel_test.go:32: this is sub-2\\n"}' + +-- chatty_parallel_test.go -- +package chatty_paralell_test + +import ( + "testing" + "fmt" + "flag" +) + +// This test ensures the the order of CONT lines in parallel chatty tests. +func TestChattyParallel(t *testing.T) { + t.Parallel() + + // The number of concurrent tests running. This is closely tied to the + // -parallel test flag, so we grab it from the flag rather than setting it + // to some constant. + parallel := flag.Lookup("test.parallel").Value.(flag.Getter).Get().(int) + + // ready is a synchronization mechanism that causes subtests to execute + // round robin. + ready := make([]chan bool, parallel) + for i := range ready { + ready[i] = make(chan bool, 1) + } + ready[0] <- true + + for i := range ready { + i := i + t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) { + t.Parallel() + for j := 0; j < 2; j++ { + <-ready[i] + t.Logf("this is sub-%d", i) + ready[(i+1)%len(ready)] <- true + } + }) + } +} diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt b/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt new file mode 100644 index 0000000..5952a87 --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt @@ -0,0 +1,39 @@ +# Run parallel chatty tests. Assert on CONT lines. This test makes sure that +# multiple parallel outputs have the appropriate CONT lines between them. +go test -parallel 3 chatty_parallel_test.go -v + +stdout '--- PASS: TestFast \([0-9.]{4}s\)\n=== CONT TestSlow\n chatty_parallel_test.go:31: this is the second TestSlow log\n--- PASS: TestSlow \([0-9.]{4}s\)' + +-- chatty_parallel_test.go -- +package chatty_paralell_test + +import ( + "testing" + "time" +) + +var ( + run = make(chan struct{}) + afterFirstLog = make(chan struct{}) + afterPass = make(chan struct{}) +) + +func TestFast(t *testing.T) { + t.Parallel() + + <-afterFirstLog + t.Cleanup(func() { + close(afterPass) + }) +} + +func TestSlow(t *testing.T) { + t.Parallel() + + t.Logf("this is the first TestSlow log") + close(afterFirstLog) + + <-afterPass + time.Sleep(100 * time.Millisecond) + t.Logf("this is the second TestSlow log") +} diff --git a/src/cmd/go/testdata/script/test_chatty_success.txt b/src/cmd/go/testdata/script/test_chatty_success.txt new file mode 100644 index 0000000..8bfa569 --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_success.txt @@ -0,0 +1,27 @@ +# Run chatty tests. Assert on CONT lines. +go test chatty_test.go -v + +# Non-parallel tests should not print CONT. +! stdout CONT + +# The assertion is condensed into one line so that it precisely matches output, +# rather than skipping lines and allow rogue CONT lines. +stdout '=== RUN TestChatty\n=== RUN TestChatty/sub-0\n chatty_test.go:12: this is sub-0\n chatty_test.go:12: this is sub-0\n=== RUN TestChatty/sub-1\n chatty_test.go:12: this is sub-1\n chatty_test.go:12: this is sub-1\n=== RUN TestChatty/sub-2\n chatty_test.go:12: this is sub-2\n chatty_test.go:12: this is sub-2\n--- PASS: TestChatty' + +-- chatty_test.go -- +package chatty_test + +import ( + "testing" + "fmt" +) + +func TestChatty(t *testing.T) { + for i := 0; i < 3; i++ { + t.Run(fmt.Sprintf("sub-%d", i), func(t *testing.T) { + for j := 0; j < 2; j++ { + t.Logf("this is sub-%d", i) + } + }) + } +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_regexps.txt b/src/cmd/go/testdata/script/test_regexps.txt index 39dedbf..a616195 100644 --- a/src/cmd/go/testdata/script/test_regexps.txt +++ b/src/cmd/go/testdata/script/test_regexps.txt @@ -4,28 +4,28 @@ go test -cpu=1 -run=X/Y -bench=X/Y -count=2 -v testregexp # TestX is run, twice stdout -count=2 '^=== RUN TestX$' -stdout -count=2 '^ TestX: x_test.go:6: LOG: X running$' +stdout -count=2 '^ x_test.go:6: LOG: X running$' # TestX/Y is run, twice stdout -count=2 '^=== RUN TestX/Y$' -stdout -count=2 '^ TestX/Y: x_test.go:8: LOG: Y running$' +stdout -count=2 '^ x_test.go:8: LOG: Y running$' # TestXX is run, twice stdout -count=2 '^=== RUN TestXX$' -stdout -count=2 '^ TestXX: z_test.go:10: LOG: XX running' +stdout -count=2 '^ z_test.go:10: LOG: XX running' # TestZ is not run ! stdout '^=== RUN TestZ$' # BenchmarkX is run with N=1 once, only to discover what sub-benchmarks it has, # and should not print a final summary line. -stdout -count=1 '^\s+BenchmarkX: x_test.go:13: LOG: X running N=1$' +stdout -count=1 '^ x_test.go:13: LOG: X running N=1$' ! stdout '^\s+BenchmarkX: x_test.go:13: LOG: X running N=\d\d+' ! stdout 'BenchmarkX\s+\d+' # Same for BenchmarkXX. -stdout -count=1 '^\s+BenchmarkXX: z_test.go:18: LOG: XX running N=1$' -! stdout '^\s+BenchmarkXX: z_test.go:18: LOG: XX running N=\d\d+' +stdout -count=1 '^ z_test.go:18: LOG: XX running N=1$' +! stdout '^ z_test.go:18: LOG: XX running N=\d\d+' ! stdout 'BenchmarkXX\s+\d+' # BenchmarkX/Y is run in full twice due to -count=2. @@ -33,7 +33,7 @@ stdout -count=1 '^\s+BenchmarkXX: z_test.go:18: LOG: XX running N=1$' # but may cap out at N=1e9. # We don't actually care what the final iteration count is, but it should be # a large number, and the last iteration count prints right before the results. -stdout -count=2 '^\s+BenchmarkX/Y: x_test.go:15: LOG: Y running N=[1-9]\d{4,}\nBenchmarkX/Y\s+\d+' +stdout -count=2 '^ x_test.go:15: LOG: Y running N=[1-9]\d{4,}\nBenchmarkX/Y\s+\d+' -- testregexp/x_test.go -- package x diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index f2fb654..c4f529a 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -470,8 +470,12 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) { offset := (signext24(r.Add&0xffffff) + 2) * 4 var tramp *sym.Symbol for i := 0; ; i++ { - name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i) + oName := r.Sym.Name + name := oName + fmt.Sprintf("%+d-tramp%d", offset, i) tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version)) + if oName == "runtime.deferreturn" { + tramp.Attr.Set(sym.AttrDeferReturnTramp, true) + } if tramp.Type == sym.SDYNIMPORT { // don't reuse trampoline defined in other module continue diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 3e8135c..43e1661 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -276,7 +276,7 @@ func (ctxt *Link) pclntab() { // set the resumption point to PC_B. lastWasmAddr = uint32(r.Add) } - if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" { + if r.Type.IsDirectCall() && r.Sym != nil && (r.Sym.Name == "runtime.deferreturn" || r.Sym.Attr.DeferReturnTramp()) { if ctxt.Arch.Family == sys.Wasm { deferreturn = lastWasmAddr - 1 } else { diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 9fbcff5..e84689d 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -667,7 +667,8 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) { // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as // distinct trampolines. - name := r.Sym.Name + oName := r.Sym.Name + name := oName if r.Add == 0 { name = name + fmt.Sprintf("-tramp%d", i) } else { @@ -677,6 +678,9 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) { // Look up the trampoline in case it already exists tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version)) + if oName == "runtime.deferreturn" { + tramp.Attr.Set(sym.AttrDeferReturnTramp, true) + } if tramp.Value == 0 { break } diff --git a/src/cmd/link/internal/sym/attribute.go b/src/cmd/link/internal/sym/attribute.go index 4b69bf3..773b6a4 100644 --- a/src/cmd/link/internal/sym/attribute.go +++ b/src/cmd/link/internal/sym/attribute.go @@ -81,7 +81,10 @@ const ( // AttrReadOnly indicates whether the symbol's content (Symbol.P) is backed by // read-only memory. AttrReadOnly - // 19 attributes defined so far. + // AttrDeferReturnTramp indicates the symbol is a trampoline of a deferreturn + // call. + AttrDeferReturnTramp + // 20 attributes defined so far. ) func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 } @@ -103,6 +106,7 @@ func (a Attribute) SubSymbol() bool { return a&AttrSubSymbol != 0 } func (a Attribute) Container() bool { return a&AttrContainer != 0 } func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 } func (a Attribute) ReadOnly() bool { return a&AttrReadOnly != 0 } +func (a Attribute) DeferReturnTramp() bool { return a&AttrDeferReturnTramp != 0 } func (a Attribute) CgoExport() bool { return a.CgoExportDynamic() || a.CgoExportStatic() diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index 4f792bd..f5efb51 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -447,3 +447,66 @@ func TestStrictDup(t *testing.T) { t.Errorf("unexpected output:\n%s", out) } } + +const testTrampSrc = ` +package main +import "fmt" +func main() { + fmt.Println("hello") + + defer func(){ + if e := recover(); e == nil { + panic("did not panic") + } + }() + f1() +} + +// Test deferreturn trampolines. See issue #39049. +func f1() { defer f2() } +func f2() { panic("XXX") } +` + +func TestTrampoline(t *testing.T) { + // Test that trampoline insertion works as expected. + // For stress test, we set -debugtramp=2 flag, which sets a very low + // threshold for trampoline generation, and essentially all cross-package + // calls will use trampolines. + switch runtime.GOARCH { + case "arm", "ppc64", "ppc64le": + default: + t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) + } + if runtime.GOOS == "aix" { + t.Skip("trampolines on AIX doesn't work in Go 1.14") // fixed in Go 1.15 + } + + testenv.MustHaveGoBuild(t) + + tmpdir, err := ioutil.TempDir("", "TestTrampoline") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + src := filepath.Join(tmpdir, "hello.go") + err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666) + if err != nil { + t.Fatal(err) + } + exe := filepath.Join(tmpdir, "hello.exe") + + cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("build failed: %v\n%s", err, out) + } + cmd = exec.Command(exe) + out, err = cmd.CombinedOutput() + if err != nil { + t.Errorf("executable failed to run: %v\n%s", err, out) + } + if string(out) != "hello\n" { + t.Errorf("unexpected output:\n%s", out) + } +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go index a81dba9..e6177f2 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -801,6 +801,7 @@ var printVerbs = []printVerb{ {'g', sharpNumFlag, argFloat | argComplex}, {'G', sharpNumFlag, argFloat | argComplex}, {'o', sharpNumFlag, argInt | argPointer}, + {'O', sharpNumFlag, argInt | argPointer}, {'p', "-#", argPointer}, {'q', " -+.0#", argRune | argInt | argString}, {'s', " -+.0", argString}, diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 475bfaa..d36e081 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -44,7 +44,7 @@ golang.org/x/mod/zip ## explicit golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/tools v0.0.0-20200131233409-575de47986ce +# golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0 ## explicit golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/internal/analysisflags diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go index 34d5853..1e0f3ac 100644 --- a/src/crypto/x509/root_windows.go +++ b/src/crypto/x509/root_windows.go @@ -88,6 +88,9 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e switch status { case syscall.CERT_TRUST_IS_NOT_TIME_VALID: return CertificateInvalidError{c, Expired, ""} + case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE: + return CertificateInvalidError{c, IncompatibleUsage, ""} + // TODO(filippo): surface more error statuses. default: return UnknownAuthorityError{c, nil, nil} } @@ -138,11 +141,19 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex return nil } +// windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the +// OIDs for use with the Windows API. +var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs)) + +func init() { + for _, eku := range extKeyUsageOIDs { + windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00") + } +} + // systemVerify is like Verify, except that it uses CryptoAPI calls // to build certificate chains and verify them. func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { - hasDNSName := opts != nil && len(opts.DNSName) > 0 - storeCtx, err := createStoreContext(c, opts) if err != nil { return nil, err @@ -152,17 +163,26 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate para := new(syscall.CertChainPara) para.Size = uint32(unsafe.Sizeof(*para)) - // If there's a DNSName set in opts, assume we're verifying - // a certificate from a TLS server. - if hasDNSName { - oids := []*byte{ - &syscall.OID_PKIX_KP_SERVER_AUTH[0], - // Both IE and Chrome allow certificates with - // Server Gated Crypto as well. Some certificates - // in the wild require them. - &syscall.OID_SERVER_GATED_CRYPTO[0], - &syscall.OID_SGC_NETSCAPE[0], + keyUsages := opts.KeyUsages + if len(keyUsages) == 0 { + keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} + } + oids := make([]*byte, 0, len(keyUsages)) + for _, eku := range keyUsages { + if eku == ExtKeyUsageAny { + oids = nil + break + } + if oid, ok := windowsExtKeyUsageOIDs[eku]; ok { + oids = append(oids, &oid[0]) } + // Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth. + if eku == ExtKeyUsageServerAuth { + oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0]) + oids = append(oids, &syscall.OID_SGC_NETSCAPE[0]) + } + } + if oids != nil { para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR para.RequestedUsage.Usage.Length = uint32(len(oids)) para.RequestedUsage.Usage.UsageIdentifiers = &oids[0] @@ -208,7 +228,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return nil, err } - if hasDNSName { + if opts != nil && len(opts.DNSName) > 0 { err = checkChainSSLServerPolicy(c, chainCtx, opts) if err != nil { return nil, err diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 358fca4..c80b5ac 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -188,23 +188,32 @@ var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificat // VerifyOptions contains parameters for Certificate.Verify. It's a structure // because other PKIX verification APIs have ended up needing many options. type VerifyOptions struct { - DNSName string + // DNSName, if set, is checked against the leaf certificate with + // Certificate.VerifyHostname or the platform verifier. + DNSName string + + // Intermediates is an optional pool of certificates that are not trust + // anchors, but can be used to form a chain from the leaf certificate to a + // root certificate. Intermediates *CertPool - Roots *CertPool // if nil, the system roots are used - CurrentTime time.Time // if zero, the current time is used - // KeyUsage specifies which Extended Key Usage values are acceptable. A leaf - // certificate is accepted if it contains any of the listed values. An empty - // list means ExtKeyUsageServerAuth. To accept any key usage, include - // ExtKeyUsageAny. - // - // Certificate chains are required to nest these extended key usage values. - // (This matches the Windows CryptoAPI behavior, but not the spec.) + // Roots is the set of trusted root certificates the leaf certificate needs + // to chain up to. If nil, the system roots or the platform verifier are used. + Roots *CertPool + + // CurrentTime is used to check the validity of all certificates in the + // chain. If zero, the current time is used. + CurrentTime time.Time + + // KeyUsages specifies which Extended Key Usage values are acceptable. A + // chain is accepted if it allows any of the listed values. An empty list + // means ExtKeyUsageServerAuth. To accept any key usage, include ExtKeyUsageAny. KeyUsages []ExtKeyUsage + // MaxConstraintComparisions is the maximum number of comparisons to // perform when checking a given certificate's name constraints. If // zero, a sensible default is used. This limit prevents pathological // certificates from consuming excessive amounts of CPU time when - // validating. + // validating. It does not apply to the platform verifier. MaxConstraintComparisions int } @@ -717,8 +726,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V // needed. If successful, it returns one or more chains where the first // element of the chain is c and the last element is from opts.Roots. // -// If opts.Roots is nil and system roots are unavailable the returned error -// will be of type SystemRootsError. +// If opts.Roots is nil, the platform verifier might be used, and +// verification details might differ from what is described below. If system +// roots are unavailable the returned error will be of type SystemRootsError. // // Name constraints in the intermediates will be applied to all names claimed // in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim @@ -726,9 +736,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V // the name being validated. Note that DirectoryName constraints are not // supported. // -// Extended Key Usage values are enforced down a chain, so an intermediate or -// root that enumerates EKUs prevents a leaf from asserting an EKU not in that -// list. +// Extended Key Usage values are enforced nested down a chain, so an intermediate +// or root that enumerates EKUs prevents a leaf from asserting an EKU not in that +// list. (While this is not specified, it is common practice in order to limit +// the types of certificates a CA can issue.) // // WARNING: this function doesn't do any revocation checking. func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index 86fe76a..bbb68db 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -21,34 +21,24 @@ import ( ) type verifyTest struct { - leaf string - intermediates []string - roots []string - currentTime int64 - dnsName string - systemSkip bool - keyUsages []ExtKeyUsage - testSystemRootsError bool - sha2 bool - ignoreCN bool - - errorCallback func(*testing.T, int, error) bool + name string + leaf string + intermediates []string + roots []string + currentTime int64 + dnsName string + systemSkip bool + systemLax bool + keyUsages []ExtKeyUsage + ignoreCN bool + + errorCallback func(*testing.T, error) expectedChains [][]string } var verifyTests = []verifyTest{ { - leaf: googleLeaf, - intermediates: []string{giag2Intermediate}, - currentTime: 1395785200, - dnsName: "www.google.com", - testSystemRootsError: true, - - // Without any roots specified we should get a system roots - // error. - errorCallback: expectSystemRootsError, - }, - { + name: "Valid", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -60,6 +50,7 @@ var verifyTests = []verifyTest{ }, }, { + name: "MixedCase", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -71,6 +62,7 @@ var verifyTests = []verifyTest{ }, }, { + name: "HostnameMismatch", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -80,6 +72,7 @@ var verifyTests = []verifyTest{ errorCallback: expectHostnameError("certificate is valid for"), }, { + name: "IPMissing", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -89,6 +82,7 @@ var verifyTests = []verifyTest{ errorCallback: expectHostnameError("doesn't contain any IP SANs"), }, { + name: "Expired", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -98,6 +92,7 @@ var verifyTests = []verifyTest{ errorCallback: expectExpired, }, { + name: "MissingIntermediate", leaf: googleLeaf, roots: []string{geoTrustRoot}, currentTime: 1395785200, @@ -109,6 +104,7 @@ var verifyTests = []verifyTest{ errorCallback: expectAuthorityUnknown, }, { + name: "RootInIntermediates", leaf: googleLeaf, intermediates: []string{geoTrustRoot, giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -119,31 +115,50 @@ var verifyTests = []verifyTest{ {"Google", "Google Internet Authority", "GeoTrust"}, }, // CAPI doesn't build the chain with the duplicated GeoTrust - // entry so the results don't match. Thus we skip this test - // until that's fixed. + // entry so the results don't match. + systemLax: true, + }, + { + name: "dnssec-exp", + leaf: dnssecExpLeaf, + intermediates: []string{startComIntermediate}, + roots: []string{startComRoot}, + currentTime: 1302726541, + + // The StartCom root is not trusted by Windows when the default + // ServerAuth EKU is requested. systemSkip: true, + + expectedChains: [][]string{ + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + }, }, { + name: "dnssec-exp/AnyEKU", leaf: dnssecExpLeaf, intermediates: []string{startComIntermediate}, roots: []string{startComRoot}, currentTime: 1302726541, + keyUsages: []ExtKeyUsage{ExtKeyUsageAny}, expectedChains: [][]string{ {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { + name: "dnssec-exp/RootInIntermediates", leaf: dnssecExpLeaf, intermediates: []string{startComIntermediate, startComRoot}, roots: []string{startComRoot}, currentTime: 1302726541, + systemSkip: true, // see dnssec-exp test expectedChains: [][]string{ {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { + name: "InvalidHash", leaf: googleLeafWithInvalidHash, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -152,50 +167,52 @@ var verifyTests = []verifyTest{ // The specific error message may not occur when using system // verification. - systemSkip: true, + systemLax: true, errorCallback: expectHashError, }, + // EKULeaf tests use an unconstrained chain leading to a leaf certificate + // with an E-mail Protection EKU but not a Server Auth one, checking that + // the EKUs on the leaf are enforced. { - // The default configuration should reject an S/MIME chain. - leaf: smimeLeaf, - roots: []string{smimeIntermediate}, - currentTime: 1339436154, + name: "EKULeaf", + leaf: smimeLeaf, + intermediates: []string{smimeIntermediate}, + roots: []string{smimeRoot}, + currentTime: 1594673418, - // Key usage not implemented for Windows yet. - systemSkip: true, errorCallback: expectUsageError, }, { - leaf: smimeLeaf, - roots: []string{smimeIntermediate}, - currentTime: 1339436154, - keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth}, + name: "EKULeafExplicit", + leaf: smimeLeaf, + intermediates: []string{smimeIntermediate}, + roots: []string{smimeRoot}, + currentTime: 1594673418, + keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth}, - // Key usage not implemented for Windows yet. - systemSkip: true, errorCallback: expectUsageError, }, { - leaf: smimeLeaf, - roots: []string{smimeIntermediate}, - currentTime: 1339436154, - keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection}, + name: "EKULeafValid", + leaf: smimeLeaf, + intermediates: []string{smimeIntermediate}, + roots: []string{smimeRoot}, + currentTime: 1594673418, + keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection}, - // Key usage not implemented for Windows yet. - systemSkip: true, expectedChains: [][]string{ - {"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"}, + {"CORPORATIVO FICTICIO ACTIVO", "EAEko Herri Administrazioen CA - CA AAPP Vascas (2)", "IZENPE S.A."}, }, }, { + name: "SGCIntermediate", leaf: megaLeaf, intermediates: []string{comodoIntermediate1}, roots: []string{comodoRoot}, currentTime: 1360431182, - // CryptoAPI can find alternative validation paths so we don't - // perform this test with system validation. - systemSkip: true, + // CryptoAPI can find alternative validation paths. + systemLax: true, expectedChains: [][]string{ {"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"}, }, @@ -203,6 +220,7 @@ var verifyTests = []verifyTest{ { // Check that a name constrained intermediate works even when // it lists multiple constraints. + name: "MultipleConstraints", leaf: nameConstraintsLeaf, intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2}, roots: []string{globalSignRoot}, @@ -221,17 +239,16 @@ var verifyTests = []verifyTest{ { // Check that SHA-384 intermediates (which are popping up) // work. + name: "SHA-384", leaf: moipLeafCert, intermediates: []string{comodoIntermediateSHA384, comodoRSAAuthority}, roots: []string{addTrustRoot}, currentTime: 1397502195, dnsName: "api.moip.com.br", - // CryptoAPI can find alternative validation paths so we don't - // perform this test with system validation. - systemSkip: true, + // CryptoAPI can find alternative validation paths. + systemLax: true, - sha2: true, expectedChains: [][]string{ { "api.moip.com.br", @@ -244,11 +261,12 @@ var verifyTests = []verifyTest{ { // Putting a certificate as a root directly should work as a // way of saying “exactly this”. + name: "LeafInRoots", leaf: selfSigned, roots: []string{selfSigned}, currentTime: 1471624472, dnsName: "foo.example", - systemSkip: true, + systemSkip: true, // does not chain to a system root expectedChains: [][]string{ {"Acme Co"}, @@ -257,11 +275,12 @@ var verifyTests = []verifyTest{ { // Putting a certificate as a root directly should not skip // other checks however. + name: "LeafInRootsInvalid", leaf: selfSigned, roots: []string{selfSigned}, currentTime: 1471624472, dnsName: "notfoo.example", - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectHostnameError("certificate is valid for"), }, @@ -269,87 +288,95 @@ var verifyTests = []verifyTest{ // The issuer name in the leaf doesn't exactly match the // subject name in the root. Go does not perform // canonicalization and so should reject this. See issue 14955. + name: "IssuerSubjectMismatch", leaf: issuerSubjectMatchLeaf, roots: []string{issuerSubjectMatchRoot}, currentTime: 1475787715, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectSubjectIssuerMismatcthError, }, { // An X.509 v1 certificate should not be accepted as an // intermediate. + name: "X509v1Intermediate", leaf: x509v1TestLeaf, intermediates: []string{x509v1TestIntermediate}, roots: []string{x509v1TestRoot}, currentTime: 1481753183, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectNotAuthorizedError, }, { // If any SAN extension is present (even one without any DNS // names), the CN should be ignored. + name: "IgnoreCNWithSANs", leaf: ignoreCNWithSANLeaf, dnsName: "foo.example.com", roots: []string{ignoreCNWithSANRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectHostnameError("certificate is not valid for any names"), }, { // Test that excluded names are respected. + name: "ExcludedNames", leaf: excludedNamesLeaf, dnsName: "bender.local", intermediates: []string{excludedNamesIntermediate}, roots: []string{excludedNamesRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectNameConstraintsError, }, { // Test that unknown critical extensions in a leaf cause a // verify error. + name: "CriticalExtLeaf", leaf: criticalExtLeafWithExt, dnsName: "example.com", intermediates: []string{criticalExtIntermediate}, roots: []string{criticalExtRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectUnhandledCriticalExtension, }, { // Test that unknown critical extensions in an intermediate // cause a verify error. + name: "CriticalExtIntermediate", leaf: criticalExtLeaf, dnsName: "example.com", intermediates: []string{criticalExtIntermediateWithExt}, roots: []string{criticalExtRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectUnhandledCriticalExtension, }, { // Test that invalid CN are ignored. + name: "InvalidCN", leaf: invalidCNWithoutSAN, dnsName: "foo,invalid", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectHostnameError("Common Name is not a valid hostname"), }, { // Test that valid CN are respected. + name: "ValidCN", leaf: validCNWithoutSAN, dnsName: "foo.example.com", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root expectedChains: [][]string{ {"foo.example.com", "Test root"}, @@ -357,31 +384,34 @@ var verifyTests = []verifyTest{ }, // Replicate CN tests with ignoreCN = true { + name: "IgnoreCNWithSANs/ignoreCN", leaf: ignoreCNWithSANLeaf, dnsName: "foo.example.com", roots: []string{ignoreCNWithSANRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root ignoreCN: true, errorCallback: expectHostnameError("certificate is not valid for any names"), }, { + name: "InvalidCN/ignoreCN", leaf: invalidCNWithoutSAN, dnsName: "foo,invalid", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root ignoreCN: true, errorCallback: expectHostnameError("Common Name is not a valid hostname"), }, { + name: "ValidCN/ignoreCN", leaf: validCNWithoutSAN, dnsName: "foo.example.com", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root ignoreCN: true, errorCallback: expectHostnameError("not valid for any names"), @@ -389,11 +419,12 @@ var verifyTests = []verifyTest{ { // A certificate with an AKID should still chain to a parent without SKID. // See Issue 30079. + name: "AKIDNoSKID", leaf: leafWithAKID, roots: []string{rootWithoutSKID}, currentTime: 1550000000, dnsName: "example", - systemSkip: true, + systemSkip: true, // does not chain to a system root expectedChains: [][]string{ {"Acme LLC", "Acme Co"}, @@ -401,98 +432,70 @@ var verifyTests = []verifyTest{ }, } -func expectHostnameError(msg string) func(*testing.T, int, error) bool { - return func(t *testing.T, i int, err error) (ok bool) { +func expectHostnameError(msg string) func(*testing.T, error) { + return func(t *testing.T, err error) { if _, ok := err.(HostnameError); !ok { - t.Errorf("#%d: error was not a HostnameError: %v", i, err) - return false + t.Fatalf("error was not a HostnameError: %v", err) } if !strings.Contains(err.Error(), msg) { - t.Errorf("#%d: HostnameError did not contain %q: %v", i, msg, err) + t.Fatalf("HostnameError did not contain %q: %v", msg, err) } - return true } } -func expectExpired(t *testing.T, i int, err error) (ok bool) { +func expectExpired(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired { - t.Errorf("#%d: error was not Expired: %v", i, err) - return false + t.Fatalf("error was not Expired: %v", err) } - return true } -func expectUsageError(t *testing.T, i int, err error) (ok bool) { +func expectUsageError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage { - t.Errorf("#%d: error was not IncompatibleUsage: %v", i, err) - return false + t.Fatalf("error was not IncompatibleUsage: %v", err) } - return true } -func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { +func expectAuthorityUnknown(t *testing.T, err error) { e, ok := err.(UnknownAuthorityError) if !ok { - t.Errorf("#%d: error was not UnknownAuthorityError: %v", i, err) - return false + t.Fatalf("error was not UnknownAuthorityError: %v", err) } if e.Cert == nil { - t.Errorf("#%d: error was UnknownAuthorityError, but missing Cert: %v", i, err) - return false + t.Fatalf("error was UnknownAuthorityError, but missing Cert: %v", err) } - return true } -func expectSystemRootsError(t *testing.T, i int, err error) bool { - if _, ok := err.(SystemRootsError); !ok { - t.Errorf("#%d: error was not SystemRootsError: %v", i, err) - return false - } - return true -} - -func expectHashError(t *testing.T, i int, err error) bool { +func expectHashError(t *testing.T, err error) { if err == nil { - t.Errorf("#%d: no error resulted from invalid hash", i) - return false + t.Fatalf("no error resulted from invalid hash") } if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) { - t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %v", i, expected, err) - return false + t.Fatalf("error resulting from invalid hash didn't contain '%s', rather it was: %v", expected, err) } - return true } -func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool) { +func expectSubjectIssuerMismatcthError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NameMismatch { - t.Errorf("#%d: error was not a NameMismatch: %v", i, err) - return false + t.Fatalf("error was not a NameMismatch: %v", err) } - return true } -func expectNameConstraintsError(t *testing.T, i int, err error) (ok bool) { +func expectNameConstraintsError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != CANotAuthorizedForThisName { - t.Errorf("#%d: error was not a CANotAuthorizedForThisName: %v", i, err) - return false + t.Fatalf("error was not a CANotAuthorizedForThisName: %v", err) } - return true } -func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) { +func expectNotAuthorizedError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign { - t.Errorf("#%d: error was not a NotAuthorizedToSign: %v", i, err) - return false + t.Fatalf("error was not a NotAuthorizedToSign: %v", err) } - return true } -func expectUnhandledCriticalExtension(t *testing.T, i int, err error) (ok bool) { +func expectUnhandledCriticalExtension(t *testing.T, err error) { if _, ok := err.(UnhandledCriticalExtension); !ok { - t.Errorf("#%d: error was not an UnhandledCriticalExtension: %v", i, err) - return false + t.Fatalf("error was not an UnhandledCriticalExtension: %v", err) } - return true } func certificateFromPEM(pemBytes string) (*Certificate, error) { @@ -503,107 +506,91 @@ func certificateFromPEM(pemBytes string) (*Certificate, error) { return ParseCertificate(block.Bytes) } -func testVerify(t *testing.T, useSystemRoots bool) { - defer func(savedIgnoreCN bool) { - ignoreCN = savedIgnoreCN - }(ignoreCN) - for i, test := range verifyTests { - if useSystemRoots && test.systemSkip { - continue - } - if runtime.GOOS == "windows" && test.testSystemRootsError { - continue - } - - ignoreCN = test.ignoreCN - opts := VerifyOptions{ - Intermediates: NewCertPool(), - DNSName: test.dnsName, - CurrentTime: time.Unix(test.currentTime, 0), - KeyUsages: test.keyUsages, - } +func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) { + defer func(savedIgnoreCN bool) { ignoreCN = savedIgnoreCN }(ignoreCN) - if !useSystemRoots { - opts.Roots = NewCertPool() - for j, root := range test.roots { - ok := opts.Roots.AppendCertsFromPEM([]byte(root)) - if !ok { - t.Errorf("#%d: failed to parse root #%d", i, j) - return - } - } - } + ignoreCN = test.ignoreCN + opts := VerifyOptions{ + Intermediates: NewCertPool(), + DNSName: test.dnsName, + CurrentTime: time.Unix(test.currentTime, 0), + KeyUsages: test.keyUsages, + } - for j, intermediate := range test.intermediates { - ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate)) + if !useSystemRoots { + opts.Roots = NewCertPool() + for j, root := range test.roots { + ok := opts.Roots.AppendCertsFromPEM([]byte(root)) if !ok { - t.Errorf("#%d: failed to parse intermediate #%d", i, j) - return + t.Fatalf("failed to parse root #%d", j) } } + } - leaf, err := certificateFromPEM(test.leaf) - if err != nil { - t.Errorf("#%d: failed to parse leaf: %v", i, err) - return - } - - var oldSystemRoots *CertPool - if test.testSystemRootsError { - oldSystemRoots = systemRootsPool() - systemRoots = nil - opts.Roots = nil + for j, intermediate := range test.intermediates { + ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate)) + if !ok { + t.Fatalf("failed to parse intermediate #%d", j) } + } - chains, err := leaf.Verify(opts) + leaf, err := certificateFromPEM(test.leaf) + if err != nil { + t.Fatalf("failed to parse leaf: %v", err) + } - if test.testSystemRootsError { - systemRoots = oldSystemRoots - } + chains, err := leaf.Verify(opts) - if test.errorCallback == nil && err != nil { - t.Errorf("#%d: unexpected error: %v", i, err) - } - if test.errorCallback != nil { - if !test.errorCallback(t, i, err) { - return + if test.errorCallback == nil && err != nil { + t.Fatalf("unexpected error: %v", err) + } + if test.errorCallback != nil { + if useSystemRoots && test.systemLax { + if err == nil { + t.Fatalf("expected error") } + } else { + test.errorCallback(t, err) } + } - if len(chains) != len(test.expectedChains) { - t.Errorf("#%d: wanted %d chains, got %d", i, len(test.expectedChains), len(chains)) - } + if len(chains) != len(test.expectedChains) { + t.Errorf("wanted %d chains, got %d", len(test.expectedChains), len(chains)) + } - // We check that each returned chain matches a chain from - // expectedChains but an entry in expectedChains can't match - // two chains. - seenChains := make([]bool, len(chains)) - NextOutputChain: - for _, chain := range chains { - TryNextExpected: - for j, expectedChain := range test.expectedChains { - if seenChains[j] { - continue - } - if len(chain) != len(expectedChain) { - continue - } - for k, cert := range chain { - if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) { - continue TryNextExpected - } + // We check that each returned chain matches a chain from + // expectedChains but an entry in expectedChains can't match + // two chains. + seenChains := make([]bool, len(chains)) +NextOutputChain: + for _, chain := range chains { + TryNextExpected: + for j, expectedChain := range test.expectedChains { + if seenChains[j] { + continue + } + if len(chain) != len(expectedChain) { + continue + } + for k, cert := range chain { + if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) { + continue TryNextExpected } - // we matched - seenChains[j] = true - continue NextOutputChain } - t.Errorf("#%d: No expected chain matched %s", i, chainToDebugString(chain)) + // we matched + seenChains[j] = true + continue NextOutputChain } + t.Errorf("no expected chain matched %s", chainToDebugString(chain)) } } func TestGoVerify(t *testing.T) { - testVerify(t, false) + for _, test := range verifyTests { + t.Run(test.name, func(t *testing.T) { + testVerify(t, test, false) + }) + } } func TestSystemVerify(t *testing.T) { @@ -611,7 +598,14 @@ func TestSystemVerify(t *testing.T) { t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS) } - testVerify(t, true) + for _, test := range verifyTests { + t.Run(test.name, func(t *testing.T) { + if test.systemSkip { + t.SkipNow() + } + testVerify(t, test, true) + }) + } } func chainToDebugString(chain []*Certificate) string { @@ -648,8 +642,7 @@ tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV 5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` const giag2Intermediate = `-----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT @@ -674,8 +667,7 @@ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` const googleLeaf = `-----BEGIN CERTIFICATE----- MIIEdjCCA16gAwIBAgIIcR5k4dkoe04wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE @@ -702,8 +694,7 @@ tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+ orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi 8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` // googleLeafWithInvalidHash is the same as googleLeaf, but the signature // algorithm in the certificate contains a nonsense OID. @@ -732,8 +723,7 @@ tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+ orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi 8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` const dnssecExpLeaf = `-----BEGIN CERTIFICATE----- MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ @@ -858,58 +848,127 @@ NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= -----END CERTIFICATE-----` const smimeLeaf = `-----BEGIN CERTIFICATE----- -MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA -MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD -VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz -MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT -DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds -b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B -CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j -NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR -BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV -+5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf -9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7 -NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV -HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH -AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud -EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV -HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 -cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww -VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh -bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE -FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU -bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd -BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy -q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N -C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl -a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub -d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs +MIIIPDCCBiSgAwIBAgIQaMDxFS0pOMxZZeOBxoTJtjANBgkqhkiG9w0BAQsFADCB +nTELMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMTowOAYDVQQLDDFB +WlogWml1cnRhZ2lyaSBwdWJsaWtvYSAtIENlcnRpZmljYWRvIHB1YmxpY28gU0NB +MTwwOgYDVQQDDDNFQUVrbyBIZXJyaSBBZG1pbmlzdHJhemlvZW4gQ0EgLSBDQSBB +QVBQIFZhc2NhcyAoMikwHhcNMTcwNzEyMDg1MzIxWhcNMjEwNzEyMDg1MzIxWjCC +AQwxDzANBgNVBAoMBklaRU5QRTE4MDYGA1UECwwvWml1cnRhZ2lyaSBrb3Jwb3Jh +dGlib2EtQ2VydGlmaWNhZG8gY29ycG9yYXRpdm8xQzBBBgNVBAsMOkNvbmRpY2lv +bmVzIGRlIHVzbyBlbiB3d3cuaXplbnBlLmNvbSBub2xhIGVyYWJpbGkgamFraXRl +a28xFzAVBgNVBC4TDi1kbmkgOTk5OTk5ODlaMSQwIgYDVQQDDBtDT1JQT1JBVElW +TyBGSUNUSUNJTyBBQ1RJVk8xFDASBgNVBCoMC0NPUlBPUkFUSVZPMREwDwYDVQQE +DAhGSUNUSUNJTzESMBAGA1UEBRMJOTk5OTk5ODlaMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAwVOMwUDfBtsH0XuxYnb+v/L774jMH8valX7RPH8cl2Lb +SiqSo0RchW2RGA2d1yuYHlpChC9jGmt0X/g66/E/+q2hUJlfJtqVDJFwtFYV4u2S +yzA3J36V4PRkPQrKxAsbzZriFXAF10XgiHQz9aVeMMJ9GBhmh9+DK8Tm4cMF6i8l ++AuC35KdngPF1x0ealTYrYZplpEJFO7CiW42aLi6vQkDR2R7nmZA4AT69teqBWsK +0DZ93/f0G/3+vnWwNTBF0lB6dIXoaz8OMSyHLqGnmmAtMrzbjAr/O/WWgbB/BqhR +qjJQ7Ui16cuDldXaWQ/rkMzsxmsAox0UF+zdQNvXUQIDAQABo4IDBDCCAwAwgccG +A1UdEgSBvzCBvIYVaHR0cDovL3d3dy5pemVucGUuY29tgQ9pbmZvQGl6ZW5wZS5j +b22kgZEwgY4xRzBFBgNVBAoMPklaRU5QRSBTLkEuIC0gQ0lGIEEwMTMzNzI2MC1S +TWVyYy5WaXRvcmlhLUdhc3RlaXogVDEwNTUgRjYyIFM4MUMwQQYDVQQJDDpBdmRh +IGRlbCBNZWRpdGVycmFuZW8gRXRvcmJpZGVhIDE0IC0gMDEwMTAgVml0b3JpYS1H +YXN0ZWl6MB4GA1UdEQQXMBWBE2ZpY3RpY2lvQGl6ZW5wZS5ldXMwDgYDVR0PAQH/ +BAQDAgXgMCkGA1UdJQQiMCAGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQC +AjAdBgNVHQ4EFgQUyeoOD4cgcljKY0JvrNuX2waFQLAwHwYDVR0jBBgwFoAUwKlK +90clh/+8taaJzoLSRqiJ66MwggEnBgNVHSAEggEeMIIBGjCCARYGCisGAQQB8zkB +AQEwggEGMDMGCCsGAQUFBwIBFidodHRwOi8vd3d3Lml6ZW5wZS5jb20vcnBhc2Nh +Y29ycG9yYXRpdm8wgc4GCCsGAQUFBwICMIHBGoG+Wml1cnRhZ2lyaWEgRXVza2Fs +IEF1dG9ub21pYSBFcmtpZGVnb2tvIHNla3RvcmUgcHVibGlrb2tvIGVyYWt1bmRl +ZW4gYmFybmUtc2FyZWV0YW4gYmFrYXJyaWsgZXJhYmlsIGRhaXRla2UuIFVzbyBy +ZXN0cmluZ2lkbyBhbCBhbWJpdG8gZGUgcmVkZXMgaW50ZXJuYXMgZGUgRW50aWRh +ZGVzIGRlbCBTZWN0b3IgUHVibGljbyBWYXNjbzAyBggrBgEFBQcBAQQmMCQwIgYI +KwYBBQUHMAGGFmh0dHA6Ly9vY3NwLml6ZW5wZS5jb20wOgYDVR0fBDMwMTAvoC2g +K4YpaHR0cDovL2NybC5pemVucGUuY29tL2NnaS1iaW4vY3JsaW50ZXJuYTIwDQYJ +KoZIhvcNAQELBQADggIBAIy5PQ+UZlCRq6ig43vpHwlwuD9daAYeejV0Q+ZbgWAE +GtO0kT/ytw95ZEJMNiMw3fYfPRlh27ThqiT0VDXZJDlzmn7JZd6QFcdXkCsiuv4+ +ZoXAg/QwnA3SGUUO9aVaXyuOIIuvOfb9MzoGp9xk23SMV3eiLAaLMLqwB5DTfBdt +BGI7L1MnGJBv8RfP/TL67aJ5bgq2ri4S8vGHtXSjcZ0+rCEOLJtmDNMnTZxancg3 +/H5edeNd+n6Z48LO+JHRxQufbC4mVNxVLMIP9EkGUejlq4E4w6zb5NwCQczJbSWL +i31rk2orsNsDlyaLGsWZp3JSNX6RmodU4KAUPor4jUJuUhrrm3Spb73gKlV/gcIw +bCE7mML1Kss3x1ySaXsis6SZtLpGWKkW2iguPWPs0ydV6RPhmsCxieMwPPIJ87vS +5IejfgyBae7RSuAIHyNFy4uI5xwvwUFf6OZ7az8qtW7ImFOgng3Ds+W9k1S2CNTx +d0cnKTfA6IpjGo8EeHcxnIXT8NPImWaRj0qqonvYady7ci6U4m3lkNSdXNn1afgw +mYust+gxVtOZs1gk2MUCgJ1V1X+g7r/Cg7viIn6TLkLrpS1kS1hvMqkl9M+7XqPo +Qd95nJKOkusQpy99X4dF/lfbYAQnnjnqh3DLD2gvYObXFaAYFaiBKTiMTV2X72F+ -----END CERTIFICATE-----` const smimeIntermediate = `-----BEGIN CERTIFICATE----- -MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw -MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy -IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4 -En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH -3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww -gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q -yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI -YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY -T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI -MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA -MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu -LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds -b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN -NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8 -eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX -eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX -YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0 -7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2 -2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH +MIIHNzCCBSGgAwIBAgIQJMXIqlZvjuhMvqcFXOFkpDALBgkqhkiG9w0BAQswODEL +MAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMRMwEQYDVQQDDApJemVu +cGUuY29tMB4XDTEwMTAyMDA4MjMzM1oXDTM3MTIxMjIzMDAwMFowgZ0xCzAJBgNV +BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjE6MDgGA1UECwwxQVpaIFppdXJ0 +YWdpcmkgcHVibGlrb2EgLSBDZXJ0aWZpY2FkbyBwdWJsaWNvIFNDQTE8MDoGA1UE +AwwzRUFFa28gSGVycmkgQWRtaW5pc3RyYXppb2VuIENBIC0gQ0EgQUFQUCBWYXNj +YXMgKDIpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoIM7nEdI0N1h +rR5T4xuV/usKDoMIasaiKvfLhbwxaNtTt+a7W/6wV5bv3svQFIy3sUXjjdzV1nG2 +To2wo/YSPQiOt8exWvOapvL21ogiof+kelWnXFjWaKJI/vThHYLgIYEMj/y4HdtU +ojI646rZwqsb4YGAopwgmkDfUh5jOhV2IcYE3TgJAYWVkj6jku9PLaIsHiarAHjD +PY8dig8a4SRv0gm5Yk7FXLmW1d14oxQBDeHZ7zOEXfpafxdEDO2SNaRJjpkh8XRr +PGqkg2y1Q3gT6b4537jz+StyDIJ3omylmlJsGCwqT7p8mEqjGJ5kC5I2VnjXKuNn +soShc72khWZVUJiJo5SGuAkNE2ZXqltBVm5Jv6QweQKsX6bkcMc4IZok4a+hx8FM +8IBpGf/I94pU6HzGXqCyc1d46drJgDY9mXa+6YDAJFl3xeXOOW2iGCfwXqhiCrKL +MYvyMZzqF3QH5q4nb3ZnehYvraeMFXJXDn+Utqp8vd2r7ShfQJz01KtM4hgKdgSg +jtW+shkVVN5ng/fPN85ovfAH2BHXFfHmQn4zKsYnLitpwYM/7S1HxlT61cdQ7Nnk +3LZTYEgAoOmEmdheklT40WAYakksXGM5VrzG7x9S7s1Tm+Vb5LSThdHC8bxxwyTb +KsDRDNJ84N9fPDO6qHnzaL2upQ43PycCAwEAAaOCAdkwggHVMIHHBgNVHREEgb8w +gbyGFWh0dHA6Ly93d3cuaXplbnBlLmNvbYEPaW5mb0BpemVucGUuY29tpIGRMIGO +MUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBBMDEzMzcyNjAtUk1lcmMuVml0 +b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEGA1UECQw6QXZkYSBkZWwgTWVk +aXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEwIFZpdG9yaWEtR2FzdGVpejAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwKlK90cl +h/+8taaJzoLSRqiJ66MwHwYDVR0jBBgwFoAUHRxlDqjyJXu0kc/ksbHmvVV0bAUw +OgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly93d3cuaXpl +bnBlLmNvbS9jcHMwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8v +b2NzcC5pemVucGUuY29tOjgwOTQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny +bC5pemVucGUuY29tL2NnaS1iaW4vYXJsMjALBgkqhkiG9w0BAQsDggIBAMbjc3HM +3DG9ubWPkzsF0QsktukpujbTTcGk4h20G7SPRy1DiiTxrRzdAMWGjZioOP3/fKCS +M539qH0M+gsySNie+iKlbSZJUyE635T1tKw+G7bDUapjlH1xyv55NC5I6wCXGC6E +3TEP5B/E7dZD0s9E4lS511ubVZivFgOzMYo1DO96diny/N/V1enaTCpRl1qH1OyL +xUYTijV4ph2gL6exwuG7pxfRcVNHYlrRaXWfTz3F6NBKyULxrI3P/y6JAtN1GqT4 +VF/+vMygx22n0DufGepBwTQz6/rr1ulSZ+eMnuJiTXgh/BzQnkUsXTb8mHII25iR +0oYF2qAsk6ecWbLiDpkHKIDHmML21MZE13MS8NSvTHoqJO4LyAmDe6SaeNHtrPlK +b6mzE1BN2ug+ZaX8wLA5IMPFaf0jKhb/Cxu8INsxjt00brsErCc9ip1VNaH0M4bi +1tGxfiew2436FaeyUxW7Pl6G5GgkNbuUc7QIoRy06DdU/U38BxW3uyJMY60zwHvS +FlKAn0OvYp4niKhAJwaBVN3kowmJuOU5Rid+TUnfyxbJ9cttSgzaF3hP/N4zgMEM +5tikXUskeckt8LUK96EH0QyssavAMECUEb/xrupyRdYWwjQGvNLq6T5+fViDGyOw +k+lzD44wofy8paAy9uC9Owae0zMEzhcsyRm7 +-----END CERTIFICATE-----` + +const smimeRoot = `-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE-----` var megaLeaf = `-----BEGIN CERTIFICATE----- @@ -1315,50 +1374,7 @@ vRAvOtNiKtPzFeQVdbRPOskC4rcHyPeiDAMAMixeLi63+CFty4da3r5lRezeedCE cw3ESZzThBwWqvPOtJdpXdm+r57pDW8qD+/0lY8wfImMNkQAyCUCLg/1Lxt/hrBj -----END CERTIFICATE-----` -const issuerSubjectMatchRoot = ` -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 161640039802297062 (0x23e42c281e55ae6) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=Golang, CN=Root ca - Validity - Not Before: Jan 1 00:00:00 2015 GMT - Not After : Jan 1 00:00:00 2025 GMT - Subject: O=Golang, CN=Root ca - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (1024 bit) - Modulus: - 00:e9:0e:7f:11:0c:e6:5a:e6:86:83:70:f6:51:07: - 2e:02:78:11:f5:b2:24:92:38:ee:26:62:02:c7:94: - f1:3e:a1:77:6a:c0:8f:d5:22:68:b6:5d:e2:4c:da: - e0:85:11:35:c2:92:72:49:8d:81:b4:88:97:6b:b7: - fc:b2:44:5b:d9:4d:06:70:f9:0c:c6:8f:e9:b3:df: - a3:6a:84:6c:43:59:be:9d:b2:d0:76:9b:c3:d7:fa: - 99:59:c3:b8:e5:f3:53:03:bd:49:d6:b3:cc:a2:43: - fe:ad:c2:0b:b9:01:b8:56:29:94:03:24:a7:0d:28: - 21:29:a9:ae:94:5b:4a:f9:9f - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - 40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01 - Signature Algorithm: sha256WithRSAEncryption - 6f:84:df:49:e0:99:d4:71:66:1d:32:86:56:cb:ea:5a:6b:0e: - 00:6a:d1:5a:6e:1f:06:23:07:ff:cb:d1:1a:74:e4:24:43:0b: - aa:2a:a0:73:75:25:82:bc:bf:3f:a9:f8:48:88:ac:ed:3a:94: - 3b:0d:d3:88:c8:67:44:61:33:df:71:6c:c5:af:ed:16:8c:bf: - 82:f9:49:bb:e3:2a:07:53:36:37:25:77:de:91:a4:77:09:7f: - 6f:b2:91:58:c4:05:89:ea:8e:fa:e1:3b:19:ef:f8:f6:94:b7: - 7b:27:e6:e4:84:dd:2b:f5:93:f5:3c:d8:86:c5:38:01:56:5c: - 9f:6d ------BEGIN CERTIFICATE----- +const issuerSubjectMatchRoot = `-----BEGIN CERTIFICATE----- MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNhMB4XDTE1MDEwMTAwMDAwMFoXDTI1 MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNh @@ -1373,53 +1389,7 @@ RGEz33Fsxa/tFoy/gvlJu+MqB1M2NyV33pGkdwl/b7KRWMQFieqO+uE7Ge/49pS3 eyfm5ITdK/WT9TzYhsU4AVZcn20= -----END CERTIFICATE-----` -const issuerSubjectMatchLeaf = ` -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 16785088708916013734 (0xe8f09d3fe25beaa6) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=Golang, CN=Root CA - Validity - Not Before: Jan 1 00:00:00 2015 GMT - Not After : Jan 1 00:00:00 2025 GMT - Subject: O=Golang, CN=Leaf - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (1024 bit) - Modulus: - 00:db:46:7d:93:2e:12:27:06:48:bc:06:28:21:ab: - 7e:c4:b6:a2:5d:fe:1e:52:45:88:7a:36:47:a5:08: - 0d:92:42:5b:c2:81:c0:be:97:79:98:40:fb:4f:6d: - 14:fd:2b:13:8b:c2:a5:2e:67:d8:d4:09:9e:d6:22: - 38:b7:4a:0b:74:73:2b:c2:34:f1:d1:93:e5:96:d9: - 74:7b:f3:58:9f:6c:61:3c:c0:b0:41:d4:d9:2b:2b: - 24:23:77:5b:1c:3b:bd:75:5d:ce:20:54:cf:a1:63: - 87:1d:1e:24:c4:f3:1d:1a:50:8b:aa:b6:14:43:ed: - 97:a7:75:62:f4:14:c8:52:d7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Key Encipherment - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:FALSE - X509v3 Subject Key Identifier: - 9F:91:16:1F:43:43:3E:49:A6:DE:6D:B6:80:D7:9F:60 - X509v3 Authority Key Identifier: - keyid:40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01 - - Signature Algorithm: sha256WithRSAEncryption - 8d:86:05:da:89:f5:1d:c5:16:14:41:b9:34:87:2b:5c:38:99: - e3:d9:5a:5b:7a:5b:de:0b:5c:08:45:09:6f:1c:9d:31:5f:08: - ca:7a:a3:99:da:83:0b:22:be:4f:02:35:91:4e:5d:5c:37:bf: - 89:22:58:7d:30:76:d2:2f:d0:a0:ee:77:9e:77:c0:d6:19:eb: - ec:a0:63:35:6a:80:9b:80:1a:80:de:64:bc:40:38:3c:22:69: - ad:46:26:a2:3d:ea:f4:c2:92:49:16:03:96:ae:64:21:b9:7c: - ee:64:91:47:81:aa:b4:0c:09:2b:12:1a:b2:f3:af:50:b3:b1: - ce:24 ------BEGIN CERTIFICATE----- +const issuerSubjectMatchLeaf = `-----BEGIN CERTIFICATE----- MIICODCCAaGgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCMxDzANBgNV BAoTBkdvbGFuZzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xNTAxMDEwMDAwMDBaFw0y NTAxMDEwMDAwMDBaMCAxDzANBgNVBAoTBkdvbGFuZzENMAsGA1UEAxMETGVhZjCB @@ -1432,11 +1402,9 @@ Q0M+SabebbaA159gMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEwDQYJKoZI hvcNAQELBQADgYEAjYYF2on1HcUWFEG5NIcrXDiZ49laW3pb3gtcCEUJbxydMV8I ynqjmdqDCyK+TwI1kU5dXDe/iSJYfTB20i/QoO53nnfA1hnr7KBjNWqAm4AagN5k vEA4PCJprUYmoj3q9MKSSRYDlq5kIbl87mSRR4GqtAwJKxIasvOvULOxziQ= ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` -const x509v1TestRoot = ` ------BEGIN CERTIFICATE----- +const x509v1TestRoot = `-----BEGIN CERTIFICATE----- MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAwMDAwMFoXDTI1 MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENB @@ -1451,8 +1419,7 @@ h2NtN34ard0hEfHc8qW8mkXdsysVmq6cPvFYaHz+dBtkHuHDoy8YQnC0zdN/WyYB /1JmacUUofl+HusHuLkDxmadogI= -----END CERTIFICATE-----` -const x509v1TestIntermediate = ` ------BEGIN CERTIFICATE----- +const x509v1TestIntermediate = `-----BEGIN CERTIFICATE----- MIIByjCCATMCCQCCdEMsT8ykqTANBgkqhkiG9w0BAQsFADAjMQ8wDQYDVQQKEwZH b2xhbmcxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAwMDAwWhcNMjUwMTAx MDAwMDAwWjAwMQ8wDQYDVQQKEwZHb2xhbmcxHTAbBgNVBAMTFFguNTA5djEgaW50 @@ -1465,8 +1432,7 @@ zWE77kJDibzd141u21ZbLsKvEdUJXjla43bdyMmEqf5VGpC3D4sFt3QVH7lGeRur x5Wlq1u3YDL/j6s1nU2dQ3ySB/oP7J+vQ9V4QeM+ -----END CERTIFICATE-----` -const x509v1TestLeaf = ` ------BEGIN CERTIFICATE----- +const x509v1TestLeaf = `-----BEGIN CERTIFICATE----- MIICMzCCAZygAwIBAgIJAPo99mqJJrpJMA0GCSqGSIb3DQEBCwUAMDAxDzANBgNV BAoTBkdvbGFuZzEdMBsGA1UEAxMUWC41MDl2MSBpbnRlcm1lZGlhdGUwHhcNMTUw MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjArMQ8wDQYDVQQKEwZHb2xhbmcxGDAW @@ -1481,8 +1447,7 @@ CwUAA4GBADYzYUvaToO/ucBskPdqXV16AaakIhhSENswYVSl97/sODaxsjishKq9 /jt8qszOXCv2vYdUTPNuPqufXLWMoirpuXrr1liJDmedCcAHepY/ -----END CERTIFICATE-----` -const ignoreCNWithSANRoot = ` ------BEGIN CERTIFICATE----- +const ignoreCNWithSANRoot = `-----BEGIN CERTIFICATE----- MIIDPzCCAiegAwIBAgIIJkzCwkNrPHMwDQYJKoZIhvcNAQELBQAwMDEQMA4GA1UE ChMHVEVTVElORzEcMBoGA1UEAxMTKipUZXN0aW5nKiogUm9vdCBDQTAeFw0xNTAx MDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMDAxEDAOBgNVBAoTB1RFU1RJTkcxHDAa @@ -1503,8 +1468,7 @@ aSLjI/Ya0zwUARMmyZ3RRGCyhIarPb20mKSaMf1/Nb23pS3k1QgmZhk5pAnXYsWu BJ6bvwEAasFiLGP6Zbdmxb2hIA== -----END CERTIFICATE-----` -const ignoreCNWithSANLeaf = ` ------BEGIN CERTIFICATE----- +const ignoreCNWithSANLeaf = `-----BEGIN CERTIFICATE----- MIIDaTCCAlGgAwIBAgIJAONakvRTxgJhMA0GCSqGSIb3DQEBCwUAMDAxEDAOBgNV BAoTB1RFU1RJTkcxHDAaBgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwHhcNMTUw MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAsMRAwDgYDVQQKEwdURVNUSU5HMRgw @@ -1526,8 +1490,7 @@ j2kBQyvnyKsXHLAKUoUOpd6t/1PHrfXnGj+HmzZNloJ/BZ1kiWb4eLvMljoLGkZn xZbqP3Krgjj4XNaXjg== -----END CERTIFICATE-----` -const excludedNamesLeaf = ` ------BEGIN CERTIFICATE----- +const excludedNamesLeaf = `-----BEGIN CERTIFICATE----- MIID4DCCAsigAwIBAgIHDUSFtJknhzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5 @@ -1549,11 +1512,9 @@ hDt8MCFJ8eSjCyKdtZh1MPMLrLVymmJV+Rc9JUUYM9TIeERkpl0rskcO1YGewkYt qKlWE+0S16+pzsWvKn831uylqwIb8ANBPsCX4aM4muFBHavSWAHgRO+P+yXVw8Q+ VQDnMHUe5PbZd1/+1KKVs1K/CkBCtoHNHp1d/JT+2zUQJphwja9CcgfFdVhSnHL4 oEEOFtqVMIuQfR2isi08qW/JGOHc4sFoLYB8hvdaxKWSE19A ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` -const excludedNamesIntermediate = ` ------BEGIN CERTIFICATE----- +const excludedNamesIntermediate = `-----BEGIN CERTIFICATE----- MIIDzTCCArWgAwIBAgIHDUSFqYeczDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5 @@ -1577,8 +1538,7 @@ LbIjZCSfgZnk/LK1KU1j91FI2bc2ULYZvAC1PAg8/zvIgxn6YM2Q7ZsdEgWw0FpS zMBX1/lk4wkFckeUIlkD55Y= -----END CERTIFICATE-----` -const excludedNamesRoot = ` ------BEGIN CERTIFICATE----- +const excludedNamesRoot = `-----BEGIN CERTIFICATE----- MIIEGTCCAwGgAwIBAgIHDUSFpInn/zANBgkqhkiG9w0BAQsFADCBozELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5 @@ -1603,46 +1563,16 @@ yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ/Nq7isGvknaTmcIwT +NQCZDd5eFeU8PpNX7rgaYE4GPq+EEmLVCBYmdctr8QVdqJ//8Xu3+1phjDy -----END CERTIFICATE-----` -const invalidCNRoot = ` ------BEGIN CERTIFICATE----- +const invalidCNRoot = `-----BEGIN CERTIFICATE----- MIIBFjCBvgIJAIsu4r+jb70UMAoGCCqGSM49BAMCMBQxEjAQBgNVBAsMCVRlc3Qg cm9vdDAeFw0xODA3MTExODMyMzVaFw0yODA3MDgxODMyMzVaMBQxEjAQBgNVBAsM CVRlc3Qgcm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF6oDgMg0LV6YhPj QXaPXYCc2cIyCdqp0ROUksRz0pOLTc5iY2nraUheRUD1vRRneq7GeXOVNn7uXONg oCGMjNwwCgYIKoZIzj0EAwIDRwAwRAIgDSiwgIn8g1lpruYH0QD1GYeoWVunfmrI XzZZl0eW/ugCICgOfXeZ2GGy3wIC0352BaC3a8r5AAb2XSGNe+e9wNN6 ------END CERTIFICATE----- -` - -const invalidCNWithoutSAN = ` -Certificate: - Data: - Version: 1 (0x0) - Serial Number: - 07:ba:bc:b7:d9:ab:0c:02:fe:50:1d:4e:15:a3:0d:e4:11:16:14:a2 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: OU = Test root - Validity - Not Before: Jul 11 18:35:21 2018 GMT - Not After : Jul 8 18:35:21 2028 GMT - Subject: CN = "foo,invalid" - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:a7:a6:7c:22:33:a7:47:7f:08:93:2d:5f:61:35: - 2e:da:45:67:76:f2:97:73:18:b0:01:12:4a:1a:d5: - b7:6f:41:3c:bb:05:69:f4:06:5d:ff:eb:2b:a7:85: - 0b:4c:f7:45:4e:81:40:7a:a9:c6:1d:bb:ba:d9:b9: - 26:b3:ca:50:90 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - Signature Algorithm: ecdsa-with-SHA256 - 30:45:02:21:00:85:96:75:b6:72:3c:67:12:a0:7f:86:04:81: - d2:dd:c8:67:50:d7:5f:85:c0:54:54:fc:e6:6b:45:08:93:d3: - 2a:02:20:60:86:3e:d6:28:a6:4e:da:dd:6e:95:89:cc:00:76: - 78:1c:03:80:85:a6:5a:0b:eb:c5:f3:9c:2e:df:ef:6e:fa ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const invalidCNWithoutSAN = `-----BEGIN CERTIFICATE----- MIIBJDCBywIUB7q8t9mrDAL+UB1OFaMN5BEWFKIwCgYIKoZIzj0EAwIwFDESMBAG A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4MzUyMVoXDTI4MDcwODE4MzUyMVow FjEUMBIGA1UEAwwLZm9vLGludmFsaWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC @@ -1650,38 +1580,9 @@ AASnpnwiM6dHfwiTLV9hNS7aRWd28pdzGLABEkoa1bdvQTy7BWn0Bl3/6yunhQtM 90VOgUB6qcYdu7rZuSazylCQMAoGCCqGSM49BAMCA0gAMEUCIQCFlnW2cjxnEqB/ hgSB0t3IZ1DXX4XAVFT85mtFCJPTKgIgYIY+1iimTtrdbpWJzAB2eBwDgIWmWgvr xfOcLt/vbvo= ------END CERTIFICATE----- -` - -const validCNWithoutSAN = ` -Certificate: - Data: - Version: 1 (0x0) - Serial Number: - 07:ba:bc:b7:d9:ab:0c:02:fe:50:1d:4e:15:a3:0d:e4:11:16:14:a4 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: OU = Test root - Validity - Not Before: Jul 11 18:47:24 2018 GMT - Not After : Jul 8 18:47:24 2028 GMT - Subject: CN = foo.example.com - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:a7:a6:7c:22:33:a7:47:7f:08:93:2d:5f:61:35: - 2e:da:45:67:76:f2:97:73:18:b0:01:12:4a:1a:d5: - b7:6f:41:3c:bb:05:69:f4:06:5d:ff:eb:2b:a7:85: - 0b:4c:f7:45:4e:81:40:7a:a9:c6:1d:bb:ba:d9:b9: - 26:b3:ca:50:90 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - Signature Algorithm: ecdsa-with-SHA256 - 30:44:02:20:53:6c:d7:b7:59:61:51:72:a5:18:a3:4b:0d:52: - ea:15:fa:d0:93:30:32:54:4b:ed:0f:58:85:b8:a8:1a:82:3b: - 02:20:14:77:4b:0e:7e:4f:0a:4f:64:26:97:dc:d0:ed:aa:67: - 1d:37:85:da:b4:87:ba:25:1c:2a:58:f7:23:11:8b:3d ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const validCNWithoutSAN = `-----BEGIN CERTIFICATE----- MIIBJzCBzwIUB7q8t9mrDAL+UB1OFaMN5BEWFKQwCgYIKoZIzj0EAwIwFDESMBAG A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4NDcyNFoXDTI4MDcwODE4NDcyNFow GjEYMBYGA1UEAwwPZm9vLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D @@ -1689,48 +1590,9 @@ AQcDQgAEp6Z8IjOnR38Iky1fYTUu2kVndvKXcxiwARJKGtW3b0E8uwVp9AZd/+sr p4ULTPdFToFAeqnGHbu62bkms8pQkDAKBggqhkjOPQQDAgNHADBEAiBTbNe3WWFR cqUYo0sNUuoV+tCTMDJUS+0PWIW4qBqCOwIgFHdLDn5PCk9kJpfc0O2qZx03hdq0 h7olHCpY9yMRiz0= ------END CERTIFICATE----- -` - -const ( - rootWithoutSKID = ` -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 78:29:2a:dc:2f:12:39:7f:c9:33:93:ea:61:39:7d:70 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: O = Acme Co - Validity - Not Before: Feb 4 22:56:34 2019 GMT - Not After : Feb 1 22:56:34 2029 GMT - Subject: O = Acme Co - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:84:a6:8c:69:53:af:87:4b:39:64:fe:04:24:e6: - d8:fc:d6:46:39:35:0e:92:dc:48:08:7e:02:5f:1e: - 07:53:5c:d9:e0:56:c5:82:07:f6:a3:e2:ad:f6:ad: - be:a0:4e:03:87:39:67:0c:9c:46:91:68:6b:0e:8e: - f8:49:97:9d:5b - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Key Encipherment, Certificate Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Alternative Name: - DNS:example - Signature Algorithm: ecdsa-with-SHA256 - 30:46:02:21:00:c6:81:61:61:42:8d:37:e7:d0:c3:72:43:44: - 17:bd:84:ff:88:81:68:9a:99:08:ab:3c:3a:c0:1e:ea:8c:ba: - c0:02:21:00:de:c9:fa:e5:5e:c6:e2:db:23:64:43:a9:37:42: - 72:92:7f:6e:89:38:ea:9e:2a:a7:fd:2f:ea:9a:ff:20:21:e7 ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const rootWithoutSKID = `-----BEGIN CERTIFICATE----- MIIBbzCCARSgAwIBAgIQeCkq3C8SOX/JM5PqYTl9cDAKBggqhkjOPQQDAjASMRAw DgYDVQQKEwdBY21lIENvMB4XDTE5MDIwNDIyNTYzNFoXDTI5MDIwMTIyNTYzNFow EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABISm @@ -1739,49 +1601,9 @@ ZwycRpFoaw6O+EmXnVujTDBKMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBIGA1UdEQQLMAmCB2V4YW1wbGUwCgYI KoZIzj0EAwIDSQAwRgIhAMaBYWFCjTfn0MNyQ0QXvYT/iIFompkIqzw6wB7qjLrA AiEA3sn65V7G4tsjZEOpN0Jykn9uiTjqniqn/S/qmv8gIec= ------END CERTIFICATE----- -` - leafWithAKID = ` - Certificate: - Data: - Version: 3 (0x2) - Serial Number: - f0:8a:62:f0:03:84:a2:cf:69:63:ad:71:3b:b6:5d:8c - Signature Algorithm: ecdsa-with-SHA256 - Issuer: O = Acme Co - Validity - Not Before: Feb 4 23:06:52 2019 GMT - Not After : Feb 1 23:06:52 2029 GMT - Subject: O = Acme LLC - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:5a:4e:4d:fb:ff:17:f7:b6:13:e8:29:45:34:81: - 39:ff:8c:9c:d9:8c:0a:9f:dd:b5:97:4c:2b:20:91: - 1c:4f:6b:be:53:27:66:ec:4a:ad:08:93:6d:66:36: - 0c:02:70:5d:01:ca:7f:c3:29:e9:4f:00:ba:b4:14: - ec:c5:c3:34:b3 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Key Encipherment - X509v3 Extended Key Usage: - TLS Web Server Authentication - X509v3 Basic Constraints: critical - CA:FALSE - X509v3 Authority Key Identifier: - keyid:C2:2B:5F:91:78:34:26:09:42:8D:6F:51:B2:C5:AF:4C:0B:DE:6A:42 - - X509v3 Subject Alternative Name: - DNS:example - Signature Algorithm: ecdsa-with-SHA256 - 30:44:02:20:64:e0:ba:56:89:63:ce:22:5e:4f:22:15:fd:3c: - 35:64:9a:3a:6b:7b:9a:32:a0:7f:f7:69:8c:06:f0:00:58:b8: - 02:20:09:e4:9f:6d:8b:9e:38:e1:b6:01:d5:ee:32:a4:94:65: - 93:2a:78:94:bb:26:57:4b:c7:dd:6c:3d:40:2b:63:90 ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const leafWithAKID = `-----BEGIN CERTIFICATE----- MIIBjTCCATSgAwIBAgIRAPCKYvADhKLPaWOtcTu2XYwwCgYIKoZIzj0EAwIwEjEQ MA4GA1UEChMHQWNtZSBDbzAeFw0xOTAyMDQyMzA2NTJaFw0yOTAyMDEyMzA2NTJa MBMxETAPBgNVBAoTCEFjbWUgTExDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE @@ -1791,9 +1613,7 @@ CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUwitfkXg0JglCjW9R ssWvTAveakIwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNHADBEAiBk 4LpWiWPOIl5PIhX9PDVkmjpre5oyoH/3aYwG8ABYuAIgCeSfbYueOOG2AdXuMqSU ZZMqeJS7JldLx91sPUArY5A= ------END CERTIFICATE----- -` -) +-----END CERTIFICATE-----` var unknownAuthorityErrorTests = []struct { cert string @@ -2124,3 +1944,33 @@ func TestLongChain(t *testing.T) { } t.Logf("verification took %v", time.Since(start)) } + +func TestSystemRootsError(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Windows does not use (or support) systemRoots") + } + + defer func(oldSystemRoots *CertPool) { systemRoots = oldSystemRoots }(systemRootsPool()) + + opts := VerifyOptions{ + Intermediates: NewCertPool(), + DNSName: "www.google.com", + CurrentTime: time.Unix(1395785200, 0), + } + + if ok := opts.Intermediates.AppendCertsFromPEM([]byte(giag2Intermediate)); !ok { + t.Fatalf("failed to parse intermediate") + } + + leaf, err := certificateFromPEM(googleLeaf) + if err != nil { + t.Fatalf("failed to parse leaf: %v", err) + } + + systemRoots = nil + + _, err = leaf.Verify(opts) + if _, ok := err.(SystemRootsError); !ok { + t.Errorf("error was not SystemRootsError: %v", err) + } +} diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index 316e7ce..a0ba7ec 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -255,12 +255,9 @@ type ConnBeginTx interface { // SessionResetter may be implemented by Conn to allow drivers to reset the // session state associated with the connection and to signal a bad connection. type SessionResetter interface { - // ResetSession is called while a connection is in the connection - // pool. No queries will run on this connection until this method returns. - // - // If the connection is bad this should return driver.ErrBadConn to prevent - // the connection from being returned to the connection pool. Any other - // error will be discarded. + // ResetSession is called prior to executing a query on the connection + // if the connection has been used before. If the driver returns ErrBadConn + // the connection is discarded. ResetSession(ctx context.Context) error } diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go index a0028be..0ec72d4 100644 --- a/src/database/sql/fakedb_test.go +++ b/src/database/sql/fakedb_test.go @@ -390,12 +390,19 @@ func setStrictFakeConnClose(t *testing.T) { func (c *fakeConn) ResetSession(ctx context.Context) error { c.dirtySession = false + c.currTx = nil if c.isBad() { return driver.ErrBadConn } return nil } +var _ validator = (*fakeConn)(nil) + +func (c *fakeConn) IsValid() bool { + return !c.isBad() +} + func (c *fakeConn) Close() (err error) { drv := fdriver.(*fakeDriver) defer func() { @@ -728,6 +735,9 @@ var hookExecBadConn func() bool func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) { panic("Using ExecContext") } + +var errFakeConnSessionDirty = errors.New("fakedb: session is dirty") + func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { if s.panic == "Exec" { panic(s.panic) @@ -740,7 +750,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d return nil, driver.ErrBadConn } if s.c.isDirtyAndMark() { - return nil, errors.New("fakedb: session is dirty") + return nil, errFakeConnSessionDirty } err := checkSubsetTypes(s.c.db.allowAny, args) @@ -854,7 +864,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( return nil, driver.ErrBadConn } if s.c.isDirtyAndMark() { - return nil, errors.New("fakedb: session is dirty") + return nil, errFakeConnSessionDirty } err := checkSubsetTypes(s.c.db.allowAny, args) @@ -887,6 +897,37 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( } } } + if s.table == "tx_status" && s.colName[0] == "tx_status" { + txStatus := "autocommit" + if s.c.currTx != nil { + txStatus = "transaction" + } + cursor := &rowsCursor{ + parentMem: s.c, + posRow: -1, + rows: [][]*row{ + []*row{ + { + cols: []interface{}{ + txStatus, + }, + }, + }, + }, + cols: [][]string{ + []string{ + "tx_status", + }, + }, + colType: [][]string{ + []string{ + "string", + }, + }, + errPos: -1, + } + return cursor, nil + } t.mu.Lock() diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index 0f5bbc0..a0b7ca8 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -421,7 +421,6 @@ type DB struct { // It is closed during db.Close(). The close tells the connectionOpener // goroutine to exit. openerCh chan struct{} - resetterCh chan *driverConn closed bool dep map[finalCloser]depSet lastPut map[*driverConn]string // stacktrace of last conn's put; debug only @@ -458,10 +457,10 @@ type driverConn struct { sync.Mutex // guards following ci driver.Conn + needReset bool // The connection session should be reset before use if true. closed bool finalClosed bool // ci.Close has been called openStmt map[*driverStmt]bool - lastErr error // lastError captures the result of the session resetter. // guarded by db.mu inUse bool @@ -486,6 +485,41 @@ func (dc *driverConn) expired(timeout time.Duration) bool { return dc.createdAt.Add(timeout).Before(nowFunc()) } +// resetSession checks if the driver connection needs the +// session to be reset and if required, resets it. +func (dc *driverConn) resetSession(ctx context.Context) error { + dc.Lock() + defer dc.Unlock() + + if !dc.needReset { + return nil + } + if cr, ok := dc.ci.(driver.SessionResetter); ok { + return cr.ResetSession(ctx) + } + return nil +} + +// validator was introduced for Go1.15, but backported to Go1.14. +type validator interface { + IsValid() bool +} + +// validateConnection checks if the connection is valid and can +// still be used. It also marks the session for reset if required. +func (dc *driverConn) validateConnection(needsReset bool) bool { + dc.Lock() + defer dc.Unlock() + + if needsReset { + dc.needReset = true + } + if cv, ok := dc.ci.(validator); ok { + return cv.IsValid() + } + return true +} + // prepareLocked prepares the query on dc. When cg == nil the dc must keep track of // the prepared statements in a pool. func (dc *driverConn) prepareLocked(ctx context.Context, cg stmtConnGrabber, query string) (*driverStmt, error) { @@ -511,19 +545,6 @@ func (dc *driverConn) prepareLocked(ctx context.Context, cg stmtConnGrabber, que return ds, nil } -// resetSession resets the connection session and sets the lastErr -// that is checked before returning the connection to another query. -// -// resetSession assumes that the embedded mutex is locked when the connection -// was returned to the pool. This unlocks the mutex. -func (dc *driverConn) resetSession(ctx context.Context) { - defer dc.Unlock() // In case of panic. - if dc.closed { // Check if the database has been closed. - return - } - dc.lastErr = dc.ci.(driver.SessionResetter).ResetSession(ctx) -} - // the dc.db's Mutex is held. func (dc *driverConn) closeDBLocked() func() error { dc.Lock() @@ -713,14 +734,12 @@ func OpenDB(c driver.Connector) *DB { db := &DB{ connector: c, openerCh: make(chan struct{}, connectionRequestQueueSize), - resetterCh: make(chan *driverConn, 50), lastPut: make(map[*driverConn]string), connRequests: make(map[uint64]chan connRequest), stop: cancel, } go db.connectionOpener(ctx) - go db.connectionResetter(ctx) return db } @@ -1058,23 +1077,6 @@ func (db *DB) connectionOpener(ctx context.Context) { } } -// connectionResetter runs in a separate goroutine to reset connections async -// to exported API. -func (db *DB) connectionResetter(ctx context.Context) { - for { - select { - case <-ctx.Done(): - close(db.resetterCh) - for dc := range db.resetterCh { - dc.Unlock() - } - return - case dc := <-db.resetterCh: - dc.resetSession(ctx) - } - } -} - // Open one new connection func (db *DB) openNewConnection(ctx context.Context) { // maybeOpenNewConnctions has already executed db.numOpen++ before it sent @@ -1155,14 +1157,13 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn conn.Close() return nil, driver.ErrBadConn } - // Lock around reading lastErr to ensure the session resetter finished. - conn.Lock() - err := conn.lastErr - conn.Unlock() - if err == driver.ErrBadConn { + + // Reset the session if required. + if err := conn.resetSession(ctx); err == driver.ErrBadConn { conn.Close() return nil, driver.ErrBadConn } + return conn, nil } @@ -1204,18 +1205,22 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn if !ok { return nil, errDBClosed } - if ret.err == nil && ret.conn.expired(lifetime) { + // Only check if the connection is expired if the strategy is cachedOrNewConns. + // If we require a new connection, just re-use the connection without looking + // at the expiry time. If it is expired, it will be checked when it is placed + // back into the connection pool. + // This prioritizes giving a valid connection to a client over the exact connection + // lifetime, which could expire exactly after this point anyway. + if strategy == cachedOrNewConn && ret.err == nil && ret.conn.expired(lifetime) { ret.conn.Close() return nil, driver.ErrBadConn } if ret.conn == nil { return nil, ret.err } - // Lock around reading lastErr to ensure the session resetter finished. - ret.conn.Lock() - err := ret.conn.lastErr - ret.conn.Unlock() - if err == driver.ErrBadConn { + + // Reset the session if required. + if err := ret.conn.resetSession(ctx); err == driver.ErrBadConn { ret.conn.Close() return nil, driver.ErrBadConn } @@ -1275,13 +1280,23 @@ const debugGetPut = false // putConn adds a connection to the db's free pool. // err is optionally the last error that occurred on this connection. func (db *DB) putConn(dc *driverConn, err error, resetSession bool) { + if err != driver.ErrBadConn { + if !dc.validateConnection(resetSession) { + err = driver.ErrBadConn + } + } db.mu.Lock() if !dc.inUse { + db.mu.Unlock() if debugGetPut { fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", dc, stack(), db.lastPut[dc]) } panic("sql: connection returned that was never out") } + + if err != driver.ErrBadConn && dc.expired(db.maxLifetime) { + err = driver.ErrBadConn + } if debugGetPut { db.lastPut[dc] = stack() } @@ -1305,41 +1320,13 @@ func (db *DB) putConn(dc *driverConn, err error, resetSession bool) { if putConnHook != nil { putConnHook(db, dc) } - if db.closed { - // Connections do not need to be reset if they will be closed. - // Prevents writing to resetterCh after the DB has closed. - resetSession = false - } - if resetSession { - if _, resetSession = dc.ci.(driver.SessionResetter); resetSession { - // Lock the driverConn here so it isn't released until - // the connection is reset. - // The lock must be taken before the connection is put into - // the pool to prevent it from being taken out before it is reset. - dc.Lock() - } - } added := db.putConnDBLocked(dc, nil) db.mu.Unlock() if !added { - if resetSession { - dc.Unlock() - } dc.Close() return } - if !resetSession { - return - } - select { - default: - // If the resetterCh is blocking then mark the connection - // as bad and continue on. - dc.lastErr = driver.ErrBadConn - dc.Unlock() - case db.resetterCh <- dc: - } } // Satisfy a connRequest or put the driverConn in the idle pool and return true @@ -1701,7 +1688,11 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra // beginDC starts a transaction. The provided dc must be valid and ready to use. func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error), opts *TxOptions) (tx *Tx, err error) { var txi driver.Tx + keepConnOnRollback := false withLock(dc, func() { + _, hasSessionResetter := dc.ci.(driver.SessionResetter) + _, hasConnectionValidator := dc.ci.(validator) + keepConnOnRollback = hasSessionResetter && hasConnectionValidator txi, err = ctxDriverBegin(ctx, opts, dc.ci) }) if err != nil { @@ -1713,12 +1704,13 @@ func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error), // The cancel function in Tx will be called after done is set to true. ctx, cancel := context.WithCancel(ctx) tx = &Tx{ - db: db, - dc: dc, - releaseConn: release, - txi: txi, - cancel: cancel, - ctx: ctx, + db: db, + dc: dc, + releaseConn: release, + txi: txi, + cancel: cancel, + keepConnOnRollback: keepConnOnRollback, + ctx: ctx, } go tx.awaitDone() return tx, nil @@ -1980,6 +1972,11 @@ type Tx struct { // Use atomic operations on value when checking value. done int32 + // keepConnOnRollback is true if the driver knows + // how to reset the connection's session and if need be discard + // the connection. + keepConnOnRollback bool + // All Stmts prepared for this transaction. These will be closed after the // transaction has been committed or rolled back. stmts struct { @@ -2005,7 +2002,10 @@ func (tx *Tx) awaitDone() { // transaction is closed and the resources are released. This // rollback does nothing if the transaction has already been // committed or rolled back. - tx.rollback(true) + // Do not discard the connection if the connection knows + // how to reset the session. + discardConnection := !tx.keepConnOnRollback + tx.rollback(discardConnection) } func (tx *Tx) isDone() bool { @@ -2016,14 +2016,10 @@ func (tx *Tx) isDone() bool { // that has already been committed or rolled back. var ErrTxDone = errors.New("sql: transaction has already been committed or rolled back") -// close returns the connection to the pool and -// must only be called by Tx.rollback or Tx.Commit. -func (tx *Tx) close(err error) { - tx.cancel() - - tx.closemu.Lock() - defer tx.closemu.Unlock() - +// closeLocked returns the connection to the pool and +// must only be called by Tx.rollback or Tx.Commit while +// closemu is Locked and tx already canceled. +func (tx *Tx) closeLocked(err error) { tx.releaseConn(err) tx.dc = nil tx.txi = nil @@ -2090,6 +2086,15 @@ func (tx *Tx) Commit() error { if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) { return ErrTxDone } + + // Cancel the Tx to release any active R-closemu locks. + // This is safe to do because tx.done has already transitioned + // from 0 to 1. Hold the W-closemu lock prior to rollback + // to ensure no other connection has an active query. + tx.cancel() + tx.closemu.Lock() + defer tx.closemu.Unlock() + var err error withLock(tx.dc, func() { err = tx.txi.Commit() @@ -2097,16 +2102,31 @@ func (tx *Tx) Commit() error { if err != driver.ErrBadConn { tx.closePrepared() } - tx.close(err) + tx.closeLocked(err) return err } +var rollbackHook func() + // rollback aborts the transaction and optionally forces the pool to discard // the connection. func (tx *Tx) rollback(discardConn bool) error { if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) { return ErrTxDone } + + if rollbackHook != nil { + rollbackHook() + } + + // Cancel the Tx to release any active R-closemu locks. + // This is safe to do because tx.done has already transitioned + // from 0 to 1. Hold the W-closemu lock prior to rollback + // to ensure no other connection has an active query. + tx.cancel() + tx.closemu.Lock() + defer tx.closemu.Unlock() + var err error withLock(tx.dc, func() { err = tx.txi.Rollback() @@ -2117,7 +2137,7 @@ func (tx *Tx) rollback(discardConn bool) error { if discardConn { err = driver.ErrBadConn } - tx.close(err) + tx.closeLocked(err) return err } diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 6f59260..a9e1800 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -80,6 +80,11 @@ func newTestDBConnector(t testing.TB, fc *fakeConnector, name string) *DB { exec(t, db, "CREATE|magicquery|op=string,millis=int32") exec(t, db, "INSERT|magicquery|op=sleep,millis=10") } + if name == "tx_status" { + // Magic table name and column, known by fakedb_test.go. + exec(t, db, "CREATE|tx_status|tx_status=string") + exec(t, db, "INSERT|tx_status|tx_status=invalid") + } return db } @@ -437,6 +442,7 @@ func TestTxContextWait(t *testing.T) { } t.Fatal(err) } + tx.keepConnOnRollback = false // This will trigger the *fakeConn.Prepare method which will take time // performing the query. The ctxDriverPrepare func will check the context @@ -449,6 +455,35 @@ func TestTxContextWait(t *testing.T) { waitForFree(t, db, 5*time.Second, 0) } +// TestTxContextWaitNoDiscard is the same as TestTxContextWait, but should not discard +// the final connection. +func TestTxContextWaitNoDiscard(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond) + defer cancel() + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + // Guard against the context being canceled before BeginTx completes. + if err == context.DeadlineExceeded { + t.Skip("tx context canceled prior to first use") + } + t.Fatal(err) + } + + // This will trigger the *fakeConn.Prepare method which will take time + // performing the query. The ctxDriverPrepare func will check the context + // after this and close the rows and return an error. + _, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|") + if err != context.DeadlineExceeded { + t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err) + } + + waitForFree(t, db, 5*time.Second, 1) +} + // TestUnsupportedOptions checks that the database fails when a driver that // doesn't implement ConnBeginTx is used with non-default options and an // un-cancellable context. @@ -1525,6 +1560,37 @@ func TestConnTx(t *testing.T) { } } +// TestConnIsValid verifies that a database connection that should be discarded, +// is actually discarded and does not re-enter the connection pool. +// If the IsValid method from *fakeConn is removed, this test will fail. +func TestConnIsValid(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + db.SetMaxOpenConns(1) + + ctx := context.Background() + + c, err := db.Conn(ctx) + if err != nil { + t.Fatal(err) + } + + err = c.Raw(func(raw interface{}) error { + dc := raw.(*fakeConn) + dc.stickyBad = true + return nil + }) + if err != nil { + t.Fatal(err) + } + c.Close() + + if len(db.freeConn) > 0 && db.freeConn[0].ci.(*fakeConn).stickyBad { + t.Fatal("bad connection returned to pool; expected bad connection to be discarded") + } +} + // Tests fix for issue 2542, that we release a lock when querying on // a closed connection. func TestIssue2542Deadlock(t *testing.T) { @@ -2658,6 +2724,159 @@ func TestManyErrBadConn(t *testing.T) { } } +// Issue 34755: Ensure that a Tx cannot commit after a rollback. +func TestTxCannotCommitAfterRollback(t *testing.T) { + db := newTestDB(t, "tx_status") + defer closeDB(t, db) + + // First check query reporting is correct. + var txStatus string + err := db.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus) + if err != nil { + t.Fatal(err) + } + if g, w := txStatus, "autocommit"; g != w { + t.Fatalf("tx_status=%q, wanted %q", g, w) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + t.Fatal(err) + } + + // Ignore dirty session for this test. + // A failing test should trigger the dirty session flag as well, + // but that isn't exactly what this should test for. + tx.txi.(*fakeTx).c.skipDirtySession = true + + defer tx.Rollback() + + err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus) + if err != nil { + t.Fatal(err) + } + if g, w := txStatus, "transaction"; g != w { + t.Fatalf("tx_status=%q, wanted %q", g, w) + } + + // 1. Begin a transaction. + // 2. (A) Start a query, (B) begin Tx rollback through a ctx cancel. + // 3. Check if 2.A has committed in Tx (pass) or outside of Tx (fail). + sendQuery := make(chan struct{}) + hookTxGrabConn = func() { + cancel() + <-sendQuery + } + rollbackHook = func() { + close(sendQuery) + } + defer func() { + hookTxGrabConn = nil + rollbackHook = nil + }() + + err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus) + if err != nil { + // A failure here would be expected if skipDirtySession was not set to true above. + t.Fatal(err) + } + if g, w := txStatus, "transaction"; g != w { + t.Fatalf("tx_status=%q, wanted %q", g, w) + } +} + +// Issue32530 encounters an issue where a connection may +// expire right after it comes out of a used connection pool +// even when a new connection is requested. +func TestConnExpiresFreshOutOfPool(t *testing.T) { + execCases := []struct { + expired bool + badReset bool + }{ + {false, false}, + {true, false}, + {false, true}, + } + + t0 := time.Unix(1000000, 0) + offset := time.Duration(0) + offsetMu := sync.RWMutex{} + + nowFunc = func() time.Time { + offsetMu.RLock() + defer offsetMu.RUnlock() + return t0.Add(offset) + } + defer func() { nowFunc = time.Now }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + db := newTestDB(t, "magicquery") + defer closeDB(t, db) + + db.SetMaxOpenConns(1) + + for _, ec := range execCases { + ec := ec + name := fmt.Sprintf("expired=%t,badReset=%t", ec.expired, ec.badReset) + t.Run(name, func(t *testing.T) { + db.clearAllConns(t) + + db.SetMaxIdleConns(1) + db.SetConnMaxLifetime(10 * time.Second) + + conn, err := db.conn(ctx, alwaysNewConn) + if err != nil { + t.Fatal(err) + } + + afterPutConn := make(chan struct{}) + waitingForConn := make(chan struct{}) + + go func() { + conn, err := db.conn(ctx, alwaysNewConn) + if err != nil { + t.Fatal(err) + } + db.putConn(conn, err, false) + close(afterPutConn) + }() + go func() { + for { + db.mu.Lock() + ct := len(db.connRequests) + db.mu.Unlock() + if ct > 0 { + close(waitingForConn) + return + } + time.Sleep(10 * time.Millisecond) + } + }() + + <-waitingForConn + + offsetMu.Lock() + if ec.expired { + offset = 11 * time.Second + } else { + offset = time.Duration(0) + } + offsetMu.Unlock() + + conn.ci.(*fakeConn).stickyBad = ec.badReset + + db.putConn(conn, err, true) + + <-afterPutConn + }) + } +} + // TestIssue20575 ensures the Rows from query does not block // closing a transaction. Ensure Rows is closed while closing a trasaction. func TestIssue20575(t *testing.T) { diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index b60e2bb..86d8a69 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -213,9 +213,6 @@ type decodeState struct { savedError error useNumber bool disallowUnknownFields bool - // safeUnquote is the number of current string literal bytes that don't - // need to be unquoted. When negative, no bytes need unquoting. - safeUnquote int } // readIndex returns the position of the last byte read. @@ -317,27 +314,13 @@ func (d *decodeState) rescanLiteral() { Switch: switch data[i-1] { case '"': // string - // safeUnquote is initialized at -1, which means that all bytes - // checked so far can be unquoted at a later time with no work - // at all. When reaching the closing '"', if safeUnquote is - // still -1, all bytes can be unquoted with no work. Otherwise, - // only those bytes up until the first '\\' or non-ascii rune - // can be safely unquoted. - safeUnquote := -1 for ; i < len(data); i++ { - if c := data[i]; c == '\\' { - if safeUnquote < 0 { // first unsafe byte - safeUnquote = int(i - d.off) - } + switch data[i] { + case '\\': i++ // escaped char - } else if c == '"' { - d.safeUnquote = safeUnquote + case '"': i++ // tokenize the closing quote too break Switch - } else if c >= utf8.RuneSelf { - if safeUnquote < 0 { // first unsafe byte - safeUnquote = int(i - d.off) - } } } case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': // number @@ -691,7 +674,7 @@ func (d *decodeState) object(v reflect.Value) error { start := d.readIndex() d.rescanLiteral() item := d.data[start:d.readIndex()] - key, ok := d.unquoteBytes(item) + key, ok := unquoteBytes(item) if !ok { panic(phasePanicMsg) } @@ -892,7 +875,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex())}) return nil } - s, ok := d.unquoteBytes(item) + s, ok := unquoteBytes(item) if !ok { if fromQuoted { return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) @@ -943,7 +926,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool } case '"': // string - s, ok := d.unquoteBytes(item) + s, ok := unquoteBytes(item) if !ok { if fromQuoted { return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) @@ -1103,7 +1086,7 @@ func (d *decodeState) objectInterface() map[string]interface{} { start := d.readIndex() d.rescanLiteral() item := d.data[start:d.readIndex()] - key, ok := d.unquote(item) + key, ok := unquote(item) if !ok { panic(phasePanicMsg) } @@ -1152,7 +1135,7 @@ func (d *decodeState) literalInterface() interface{} { return c == 't' case '"': // string - s, ok := d.unquote(item) + s, ok := unquote(item) if !ok { panic(phasePanicMsg) } @@ -1195,33 +1178,40 @@ func getu4(s []byte) rune { // unquote converts a quoted JSON string literal s into an actual string t. // The rules are different than for Go, so cannot use strconv.Unquote. -// The first byte in s must be '"'. -func (d *decodeState) unquote(s []byte) (t string, ok bool) { - s, ok = d.unquoteBytes(s) +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) t = string(s) return } -func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) { - // We already know that s[0] == '"'. However, we don't know that the - // closing quote exists in all cases, such as when the string is nested - // via the ",string" option. - if len(s) < 2 || s[len(s)-1] != '"' { +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { return } s = s[1 : len(s)-1] - // If there are no unusual characters, no unquoting is needed, so return - // a slice of the original bytes. - r := d.safeUnquote - if r == -1 { + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { return s, true } - // Only perform up to one safe unquote for each re-scanned string - // literal. In some edge cases, the decoder unquotes a literal a second - // time, even after another literal has been re-scanned. Thus, only the - // first unquote can safely use safeUnquote. - d.safeUnquote = 0 b := make([]byte, len(s)+2*utf8.UTFMax) w := copy(b, s[0:r]) diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index a49181e..689cc34 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -2459,4 +2459,20 @@ func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) { if t1 != t2 { t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2) } + + // See golang.org/issues/39555. + input := map[textUnmarshalerString]string{"FOO": "", `"`: ""} + + encoded, err := Marshal(input) + if err != nil { + t.Fatalf("Marshal unexpected error: %v", err) + } + var got map[textUnmarshalerString]string + if err := Unmarshal(encoded, &got); err != nil { + t.Fatalf("Unmarshal unexpected error: %v", err) + } + want := map[textUnmarshalerString]string{"foo": "", `"`: ""} + if !reflect.DeepEqual(want, got) { + t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got) + } } diff --git a/src/net/http/fs.go b/src/net/http/fs.go index d214485..b8a68cc 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -411,6 +411,7 @@ func checkIfNoneMatch(w ResponseWriter, r *Request) condResult { } if buf[0] == ',' { buf = buf[1:] + continue } if buf[0] == '*' { return condFalse diff --git a/src/net/http/fs_test.go b/src/net/http/fs_test.go index 435e34b..c082cee 100644 --- a/src/net/http/fs_test.go +++ b/src/net/http/fs_test.go @@ -849,6 +849,15 @@ func TestServeContent(t *testing.T) { wantStatus: 200, wantContentType: "text/css; charset=utf-8", }, + "if_none_match_malformed": { + file: "testdata/style.css", + serveETag: `"foo"`, + reqHeader: map[string]string{ + "If-None-Match": `,`, + }, + wantStatus: 200, + wantContentType: "text/css; charset=utf-8", + }, "range_good": { file: "testdata/style.css", serveETag: `"A"`, diff --git a/src/net/http/server.go b/src/net/http/server.go index 58aff08..af43799 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -425,6 +425,16 @@ type response struct { wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" wantsClose bool // HTTP request has Connection "close" + // canWriteContinue is a boolean value accessed as an atomic int32 + // that says whether or not a 100 Continue header can be written + // to the connection. + // writeContinueMu must be held while writing the header. + // These two fields together synchronize the body reader + // (the expectContinueReader, which wants to write 100 Continue) + // against the main writer. + canWriteContinue atomicBool + writeContinueMu sync.Mutex + w *bufio.Writer // buffers output in chunks to chunkWriter cw chunkWriter @@ -515,6 +525,7 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } // declareTrailer is called for each Trailer header when the // response header is written. It notes that a header will need to be @@ -877,21 +888,27 @@ type expectContinueReader struct { resp *response readCloser io.ReadCloser closed bool - sawEOF bool + sawEOF atomicBool } func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { if ecr.closed { return 0, ErrBodyReadAfterClose } - if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() { - ecr.resp.wroteContinue = true - ecr.resp.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") - ecr.resp.conn.bufw.Flush() + w := ecr.resp + if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() { + w.wroteContinue = true + w.writeContinueMu.Lock() + if w.canWriteContinue.isSet() { + w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") + w.conn.bufw.Flush() + w.canWriteContinue.setFalse() + } + w.writeContinueMu.Unlock() } n, err = ecr.readCloser.Read(p) if err == io.EOF { - ecr.sawEOF = true + ecr.sawEOF.setTrue() } return } @@ -1310,7 +1327,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // because we don't know if the next bytes on the wire will be // the body-following-the-timer or the subsequent request. // See Issue 11549. - if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF { + if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() { w.closeAfterReply = true } @@ -1560,6 +1577,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er } return 0, ErrHijacked } + + if w.canWriteContinue.isSet() { + // Body reader wants to write 100 Continue but hasn't yet. + // Tell it not to. The store must be done while holding the lock + // because the lock makes sure that there is not an active write + // this very moment. + w.writeContinueMu.Lock() + w.canWriteContinue.setFalse() + w.writeContinueMu.Unlock() + } + if !w.wroteHeader { w.WriteHeader(StatusOK) } @@ -1871,6 +1899,7 @@ func (c *conn) serve(ctx context.Context) { if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { // Wrap the Body reader with one that replies on the connection req.Body = &expectContinueReader{readCloser: req.Body, resp: w} + w.canWriteContinue.setTrue() } } else if req.Header.get("Expect") != "" { w.sendExpectationFailed() diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 5f2f600..69837c7 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -789,6 +789,11 @@ var loop1, loop2 Loop var loopy1, loopy2 Loopy var cycleMap1, cycleMap2, cycleMap3 map[string]interface{} +type structWithSelfPtr struct { + p *structWithSelfPtr + s string +} + func init() { loop1 = &loop2 loop2 = &loop1 @@ -845,6 +850,7 @@ var deepEqualTests = []DeepEqualTest{ {[]float64{math.NaN()}, self{}, true}, {map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, {map[float64]float64{math.NaN(): 1}, self{}, true}, + {&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false}, // Nil vs empty: not the same. {[]int{}, []int(nil), false}, diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go index f2d4616..8a2bf8b 100644 --- a/src/reflect/deepequal.go +++ b/src/reflect/deepequal.go @@ -45,8 +45,20 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool { } if hard(v1, v2) { - addr1 := v1.ptr - addr2 := v2.ptr + // For a Ptr or Map value, we need to check flagIndir, + // which we do by calling the pointer method. + // For Slice or Interface, flagIndir is always set, + // and using v.ptr suffices. + ptrval := func(v Value) unsafe.Pointer { + switch v.Kind() { + case Ptr, Map: + return v.pointer() + default: + return v.ptr + } + } + addr1 := ptrval(v1) + addr2 := ptrval(v2) if uintptr(addr1) > uintptr(addr2) { // Canonicalize order to reduce number of entries in visited. // Assumes non-moving garbage collector. diff --git a/src/reflect/type.go b/src/reflect/type.go index cd8522d..8a37a12 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -3067,6 +3067,7 @@ func ifaceIndir(t *rtype) bool { return t.kind&kindDirectIface == 0 } +// Note: this type must agree with runtime.bitvector. type bitVector struct { n uint32 // number of bits data []byte diff --git a/src/reflect/value.go b/src/reflect/value.go index 2b7dd66..45517be 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -574,6 +574,13 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool) { // Convert v to type typ if v is assignable to a variable // of type t in the language spec. // See issue 28761. + if typ.Kind() == Interface { + // We must clear the destination before calling assignTo, + // in case assignTo writes (with memory barriers) to the + // target location used as scratch space. See issue 39541. + *(*uintptr)(addr) = 0 + *(*uintptr)(add(addr, ptrSize, "typ.size == 2*ptrSize")) = 0 + } v = v.assignTo("reflect.MakeFunc", typ, addr) // We are writing to stack. No write barrier. @@ -2367,6 +2374,7 @@ func NewAt(typ Type, p unsafe.Pointer) Value { // assignTo returns a value v that can be assigned directly to typ. // It panics if v is not assignable to typ. // For a conversion to an interface type, target is a suggested scratch space to use. +// target must be initialized memory (or nil). func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { if v.flag&flagMethod != 0 { v = makeMethodValue(context, v) diff --git a/src/runtime/stack.go b/src/runtime/stack.go index ebbe3e0..0f5b165 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -551,6 +551,7 @@ func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) { } // Information from the compiler about the layout of stack frames. +// Note: this type must agree with reflect.bitVector. type bitvector struct { n int32 // # of bits bytedata *uint8 diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index 88ba0f0..5276600 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -526,6 +526,7 @@ func runBenchmarks(importPath string, matchString func(pat, str string) (bool, e name: "Main", w: os.Stdout, chatty: *chatty, + bench: true, }, importPath: importPath, benchFunc: func(b *B) { @@ -559,6 +560,7 @@ func (ctx *benchContext) processBench(b *B) { name: b.name, w: b.w, chatty: b.chatty, + bench: true, }, benchFunc: b.benchFunc, benchTime: b.benchTime, @@ -624,6 +626,7 @@ func (b *B) Run(name string, f func(b *B)) bool { creator: pc[:n], w: b.w, chatty: b.chatty, + bench: true, }, importPath: b.importPath, benchFunc: f, diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 95f8220..8eb0084 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -438,8 +438,6 @@ func TestTRun(t *T) { }, { // A chatty test should always log with fmt.Print, even if the // parent test has completed. - // TODO(deklerk) Capture the log of fmt.Print and assert that the - // subtest message is not lost. desc: "log in finished sub test with chatty", ok: false, chatty: true, @@ -477,35 +475,37 @@ func TestTRun(t *T) { }, }} for _, tc := range testCases { - ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) - buf := &bytes.Buffer{} - root := &T{ - common: common{ - signal: make(chan bool), - name: "Test", - w: buf, - chatty: tc.chatty, - }, - context: ctx, - } - ok := root.Run(tc.desc, tc.f) - ctx.release() + t.Run(tc.desc, func(t *T) { + ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) + buf := &bytes.Buffer{} + root := &T{ + common: common{ + signal: make(chan bool), + name: "Test", + w: buf, + chatty: tc.chatty, + }, + context: ctx, + } + ok := root.Run(tc.desc, tc.f) + ctx.release() - if ok != tc.ok { - t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok) - } - if ok != !root.Failed() { - t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed()) - } - if ctx.running != 0 || ctx.numWaiting != 0 { - t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting) - } - got := strings.TrimSpace(buf.String()) - want := strings.TrimSpace(tc.output) - re := makeRegexp(want) - if ok, err := regexp.MatchString(re, got); !ok || err != nil { - t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) - } + if ok != tc.ok { + t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok) + } + if ok != !root.Failed() { + t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed()) + } + if ctx.running != 0 || ctx.numWaiting != 0 { + t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting) + } + got := strings.TrimSpace(buf.String()) + want := strings.TrimSpace(tc.output) + re := makeRegexp(want) + if ok, err := regexp.MatchString(re, got); !ok || err != nil { + t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) + } + }) } } @@ -655,43 +655,45 @@ func TestBRun(t *T) { }, }} for _, tc := range testCases { - var ok bool - buf := &bytes.Buffer{} - // This is almost like the Benchmark function, except that we override - // the benchtime and catch the failure result of the subbenchmark. - root := &B{ - common: common{ - signal: make(chan bool), - name: "root", - w: buf, - chatty: tc.chatty, - }, - benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure. - benchTime: benchTimeFlag{d: 1 * time.Microsecond}, - } - root.runN(1) - if ok != !tc.failed { - t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed) - } - if !ok != root.Failed() { - t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed()) - } - // All tests are run as subtests - if root.result.N != 1 { - t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N) - } - got := strings.TrimSpace(buf.String()) - want := strings.TrimSpace(tc.output) - re := makeRegexp(want) - if ok, err := regexp.MatchString(re, got); !ok || err != nil { - t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) - } + t.Run(tc.desc, func(t *T) { + var ok bool + buf := &bytes.Buffer{} + // This is almost like the Benchmark function, except that we override + // the benchtime and catch the failure result of the subbenchmark. + root := &B{ + common: common{ + signal: make(chan bool), + name: "root", + w: buf, + chatty: tc.chatty, + }, + benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure. + benchTime: benchTimeFlag{d: 1 * time.Microsecond}, + } + root.runN(1) + if ok != !tc.failed { + t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed) + } + if !ok != root.Failed() { + t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed()) + } + // All tests are run as subtests + if root.result.N != 1 { + t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N) + } + got := strings.TrimSpace(buf.String()) + want := strings.TrimSpace(tc.output) + re := makeRegexp(want) + if ok, err := regexp.MatchString(re, got); !ok || err != nil { + t.Errorf("%s:output:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) + } + }) } } func makeRegexp(s string) string { s = regexp.QuoteMeta(s) - s = strings.ReplaceAll(s, ":NNN:", `:\d\d\d:`) + s = strings.ReplaceAll(s, ":NNN:", `:\d\d\d\d?:`) s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`) return s } diff --git a/src/testing/testing.go b/src/testing/testing.go index 966cafb..75f1b54 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -320,6 +320,7 @@ var ( cpuListStr *string parallel *int testlog *string + printer *testPrinter haveExamples bool // are there examples? @@ -329,6 +330,48 @@ var ( numFailed uint32 // number of test failures ) +type testPrinter struct { + chatty bool + + lastNameMu sync.Mutex // guards lastName + lastName string // last printed test name in chatty mode +} + +func newTestPrinter(chatty bool) *testPrinter { + return &testPrinter{ + chatty: chatty, + } +} + +func (p *testPrinter) Print(testName, out string) { + p.Fprint(os.Stdout, testName, out) +} + +func (p *testPrinter) Fprint(w io.Writer, testName, out string) { + p.lastNameMu.Lock() + defer p.lastNameMu.Unlock() + + if !p.chatty || + strings.HasPrefix(out, "--- PASS") || + strings.HasPrefix(out, "--- FAIL") || + strings.HasPrefix(out, "=== CONT") || + strings.HasPrefix(out, "=== RUN") { + p.lastName = testName + fmt.Fprint(w, out) + return + } + + if p.lastName == "" { + p.lastName = testName + } else if p.lastName != testName { + // Always printed as-is, with 0 decoration or indentation. So, we skip + // printing to w. + fmt.Printf("=== CONT %s\n", testName) + p.lastName = testName + } + fmt.Fprint(w, out) +} + // The maximum number of stack frames to go through when skipping helper functions for // the purpose of decorating log messages. const maxStackLen = 50 @@ -347,10 +390,11 @@ type common struct { cleanup func() // optional function to be called at the end of the test chatty bool // A copy of the chatty flag. + bench bool // Whether the current test is a benchmark. finished bool // Test function has completed. - hasSub int32 // written atomically - raceErrors int // number of races detected during test - runner string // function name of tRunner running the test + hasSub int32 // Written atomically. + raceErrors int // Number of races detected during test. + runner string // Function name of tRunner running the test. parent *common level int // Nesting depth of test or benchmark. @@ -480,9 +524,6 @@ func (c *common) decorate(s string, skip int) string { buf := new(strings.Builder) // Every line is indented at least 4 spaces. buf.WriteString(" ") - if c.chatty { - fmt.Fprintf(buf, "%s: ", c.name) - } fmt.Fprintf(buf, "%s:%d: ", file, line) lines := strings.Split(s, "\n") if l := len(lines); l > 1 && lines[l-1] == "" { @@ -501,12 +542,12 @@ func (c *common) decorate(s string, skip int) string { // flushToParent writes c.output to the parent after first writing the header // with the given format and arguments. -func (c *common) flushToParent(format string, args ...interface{}) { +func (c *common) flushToParent(testName, format string, args ...interface{}) { p := c.parent p.mu.Lock() defer p.mu.Unlock() - fmt.Fprintf(p.w, format, args...) + printer.Fprint(p.w, testName, fmt.Sprintf(format, args...)) c.mu.Lock() defer c.mu.Unlock() @@ -680,7 +721,14 @@ func (c *common) logDepth(s string, depth int) { panic("Log in goroutine after " + c.name + " has completed") } else { if c.chatty { - fmt.Print(c.decorate(s, depth+1)) + if c.bench { + // Benchmarks don't print === CONT, so we should skip the test + // printer and just print straight to stdout. + fmt.Print(c.decorate(s, depth+1)) + } else { + printer.Print(c.name, c.decorate(s, depth+1)) + } + return } c.output = append(c.output, c.decorate(s, depth+1)...) @@ -873,7 +921,7 @@ func (t *T) Parallel() { for ; root.parent != nil; root = root.parent { } root.mu.Lock() - fmt.Fprintf(root.w, "=== CONT %s\n", t.name) + printer.Fprint(root.w, t.name, fmt.Sprintf("=== CONT %s\n", t.name)) root.mu.Unlock() } @@ -932,7 +980,7 @@ func tRunner(t *T, fn func(t *T)) { root.duration += time.Since(root.start) d := root.duration root.mu.Unlock() - root.flushToParent("--- FAIL: %s (%s)\n", root.name, fmtDuration(d)) + root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d)) if r := root.parent.runCleanup(recoverAndReturnPanic); r != nil { fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r) } @@ -1031,7 +1079,7 @@ func (t *T) Run(name string, f func(t *T)) bool { for ; root.parent != nil; root = root.parent { } root.mu.Lock() - fmt.Fprintf(root.w, "=== RUN %s\n", t.name) + printer.Fprint(root.w, t.name, fmt.Sprintf("=== RUN %s\n", t.name)) root.mu.Unlock() } // Instead of reducing the running count of this test before calling the @@ -1179,6 +1227,8 @@ func (m *M) Run() int { flag.Parse() } + printer = newTestPrinter(Verbose()) + if *parallel < 1 { fmt.Fprintln(os.Stderr, "testing: -parallel can only be given a positive integer") flag.Usage() @@ -1218,12 +1268,12 @@ func (t *T) report() { dstr := fmtDuration(t.duration) format := "--- %s: %s (%s)\n" if t.Failed() { - t.flushToParent(format, "FAIL", t.name, dstr) + t.flushToParent(t.name, format, "FAIL", t.name, dstr) } else if t.chatty { if t.Skipped() { - t.flushToParent(format, "SKIP", t.name, dstr) + t.flushToParent(t.name, format, "SKIP", t.name, dstr) } else { - t.flushToParent(format, "PASS", t.name, dstr) + t.flushToParent(t.name, format, "PASS", t.name, dstr) } } } diff --git a/test/fixedbugs/issue39459.go b/test/fixedbugs/issue39459.go new file mode 100644 index 0000000..de78a17 --- /dev/null +++ b/test/fixedbugs/issue39459.go @@ -0,0 +1,22 @@ +// compile + +// Copyright 2020 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 p + +type T struct { // big enough to be an unSSAable type + a, b, c, d, e, f int +} + +func f(x interface{}, p *int) { + _ = *p // trigger nil check here, removing it from below + switch x := x.(type) { + case *T: + // Zero twice, so one of them will be removed by the deadstore pass + *x = T{} + *p = 0 // store op to prevent Zero ops from being optimized by the earlier opt pass rewrite rules + *x = T{} + } +} diff --git a/test/fixedbugs/issue39541.go b/test/fixedbugs/issue39541.go new file mode 100644 index 0000000..fba5291 --- /dev/null +++ b/test/fixedbugs/issue39541.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2020 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 + +import "reflect" + +func sub(args []reflect.Value) []reflect.Value { + type A struct { + s int + t int + } + return []reflect.Value{reflect.ValueOf(A{1, 2})} +} + +func main() { + f := reflect.MakeFunc(reflect.TypeOf((func() interface{})(nil)), sub).Interface().(func() interface{}) + c := make(chan bool, 100) + for i := 0; i < 100; i++ { + go func() { + for j := 0; j < 10000; j++ { + f() + } + c <- true + }() + } + for i := 0; i < 100; i++ { + <-c + } +}