From 034ed8089d057349032fb56a3c827ab51789c712 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Thu, 11 Jun 2020 22:02:27 -0400 Subject: [release-branch.go1.13] cmd/compile/internal/ssa: skip TestNexting with old Delve on linux/386 Support for linux/386 was added to Delve in version 1.4.1, but the version of Delve currently installed on the linux-386-longtest builder is 1.2.0. That isn't new enough, which causes the test to fail. Skip it on that builder until it can be made to work. The only reason it used to pass on the linux-386-longtest builder before is because that builder was misconfigured to run tests for linux/amd64. This was resolved in CL 234520. Also improve internal documentation and the text of skip reasons. For #39309. Fixes #39561. Change-Id: I395cb1f076e59dd3a3feb53e1dcdce5101e9a0f5 Reviewed-on: https://go-review.googlesource.com/c/go/+/237617 Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/debug_test.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cmd/compile/internal/ssa/debug_test.go b/src/cmd/compile/internal/ssa/debug_test.go index 73a0afb82c..d3aaa48055 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 { -- cgit v1.2.3-54-g00ecf From 7e87a63ed651af850c859fb9f5b2002e223508da Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 15 Nov 2019 17:08:23 -0800 Subject: [release-branch.go1.13] net: add more timing slop for TestDialParallel on Windows For #35616. Fixes #39538. For #29252. Change-Id: I51b2490100cfe0e902da09eee8d027e0ec86ed53 Reviewed-on: https://go-review.googlesource.com/c/go/+/207466 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Bryan C. Mills Reviewed-by: Brad Fitzpatrick (cherry picked from commit c20b71eb37889e7257453be152a994319c76d451) Reviewed-on: https://go-review.googlesource.com/c/go/+/237602 Run-TryBot: Dmitri Shuralyov Reviewed-by: Alexander Rakoczy --- src/net/dial_test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 1bf96fd3ce..42e5d68ded 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -154,7 +154,7 @@ func slowDialTCP(ctx context.Context, network string, laddr, raddr *TCPAddr) (*T return c, err } -func dialClosedPort() (actual, expected time.Duration) { +func dialClosedPort(t *testing.T) (actual, expected time.Duration) { // Estimate the expected time for this platform. // On Windows, dialing a closed port takes roughly 1 second, // but other platforms should be instantaneous. @@ -168,6 +168,7 @@ func dialClosedPort() (actual, expected time.Duration) { l, err := Listen("tcp", "127.0.0.1:0") if err != nil { + t.Logf("dialClosedPort: Listen failed: %v", err) return 999 * time.Hour, expected } addr := l.Addr().String() @@ -183,6 +184,7 @@ func dialClosedPort() (actual, expected time.Duration) { } elapsed := time.Now().Sub(startTime) if i == 2 { + t.Logf("dialClosedPort: measured delay %v", elapsed) return elapsed, expected } } @@ -195,7 +197,7 @@ func TestDialParallel(t *testing.T) { t.Skip("both IPv4 and IPv6 are required") } - closedPortDelay, expectClosedPortDelay := dialClosedPort() + closedPortDelay, expectClosedPortDelay := dialClosedPort(t) if closedPortDelay > expectClosedPortDelay { t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay) } @@ -316,8 +318,14 @@ func TestDialParallel(t *testing.T) { t.Errorf("#%d: got nil; want non-nil", i) } - expectElapsedMin := tt.expectElapsed - 95*time.Millisecond - expectElapsedMax := tt.expectElapsed + 95*time.Millisecond + // We used to always use 95 milliseconds as the slop, + // but that was flaky on Windows. See issue 35616. + slop := 95 * time.Millisecond + if fifth := tt.expectElapsed / 5; fifth > slop { + slop = fifth + } + expectElapsedMin := tt.expectElapsed - slop + expectElapsedMax := tt.expectElapsed + slop if elapsed < expectElapsedMin { t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectElapsedMin) } else if elapsed > expectElapsedMax { @@ -664,7 +672,7 @@ func TestDialerDualStack(t *testing.T) { t.Skip("both IPv4 and IPv6 are required") } - closedPortDelay, expectClosedPortDelay := dialClosedPort() + closedPortDelay, expectClosedPortDelay := dialClosedPort(t) if closedPortDelay > expectClosedPortDelay { t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay) } -- cgit v1.2.3-54-g00ecf From 206ee872d1e91f970ced55a772615e1203a6eaca Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Mon, 15 Jun 2020 15:23:04 -0400 Subject: [release-branch.go1.13] cmd: update golang.org/x/tools to v0.0.0-20200615191743-991d59a616de This teaches vet to recognize the new formats %#b and %O on integers, %x on float/complex. They have been supported since the 1.13 release, but vet would warn about it. Also update cmd/vet testdata accordingly, as was done in CL 202083. Fixes #39287. For #29986. Change-Id: Ia7817ee60ae6beac32cc402c0c68afa917e4ef0f Reviewed-on: https://go-review.googlesource.com/c/go/+/238018 Run-TryBot: Dmitri Shuralyov Reviewed-by: Ian Lance Taylor --- src/cmd/go.mod | 2 +- src/cmd/go.sum | 4 ++-- .../vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go | 7 ++++--- src/cmd/vendor/modules.txt | 2 +- src/cmd/vet/testdata/print/print.go | 5 ++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 19496a3c67..05d995f473 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -8,5 +8,5 @@ require ( golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect - golang.org/x/tools v0.0.0-20190611154301-25a4f137592f + golang.org/x/tools v0.0.0-20200615191743-991d59a616de ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 9aa94eee7b..44c1ac8d39 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -15,6 +15,6 @@ golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dz golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20190611154301-25a4f137592f h1:6awn5JC4pwVI5HiBqs7MDtRxnwV9PpO5iSA9v6P09pA= -golang.org/x/tools v0.0.0-20190611154301-25a4f137592f/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20200615191743-991d59a616de h1:kFKSx8iHlOzmtGWtfJW+b2UzcJ+rMWHHyUBpjrZq8To= +golang.org/x/tools v0.0.0-20200615191743-991d59a616de/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 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 f59e95dc21..5f9f771a62 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 @@ -722,7 +722,7 @@ var printVerbs = []printVerb{ // '#' is alternate format for several verbs. // ' ' is spacer for numbers {'%', noFlag, 0}, - {'b', numFlag, argInt | argFloat | argComplex | argPointer}, + {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer}, {'c', "-", argRune | argInt}, {'d', numFlag, argInt | argPointer}, {'e', sharpNumFlag, argFloat | argComplex}, @@ -732,6 +732,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}, @@ -740,8 +741,8 @@ var printVerbs = []printVerb{ {'U', "-#", argRune | argInt}, {'v', allFlags, anyType}, {'w', noFlag, anyType}, - {'x', sharpNumFlag, argRune | argInt | argString | argPointer}, - {'X', sharpNumFlag, argRune | argInt | argString | argPointer}, + {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, + {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, } // okPrintfArg compares the formatState to the arguments actually present, diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index e873ad4455..8c3ed65b3a 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -26,7 +26,7 @@ golang.org/x/crypto/ssh/terminal # golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/tools v0.0.0-20190611154301-25a4f137592f +# golang.org/x/tools v0.0.0-20200615191743-991d59a616de golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/internal/analysisflags golang.org/x/tools/go/analysis/internal/facts diff --git a/src/cmd/vet/testdata/print/print.go b/src/cmd/vet/testdata/print/print.go index 7a4783aee4..fca594925f 100644 --- a/src/cmd/vet/testdata/print/print.go +++ b/src/cmd/vet/testdata/print/print.go @@ -81,8 +81,8 @@ func PrintfTests() { fmt.Printf("%T %T", 3, i) fmt.Printf("%U %U", 3, i) fmt.Printf("%v %v", 3, i) - fmt.Printf("%x %x %x %x", 3, i, "hi", s) - fmt.Printf("%X %X %X %X", 3, i, "hi", s) + fmt.Printf("%x %x %x %x %x %x %x", 3, i, "hi", s, x, c, fslice) + fmt.Printf("%X %X %X %X %X %X %X", 3, i, "hi", s, x, c, fslice) fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3) fmt.Printf("%s", &stringerv) fmt.Printf("%v", &stringerv) @@ -125,7 +125,6 @@ func PrintfTests() { fmt.Printf("%t", 23) // ERROR "Printf format %t has arg 23 of wrong type int" fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64" fmt.Printf("%x", nil) // ERROR "Printf format %x has arg nil of wrong type untyped nil" - fmt.Printf("%X", 2.3) // ERROR "Printf format %X has arg 2.3 of wrong type float64" fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type .*print.ptrStringer" fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type .*print.ptrStringer" fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type .*print.embeddedStringer" -- cgit v1.2.3-54-g00ecf From 82c1a8705bc586d1d417a4b58b6e07aa01e93ba9 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Fri, 10 Jan 2020 17:53:05 -0500 Subject: [release-branch.go1.13] cmd/go: convert TestBuildIDContainsArchModeEnv to the script framework Part of converting all tests to script framework to improve test parallelism. Fixes #39823 Updates #36320 Updates #17751 Change-Id: I69c69809fb1698c8198ef3ea00103a9acb7b6ce7 Reviewed-on: https://go-review.googlesource.com/c/go/+/214387 Run-TryBot: Michael Matloob Reviewed-by: Jay Conrod (cherry picked from CL 214387) Reviewed-on: https://go-review.googlesource.com/c/go/+/239777 Run-TryBot: Bryan C. Mills TryBot-Result: Gobot Gobot Reviewed-by: Dmitri Shuralyov Reviewed-by: Michael Matloob Reviewed-by: Carlos Amedee --- src/cmd/go/go_test.go | 39 ---------------------- .../go/testdata/script/build_cache_arch_mode.txt | 27 +++++++++++++++ 2 files changed, 27 insertions(+), 39 deletions(-) create mode 100644 src/cmd/go/testdata/script/build_cache_arch_mode.txt diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index f6caa01fd2..6fbaf4df59 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -4878,45 +4878,6 @@ func TestGoTestRaceCoverModeFailures(t *testing.T) { tg.grepBothNot("PASS", "something passed") } -// 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 TestTestRegexps(t *testing.T) { tg := testgo(t) defer tg.cleanup() 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 0000000000..7e751d02b9 --- /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 -- cgit v1.2.3-54-g00ecf From 0b75fc7a92b8973601040eed8d13d8775d099f43 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 24 Jun 2020 20:59:18 -0700 Subject: [release-branch.go1.13] cmd/compile: remove check that Zero's arg has the correct base type It doesn't have to. The type in the aux field is authoritative. There are cases involving casting from interface{} where pointers have a placeholder pointer type (because the type is not known when the IData op is generated). The check was introduced in CL 13447. Fixes #39848 Change-Id: Id77a57577806a271aeebd20bea5d92d08ee7aa6b Reviewed-on: https://go-review.googlesource.com/c/go/+/239817 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: David Chase (cherry picked from commit 3b2f67a59702e4881625cb967f853ef56b0c4828) Reviewed-on: https://go-review.googlesource.com/c/go/+/239998 Run-TryBot: Dmitri Shuralyov --- src/cmd/compile/internal/ssa/deadstore.go | 16 +++++++--------- test/fixedbugs/issue39459.go | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 test/fixedbugs/issue39459.go diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index 69616b3a88..1cbba02f2f 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/test/fixedbugs/issue39459.go b/test/fixedbugs/issue39459.go new file mode 100644 index 0000000000..de78a17ce2 --- /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{} + } +} -- cgit v1.2.3-54-g00ecf From 3c1722cf30d1edc6756022c7a12efffabcfb098a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 18 Jun 2020 12:51:35 -0700 Subject: [release-branch.go1.13] reflect: zero stack slots before writing to them with write barriers reflect.assignTo writes to the target using write barriers. Make sure that the memory it is writing to is zeroed, so the write barrier does not read pointers from uninitialized memory. Fixes #39697 Change-Id: Ia64b2cacc193bffd0c1396bbce1dfb8182d4905b Reviewed-on: https://go-review.googlesource.com/c/go/+/238760 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor (cherry picked from commit 3dec253783e1211989102ac6abd34cddbf8ba0e6) Reviewed-on: https://go-review.googlesource.com/c/go/+/238862 Reviewed-by: Emmanuel Odeke --- src/reflect/value.go | 8 ++++++++ src/runtime/stack.go | 1 + test/fixedbugs/issue39541.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/fixedbugs/issue39541.go diff --git a/src/reflect/value.go b/src/reflect/value.go index 9ea95bc1d9..6daeb65b0f 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -577,6 +577,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 7ae3eeef83..39876e2cc7 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -543,6 +543,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/test/fixedbugs/issue39541.go b/test/fixedbugs/issue39541.go new file mode 100644 index 0000000000..fba52916eb --- /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 + } +} -- cgit v1.2.3-54-g00ecf From e19af0f1447e0f99688949c68cd8c39d188f699c Mon Sep 17 00:00:00 2001 From: Marcus Weiner Date: Wed, 24 Jun 2020 15:24:56 +0000 Subject: [release-branch.go1.13] net/http: fix panic with If-None-Match value in http.ServeContent Updates #39817. Fixes #39925. Change-Id: I79f2ad7c836a8a46569f603aca583fdd526d22dc GitHub-Last-Rev: 5b88aada219aaa2af0c7e1969ed6fa646117d9da GitHub-Pull-Request: golang/go#39821 Reviewed-on: https://go-review.googlesource.com/c/go/+/239699 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick (cherry picked from commit ce81a8f5e4f6c33036aa0777fabc47eeeab468dc) Reviewed-on: https://go-review.googlesource.com/c/go/+/242077 Run-TryBot: Dmitri Shuralyov Reviewed-by: Marcus Weiner Reviewed-by: Emmanuel Odeke --- src/net/http/fs.go | 1 + src/net/http/fs_test.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 41d46dced2..ac4ed23e71 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -408,6 +408,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 762e88b05f..f3c240fe7e 100644 --- a/src/net/http/fs_test.go +++ b/src/net/http/fs_test.go @@ -837,6 +837,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"`, -- cgit v1.2.3-54-g00ecf From e434185ec16b3783629bcb364b9a350706df7c1e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 13 Jul 2020 13:27:22 -0400 Subject: [release-branch.go1.13-security] net/http: synchronize "100 Continue" write and Handler writes The expectContinueReader writes to the connection on the first Request.Body read. Since a Handler might be doing a read in parallel or before a write, expectContinueReader needs to synchronize with the ResponseWriter, and abort if a response already went out. The tests will land in a separate CL. Fixes #34902 Fixes CVE-2020-15586 Change-Id: Icdd8dd539f45e8863762bd378194bb4741e875fc Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/793350 Reviewed-by: Filippo Valsorda (cherry picked from commit b5e504f4a07c572744b228fa1b10e3989c4c44f3) Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/793499 --- src/net/http/server.go | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/net/http/server.go b/src/net/http/server.go index 8252e45aca..a66e798be6 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -424,6 +424,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 @@ -514,6 +524,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 @@ -876,21 +887,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 } @@ -1309,7 +1326,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 } @@ -1554,6 +1571,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) } @@ -1866,6 +1894,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() -- cgit v1.2.3-54-g00ecf From 4a4c8d3d971fa77e4346d2874220ff736047f13c Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Thu, 18 Jun 2020 22:45:52 -0400 Subject: [release-branch.go1.13-security] crypto/x509: respect VerifyOptions.KeyUsages on Windows When using the platform verifier on Windows (because Roots is nil) we were always enforcing server auth EKUs if DNSName was set, and none otherwise. If an application was setting KeyUsages, they were not being respected. Started correctly surfacing IncompatibleUsage errors from the system verifier, as those are the ones applications will see if they are affected by this change. Also refactored verify_test.go to make it easier to add tests for this, and replaced the EKULeaf chain with a new one that doesn't have a SHA-1 signature. Thanks to Niall Newman for reporting this. Fixes #39360 Fixes CVE-2020-14039 Change-Id: If5c00d615f2944f7d57007891aae1307f9571c32 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/774414 Reviewed-by: Katie Hockman Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/793509 Reviewed-by: Filippo Valsorda --- src/crypto/x509/root_windows.go | 46 ++- src/crypto/x509/verify.go | 44 +- src/crypto/x509/verify_test.go | 888 +++++++++++++++++----------------------- 3 files changed, 430 insertions(+), 548 deletions(-) diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go index ebf159c178..94669f229e 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 3b5b3576bd..4f11647c81 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 } @@ -707,8 +716,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 @@ -716,9 +726,11 @@ 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 86fe76a57d..bbb68db857 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) + } +} -- cgit v1.2.3-54-g00ecf From 1f8859c22ccdeb969b252c8139bf4b1aae5c4909 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Tue, 14 Jul 2020 09:15:35 -0400 Subject: [release-branch.go1.13-security] go1.13.13 Change-Id: I65f5d9cc1363d369ced4496c1d6d3d7f9144f1b8 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/794127 Reviewed-by: Katie Hockman --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0b3e27fd11..4e2f10ea6b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -go1.13.12 \ No newline at end of file +go1.13.13 \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 42e7927c5fe2da4d00786a9025bc7f8f994b0fa4 Mon Sep 17 00:00:00 2001 From: Emmanuel T Odeke Date: Tue, 14 Jul 2020 18:20:47 -0700 Subject: [release-branch.go1.13] database/sql: backport 5 Tx rollback related CLs Manually backported the subject CLs, because of lack of Gerrit "forge-author" permissions, but also because the prior cherry picks didn't apply cleanly, due to a tight relation chain. The backport comprises of: * CL 174122 * CL 216197 * CL 223963 * CL 216240 * CL 216241 Note: Due to the restrictions that we cannot retroactively introduce API changes to Go1.13.13 that weren't in Go1.13, the Conn.Validator interface (from CL 174122, CL 223963) isn't exposed, and drivers will just be inspected, for if they have an IsValid() bool method implemented. For a description of the content of each CL: * CL 174122: database/sql: process all Session Resets synchronously Adds a new interface, driver.ConnectionValidator, to allow drivers to signal they should not be used again, separatly from the session resetter interface. This is done now that the session reset is done after the connection is put into the connection pool. Previous behavior attempted to run Session Resets in a background worker. This implementation had two problems: untested performance gains for additional complexity, and failures when the pool size exceeded the connection reset channel buffer size. * CL 216197: database/sql: check conn expiry when returning to pool, not when handing it out With the original connection reuse strategy, it was possible that when a new connection was requested, the pool would wait for an an existing connection to return for re-use in a full connection pool, and then it would check if the returned connection was expired. If the returned connection expired while awaiting re-use, it would return an error to the location requestiong the new connection. The existing call sites requesting a new connection was often the last attempt at returning a connection for a query. This would then result in a failed query. This change ensures that we perform the expiry check right before a connection is inserted back in to the connection pool for while requesting a new connection. If requesting a new connection it will no longer fail due to the connection expiring. * CL 216240: database/sql: prevent Tx statement from committing after rollback It was possible for a Tx that was aborted for rollback asynchronously to execute a query after the rollback had completed on the database, which often would auto commit the query outside of the transaction. By W-locking the tx.closemu prior to issuing the rollback connection it ensures any Tx query either fails or finishes on the Tx, and never after the Tx has rolled back. * CL 216241: database/sql: on Tx rollback, retain connection if driver can reset session Previously the Tx would drop the connection after rolling back from a context cancel. Now if the driver can reset the session, keep the connection. * CL 223963 database/sql: add test for Conn.Validator interface This addresses comments made by Russ after https://golang.org/cl/174122 was merged. It addes a test for the connection validator and renames the interface to just "Validator". Updates #31480 Updates #32530 Updates #32942 Updates #34775 Fixes #40205 Change-Id: I6d7307180b0db0bf159130d91161764cf0f18b58 Reviewed-on: https://go-review.googlesource.com/c/go/+/242522 Run-TryBot: Emmanuel Odeke TryBot-Result: Gobot Gobot Reviewed-by: Daniel Theophanes --- src/database/sql/driver/driver.go | 9 +- src/database/sql/fakedb_test.go | 45 +++++++- src/database/sql/sql.go | 200 ++++++++++++++++++---------------- src/database/sql/sql_test.go | 219 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 98 deletions(-) diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index 316e7cea37..a0ba7ecf69 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 c0371f3e78..378ed77c59 100644 --- a/src/database/sql/fakedb_test.go +++ b/src/database/sql/fakedb_test.go @@ -393,12 +393,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() { @@ -731,6 +738,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) @@ -743,7 +753,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) @@ -857,7 +867,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) @@ -890,6 +900,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 5c5b7dc7e9..25e241859c 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 f68cefe43a..a799093ff9 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. @@ -1521,6 +1556,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) { @@ -2654,6 +2720,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) { -- cgit v1.2.3-54-g00ecf From d3ba94164a1c404a01369fb54ddd4f5b94d91348 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Thu, 16 Jul 2020 17:41:24 -0400 Subject: [release-branch.go1.13] go1.13.14 Change-Id: Id54fd93e1839c28e40e9fcdaa5e7304dae274565 Reviewed-on: https://go-review.googlesource.com/c/go/+/243139 Run-TryBot: Andrew Bonventre TryBot-Result: Gobot Gobot Reviewed-by: Dmitri Shuralyov --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4e2f10ea6b..be38e0e48c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -go1.13.13 \ No newline at end of file +go1.13.14 \ No newline at end of file -- cgit v1.2.3-54-g00ecf