From 78fbf606bc2084b6bf2ba79af5c19bd13f587511 Mon Sep 17 00:00:00 2001
From: Kevin Paul Herbert 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 + } +}