diff options
author | Filippo Valsorda <filippo@golang.org> | 2018-12-14 19:32:41 -0500 |
---|---|---|
committer | Filippo Valsorda <filippo@golang.org> | 2018-12-14 19:32:41 -0500 |
commit | 572c4bce6792d169d467aad26781fa8cab52f8d6 (patch) | |
tree | e7c5807794770716336a0756ce3135212b106529 | |
parent | 35cf0d9f6bbdda847f39d5798f066a64eb91ea4e (diff) | |
parent | 4601a4c1b1c00fbe507508f0267ec5a9445bb7e5 (diff) | |
download | go-572c4bce6792d169d467aad26781fa8cab52f8d6.tar.gz go-572c4bce6792d169d467aad26781fa8cab52f8d6.zip |
[dev.boringcrypto.go1.11] all: merge go1.11.4 into dev.boringcrypto.go1.11
Change-Id: I313dfbed32bd65bf400e234b608fcd0efaec7469
48 files changed, 1279 insertions, 187 deletions
diff --git a/doc/devel/release.html b/doc/devel/release.html index b02efed501..73f7a0e304 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -49,6 +49,23 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.11.2">Go 1.11.2 milestone</a> on our issue tracker for details. </p> +<p> +go1.11.3 (released 2018/12/12) includes three security fixes to "go get" and +the <code>crypto/x509</code> package. +See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.11.3">Go +1.11.3 milestone</a> on our issue tracker for details. +</p> + +<p> +go1.11.4 (released 2018/12/14) includes fixes to cgo, the compiler, linker, +runtime, documentation, go command, and the <code>net/http</code> and +<code>go/types</code> packages. +It includes a fix to a bug introduced in Go 1.11.3 that broke <code>go</code> +<code>get</code> for import path patterns containing "<code>...</code>". +See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.11.4+label%3ACherryPickApproved">Go +1.11.4 milestone</a> on our issue tracker for details. +</p> + <h2 id="go1.10">go1.10 (released 2018/02/16)</h2> <p> @@ -98,6 +115,22 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.10.5">Go 1.10.5 milestone</a> on our issue tracker for details. </p> +<p> +go1.10.6 (released 2018/12/12) includes three security fixes to "go get" and +the <code>crypto/x509</code> package. +It contains the same fixes as Go 1.11.3 and was released at the same time. +See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.10.6">Go +1.10.6 milestone</a> on our issue tracker for details. +</p> + +<p> +go1.10.7 (released 2018/12/14) includes a fix to a bug introduced in Go 1.10.6 +that broke <code>go</code> <code>get</code> for import path patterns containing +"<code>...</code>". +See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.10.7+label%3ACherryPickApproved"> +Go 1.10.7 milestone</a> on our issue tracker for details. +</p> + <h2 id="go1.9">go1.9 (released 2017/08/24)</h2> <p> diff --git a/doc/go1.11.html b/doc/go1.11.html index 16b4c904cb..1d85be9fe4 100644 --- a/doc/go1.11.html +++ b/doc/go1.11.html @@ -400,6 +400,16 @@ details. <!-- CL 126275, CL 127156, CL 122217, CL 122575, CL 123177 --> information. </p> +<h3 id="run">Run</h3> + +<p> + <!-- CL 109341 --> + The <a href="/cmd/go/"><code>go</code> <code>run</code></a> + command now allows a single import path, a directory name or a + pattern matching a single package. + This allows <code>go</code> <code>run</code> <code>pkg</code> or <code>go</code> <code>run</code> <code>dir</code>, most importantly <code>go</code> <code>run</code> <code>.</code> +</p> + <h2 id="runtime">Runtime</h2> <p><!-- CL 85887 --> diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index ccacc50fe1..242ba6c0e5 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -92,6 +92,8 @@ func Test25143(t *testing.T) { test25143(t) } func Test23356(t *testing.T) { test23356(t) } func Test26066(t *testing.T) { test26066(t) } func Test26213(t *testing.T) { test26213(t) } +func Test27660(t *testing.T) { test27660(t) } +func Test28896(t *testing.T) { test28896(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } func BenchmarkGoString(b *testing.B) { benchGoString(b) } diff --git a/misc/cgo/test/issue27340.go b/misc/cgo/test/issue27340.go new file mode 100644 index 0000000000..f8c8a87f20 --- /dev/null +++ b/misc/cgo/test/issue27340.go @@ -0,0 +1,12 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Failed to resolve typedefs consistently. +// No runtime test; just make sure it compiles. + +package cgotest + +import "./issue27340" + +var issue27340Var = issue27340.Issue27340GoFunc diff --git a/misc/cgo/test/issue27340/a.go b/misc/cgo/test/issue27340/a.go new file mode 100644 index 0000000000..f5b120c1fd --- /dev/null +++ b/misc/cgo/test/issue27340/a.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Failed to resolve typedefs consistently. +// No runtime test; just make sure it compiles. +// In separate directory to isolate #pragma GCC diagnostic. + +package issue27340 + +// We use the #pragma to avoid a compiler warning about incompatible +// pointer types, because we generate code passing a struct ptr rather +// than using the typedef. This warning is expected and does not break +// a normal build. +// We can only disable -Wincompatible-pointer-types starting with GCC 5. + +// #if __GNU_MAJOR__ >= 5 +// +// #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" +// +// typedef struct { +// int a; +// } issue27340Struct, *issue27340Ptr; +// +// static void issue27340CFunc(issue27340Ptr p) {} +// +// #else /* _GNU_MAJOR_ < 5 */ +// +// typedef struct { +// int a; +// } issue27340Struct; +// +// static issue27340Struct* issue27340Ptr(issue27340Struct* p) { return p; } +// +// static void issue27340CFunc(issue27340Struct *p) {} +// #endif /* _GNU_MAJOR_ < 5 */ +import "C" + +func Issue27340GoFunc() { + var s C.issue27340Struct + C.issue27340CFunc(C.issue27340Ptr(&s)) +} diff --git a/misc/cgo/test/issue28896.go b/misc/cgo/test/issue28896.go new file mode 100644 index 0000000000..8796040f18 --- /dev/null +++ b/misc/cgo/test/issue28896.go @@ -0,0 +1,83 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// cgo was incorrectly adding padding after a packed struct. + +package cgotest + +/* +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +typedef struct { + void *f1; + uint32_t f2; +} __attribute__((__packed__)) innerPacked; + +typedef struct { + innerPacked g1; + uint64_t g2; +} outerPacked; + +typedef struct { + void *f1; + uint32_t f2; +} innerUnpacked; + +typedef struct { + innerUnpacked g1; + uint64_t g2; +} outerUnpacked; + +size_t offset(int x) { + switch (x) { + case 0: + return offsetof(innerPacked, f2); + case 1: + return offsetof(outerPacked, g2); + case 2: + return offsetof(innerUnpacked, f2); + case 3: + return offsetof(outerUnpacked, g2); + default: + abort(); + } +} +*/ +import "C" + +import ( + "testing" + "unsafe" +) + +func offset(i int) uintptr { + var pi C.innerPacked + var po C.outerPacked + var ui C.innerUnpacked + var uo C.outerUnpacked + switch i { + case 0: + return unsafe.Offsetof(pi.f2) + case 1: + return unsafe.Offsetof(po.g2) + case 2: + return unsafe.Offsetof(ui.f2) + case 3: + return unsafe.Offsetof(uo.g2) + default: + panic("can't happen") + } +} + +func test28896(t *testing.T) { + for i := 0; i < 4; i++ { + c := uintptr(C.offset(C.int(i))) + g := offset(i) + if c != g { + t.Errorf("%d: C: %d != Go %d", i, c, g) + } + } +} diff --git a/misc/cgo/test/issue9026/issue9026.go b/misc/cgo/test/issue9026/issue9026.go index 0af86e64da..149c26562a 100644 --- a/misc/cgo/test/issue9026/issue9026.go +++ b/misc/cgo/test/issue9026/issue9026.go @@ -29,7 +29,7 @@ func Test(t *testing.T) { // Brittle: the assertion may fail spuriously when the algorithm // changes, but should remain stable otherwise. got := fmt.Sprintf("%T %T", in, opts) - want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___1" + want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___0" if got != want { t.Errorf("Non-deterministic type names: got %s, want %s", got, want) } diff --git a/misc/cgo/test/test27660.go b/misc/cgo/test/test27660.go new file mode 100644 index 0000000000..8c23b7dc58 --- /dev/null +++ b/misc/cgo/test/test27660.go @@ -0,0 +1,54 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stress the interaction between the race detector and cgo in an +// attempt to reproduce the memory corruption described in #27660. +// The bug was very timing sensitive; at the time of writing this +// test would only trigger the bug about once out of every five runs. + +package cgotest + +// #include <unistd.h> +import "C" + +import ( + "context" + "math/rand" + "runtime" + "sync" + "testing" + "time" +) + +func test27660(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ints := make([]int, 100) + locks := make([]sync.Mutex, 100) + // Slowly create threads so that ThreadSanitizer is forced to + // frequently resize its SyncClocks. + for i := 0; i < 100; i++ { + go func() { + for ctx.Err() == nil { + // Sleep in C for long enough that it is likely that the runtime + // will retake this goroutine's currently wired P. + C.usleep(1000 /* 1ms */) + runtime.Gosched() // avoid starvation (see #28701) + } + }() + go func() { + // Trigger lots of synchronization and memory reads/writes to + // increase the likelihood that the race described in #27660 + // results in corruption of ThreadSanitizer's internal state + // and thus an assertion failure or segfault. + for ctx.Err() == nil { + j := rand.Intn(100) + locks[j].Lock() + ints[j]++ + locks[j].Unlock() + } + }() + time.Sleep(time.Millisecond) + } +} diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 20e794b4be..c2f089ee39 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -163,6 +163,10 @@ func (p *Package) Translate(f *File) { // Convert C.ulong to C.unsigned long, etc. cref.Name.C = cname(cref.Name.Go) } + + var conv typeConv + conv.Init(p.PtrSize, p.IntSize) + p.loadDefines(f) p.typedefs = map[string]bool{} p.typedefList = nil @@ -178,7 +182,7 @@ func (p *Package) Translate(f *File) { } needType := p.guessKinds(f) if len(needType) > 0 { - p.loadDWARF(f, needType) + p.loadDWARF(f, &conv, needType) } // In godefs mode we're OK with the typedefs, which @@ -472,7 +476,7 @@ func (p *Package) guessKinds(f *File) []*Name { // loadDWARF parses the DWARF debug information generated // by gcc to learn the details of the constants, variables, and types // being referred to as C.xxx. -func (p *Package) loadDWARF(f *File, names []*Name) { +func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) { // Extract the types from the DWARF section of an object // from a well-formed C program. Gcc only generates DWARF info // for symbols in the object file, so it is not enough to print the @@ -579,8 +583,6 @@ func (p *Package) loadDWARF(f *File, names []*Name) { } // Record types and typedef information. - var conv typeConv - conv.Init(p.PtrSize, p.IntSize) for i, n := range names { if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" { conv.getTypeIDs[n.Go[:len(n.Go)-9]] = true @@ -1734,10 +1736,10 @@ func runGcc(stdin []byte, args []string) (string, string) { // with equivalent memory layout. type typeConv struct { // Cache of already-translated or in-progress types. - m map[dwarf.Type]*Type + m map[string]*Type // Map from types to incomplete pointers to those types. - ptrs map[dwarf.Type][]*Type + ptrs map[string][]*Type // Keys of ptrs in insertion order (deterministic worklist) // ptrKeys contains exactly the keys in ptrs. ptrKeys []dwarf.Type @@ -1772,8 +1774,8 @@ var unionWithPointer = make(map[ast.Expr]bool) func (c *typeConv) Init(ptrSize, intSize int64) { c.ptrSize = ptrSize c.intSize = intSize - c.m = make(map[dwarf.Type]*Type) - c.ptrs = make(map[dwarf.Type][]*Type) + c.m = make(map[string]*Type) + c.ptrs = make(map[string][]*Type) c.getTypeIDs = make(map[string]bool) c.bool = c.Ident("bool") c.byte = c.Ident("byte") @@ -1881,11 +1883,12 @@ func (c *typeConv) FinishType(pos token.Pos) { // Keep looping until they're all done. for len(c.ptrKeys) > 0 { dtype := c.ptrKeys[0] + dtypeKey := dtype.String() c.ptrKeys = c.ptrKeys[1:] - ptrs := c.ptrs[dtype] - delete(c.ptrs, dtype) + ptrs := c.ptrs[dtypeKey] + delete(c.ptrs, dtypeKey) - // Note Type might invalidate c.ptrs[dtype]. + // Note Type might invalidate c.ptrs[dtypeKey]. t := c.Type(dtype, pos) for _, ptr := range ptrs { ptr.Go.(*ast.StarExpr).X = t.Go @@ -1897,18 +1900,29 @@ func (c *typeConv) FinishType(pos token.Pos) { // Type returns a *Type with the same memory layout as // dtype when used as the type of a variable or a struct field. func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { - if t, ok := c.m[dtype]; ok { - if t.Go == nil { - fatalf("%s: type conversion loop at %s", lineno(pos), dtype) + // Always recompute bad pointer typedefs, as the set of such + // typedefs changes as we see more types. + checkCache := true + if dtt, ok := dtype.(*dwarf.TypedefType); ok && c.badPointerTypedef(dtt) { + checkCache = false + } + + key := dtype.String() + + if checkCache { + if t, ok := c.m[key]; ok { + if t.Go == nil { + fatalf("%s: type conversion loop at %s", lineno(pos), dtype) + } + return t } - return t } t := new(Type) t.Size = dtype.Size() // note: wrong for array of pointers, corrected below t.Align = -1 t.C = &TypeRepr{Repr: dtype.Common().Name} - c.m[dtype] = t + c.m[key] = t switch dt := dtype.(type) { default: @@ -2071,10 +2085,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { // Placeholder initialization; completed in FinishType. t.Go = &ast.StarExpr{} t.C.Set("<incomplete>*") - if _, ok := c.ptrs[dt.Type]; !ok { + key := dt.Type.String() + if _, ok := c.ptrs[key]; !ok { c.ptrKeys = append(c.ptrKeys, dt.Type) } - c.ptrs[dt.Type] = append(c.ptrs[dt.Type], t) + c.ptrs[key] = append(c.ptrs[key], t) case *dwarf.QualType: t1 := c.Type(dt.Type, pos) @@ -2462,11 +2477,6 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct anon := 0 for _, f := range dt.Field { - if f.ByteOffset > off { - fld, sizes = c.pad(fld, sizes, f.ByteOffset-off) - off = f.ByteOffset - } - name := f.Name ft := f.Type @@ -2515,6 +2525,19 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct // structs are in system headers that cannot be corrected. continue } + + // Round off up to talign, assumed to be a power of 2. + off = (off + talign - 1) &^ (talign - 1) + + if f.ByteOffset > off { + fld, sizes = c.pad(fld, sizes, f.ByteOffset-off) + off = f.ByteOffset + } + if f.ByteOffset < off { + // Drop a packed field that we can't represent. + continue + } + n := len(fld) fld = fld[0 : n+1] if name == "" { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 9f1ea2ab4b..6d8086f750 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -481,13 +481,17 @@ func Main(archInit func(*Arch)) { // Phase 1: const, type, and names and types of funcs. // This will gather all the information about types // and methods but doesn't depend on any of it. + // + // We also defer type alias declarations until phase 2 + // to avoid cycles like #18640. + // TODO(gri) Remove this again once we have a fix for #25838. defercheckwidth() // Don't use range--typecheck can add closures to xtop. timings.Start("fe", "typecheck", "top1") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op != ODCL && op != OAS && op != OAS2 { + if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) { xtop[i] = typecheck(n, Etop) } } @@ -499,7 +503,7 @@ func Main(archInit func(*Arch)) { timings.Start("fe", "typecheck", "top2") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op == ODCL || op == OAS || op == OAS2 { + if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias { xtop[i] = typecheck(n, Etop) } } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index af43da6275..5b84a0a5bb 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3863,6 +3863,18 @@ func (s *state) boundsCheck(idx, len *ssa.Value) { s.check(cmp, panicindex) } +func couldBeNegative(v *ssa.Value) bool { + switch v.Op { + case ssa.OpSliceLen, ssa.OpSliceCap, ssa.OpStringLen: + return false + case ssa.OpConst64: + return v.AuxInt < 0 + case ssa.OpConst32: + return int32(v.AuxInt) < 0 + } + return true +} + // sliceBoundsCheck generates slice bounds checking code. Checks if 0 <= idx <= len, branches to exit if not. // Starts a new block on return. // idx and len are already converted to full int width. @@ -3870,6 +3882,15 @@ func (s *state) sliceBoundsCheck(idx, len *ssa.Value) { if Debug['B'] != 0 { return } + if couldBeNegative(len) { + // OpIsSliceInBounds requires second arg not negative; if it's not obviously true, must check. + cmpop := ssa.OpGeq64 + if len.Type.Size() == 4 { + cmpop = ssa.OpGeq32 + } + cmp := s.newValue2(cmpop, types.Types[TBOOL], len, s.zeroVal(len.Type)) + s.check(cmp, panicslice) + } // bounds check cmp := s.newValue2(ssa.OpIsSliceInBounds, types.Types[TBOOL], idx, len) diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 0af0ff82c4..c926b147de 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1116,7 +1116,7 @@ func calcHasCall(n *Node) bool { // When using soft-float, these ops might be rewritten to function calls // so we ensure they are evaluated first. - case OADD, OSUB, OMINUS: + case OADD, OSUB, OMINUS, OMUL: if thearch.SoftFloat && (isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) { return true } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index bf7090518c..74b8764933 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -202,8 +202,16 @@ func typecheck(n *Node, top int) *Node { // since it would expand indefinitely when aliases // are substituted. cycle := cycleFor(n) - for _, n := range cycle { - if n.Name != nil && !n.Name.Param.Alias { + for _, n1 := range cycle { + if n1.Name != nil && !n1.Name.Param.Alias { + // Cycle is ok. But if n is an alias type and doesn't + // have a type yet, we have a recursive type declaration + // with aliases that we can't handle properly yet. + // Report an error rather than crashing later. + if n.Name != nil && n.Name.Param.Alias && n.Type == nil { + lineno = n.Pos + Fatalf("cannot handle alias type declaration (issue #25838): %v", n) + } lineno = lno return n } @@ -3987,6 +3995,12 @@ func deadcode(fn *Node) { } func deadcodeslice(nn Nodes) { + var lastLabel = -1 + for i, n := range nn.Slice() { + if n != nil && n.Op == OLABEL { + lastLabel = i + } + } for i, n := range nn.Slice() { // Cut is set to true when all nodes after i'th position // should be removed. @@ -4009,10 +4023,14 @@ func deadcodeslice(nn Nodes) { // If "then" or "else" branch ends with panic or return statement, // it is safe to remove all statements after this node. // isterminating is not used to avoid goto-related complications. + // We must be careful not to deadcode-remove labels, as they + // might be the target of a goto. See issue 28616. if body := body.Slice(); len(body) != 0 { switch body[(len(body) - 1)].Op { case ORETURN, ORETJMP, OPANIC: - cut = true + if i > lastLabel { + cut = true + } } } } diff --git a/src/cmd/compile/internal/ssa/softfloat.go b/src/cmd/compile/internal/ssa/softfloat.go index 39829b046c..4afb94057d 100644 --- a/src/cmd/compile/internal/ssa/softfloat.go +++ b/src/cmd/compile/internal/ssa/softfloat.go @@ -4,7 +4,10 @@ package ssa -import "math" +import ( + "cmd/compile/internal/types" + "math" +) func softfloat(f *Func) { if !f.Config.SoftFloat { @@ -53,6 +56,15 @@ func softfloat(f *Func) { v.Type = f.Config.Types.UInt64 } newInt64 = newInt64 || v.Type.Size() == 8 + } else if (v.Op == OpStore || v.Op == OpZero || v.Op == OpMove) && v.Aux.(*types.Type).IsFloat() { + switch size := v.Aux.(*types.Type).Size(); size { + case 4: + v.Aux = f.Config.Types.UInt32 + case 8: + v.Aux = f.Config.Types.UInt64 + default: + v.Fatalf("bad float type with size %d", size) + } } } } diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go index e4148bceb0..a314c57160 100644 --- a/src/cmd/go/internal/get/get.go +++ b/src/cmd/go/internal/get/get.go @@ -232,7 +232,7 @@ var downloadCache = map[string]bool{} var downloadRootCache = map[string]bool{} // download runs the download half of the get command -// for the package named by the argument. +// for the package or pattern named by the argument. func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) { if mode&load.ResolveImport != 0 { // Caller is responsible for expanding vendor paths. @@ -402,6 +402,23 @@ func downloadPackage(p *load.Package) error { security = web.Insecure } + // p can be either a real package, or a pseudo-package whose “import path” is + // actually a wildcard pattern. + // Trim the path at the element containing the first wildcard, + // and hope that it applies to the wildcarded parts too. + // This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH. + importPrefix := p.ImportPath + if i := strings.Index(importPrefix, "..."); i >= 0 { + slash := strings.LastIndexByte(importPrefix[:i], '/') + if slash < 0 { + return fmt.Errorf("cannot expand ... in %q", p.ImportPath) + } + importPrefix = importPrefix[:slash] + } + if err := CheckImportPath(importPrefix); err != nil { + return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err) + } + if p.Internal.Build.SrcRoot != "" { // Directory exists. Look for checkout along path to src. vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot) @@ -421,7 +438,7 @@ func downloadPackage(p *load.Package) error { } repo = remote if !*getF && err == nil { - if rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security); err == nil { + if rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security); err == nil { repo := rr.Repo if rr.vcs.resolveRepo != nil { resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo) @@ -438,7 +455,7 @@ func downloadPackage(p *load.Package) error { } else { // Analyze the import path to determine the version control system, // repository, and the import path for the root of the repository. - rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security) + rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security) if err != nil { return err } diff --git a/src/cmd/go/internal/get/path.go b/src/cmd/go/internal/get/path.go new file mode 100644 index 0000000000..d443bd28a9 --- /dev/null +++ b/src/cmd/go/internal/get/path.go @@ -0,0 +1,192 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package get + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +// The following functions are copied verbatim from cmd/go/internal/module/module.go, +// with a change to additionally reject Windows short-names, +// and one to accept arbitrary letters (golang.org/issue/29101). +// +// TODO(bcmills): After the call site for this function is backported, +// consolidate this back down to a single copy. +// +// NOTE: DO NOT MERGE THESE UNTIL WE DECIDE ABOUT ARBITRARY LETTERS IN MODULE MODE. + +// CheckImportPath checks that an import path is valid. +func CheckImportPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed import path %q: %v", path, err) + } + return nil +} + +// checkPath checks that a general path is valid. +// It returns an error describing why but not mentioning path. +// Because these checks apply to both module paths and import paths, +// the caller is expected to add the "malformed ___ path %q: " prefix. +// fileName indicates whether the final element of the path is a file name +// (as opposed to a directory name). +func checkPath(path string, fileName bool) error { + if !utf8.ValidString(path) { + return fmt.Errorf("invalid UTF-8") + } + if path == "" { + return fmt.Errorf("empty string") + } + if strings.Contains(path, "..") { + return fmt.Errorf("double dot") + } + if strings.Contains(path, "//") { + return fmt.Errorf("double slash") + } + if path[len(path)-1] == '/' { + return fmt.Errorf("trailing slash") + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if err := checkElem(path[elemStart:i], fileName); err != nil { + return err + } + elemStart = i + 1 + } + } + if err := checkElem(path[elemStart:], fileName); err != nil { + return err + } + return nil +} + +// checkElem checks whether an individual path element is valid. +// fileName indicates whether the element is a file name (not a directory name). +func checkElem(elem string, fileName bool) error { + if elem == "" { + return fmt.Errorf("empty path element") + } + if strings.Count(elem, ".") == len(elem) { + return fmt.Errorf("invalid path element %q", elem) + } + if elem[0] == '.' && !fileName { + return fmt.Errorf("leading dot in path element") + } + if elem[len(elem)-1] == '.' { + return fmt.Errorf("trailing dot in path element") + } + + charOK := pathOK + if fileName { + charOK = fileNameOK + } + for _, r := range elem { + if !charOK(r) { + return fmt.Errorf("invalid char %q", r) + } + } + + // Windows disallows a bunch of path elements, sadly. + // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + short := elem + if i := strings.Index(short, "."); i >= 0 { + short = short[:i] + } + for _, bad := range badWindowsNames { + if strings.EqualFold(bad, short) { + return fmt.Errorf("disallowed path element %q", elem) + } + } + + // Reject path components that look like Windows short-names. + // Those usually end in a tilde followed by one or more ASCII digits. + if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 { + suffix := short[tilde+1:] + suffixIsDigits := true + for _, r := range suffix { + if r < '0' || r > '9' { + suffixIsDigits = false + break + } + } + if suffixIsDigits { + return fmt.Errorf("trailing tilde and digits in path element") + } + } + + return nil +} + +// pathOK reports whether r can appear in an import path element. +// +// NOTE: This function DIVERGES from module mode pathOK by accepting Unicode letters. +func pathOK(r rune) bool { + if r < utf8.RuneSelf { + return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || + '0' <= r && r <= '9' || + 'A' <= r && r <= 'Z' || + 'a' <= r && r <= 'z' + } + return unicode.IsLetter(r) +} + +// fileNameOK reports whether r can appear in a file name. +// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. +// If we expand the set of allowed characters here, we have to +// work harder at detecting potential case-folding and normalization collisions. +// See note about "safe encoding" below. +func fileNameOK(r rune) bool { + if r < utf8.RuneSelf { + // Entire set of ASCII punctuation, from which we remove characters: + // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + // We disallow some shell special characters: " ' * < > ? ` | + // (Note that some of those are disallowed by the Windows file system as well.) + // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). + // We allow spaces (U+0020) in file names. + const allowed = "!#$%&()+,-.=@[]^_{}~ " + if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { + return true + } + for i := 0; i < len(allowed); i++ { + if rune(allowed[i]) == r { + return true + } + } + return false + } + // It may be OK to add more ASCII punctuation here, but only carefully. + // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. + return unicode.IsLetter(r) +} + +// badWindowsNames are the reserved file path elements on Windows. +// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file +var badWindowsNames = []string{ + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +} diff --git a/src/cmd/go/internal/get/vcs.go b/src/cmd/go/internal/get/vcs.go index 5cd164f2ff..baf9b1b01e 100644 --- a/src/cmd/go/internal/get/vcs.go +++ b/src/cmd/go/internal/get/vcs.go @@ -647,14 +647,7 @@ const ( func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) { rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths) if err == errUnknownSite { - // If there are wildcards, look up the thing before the wildcard, - // hoping it applies to the wildcarded parts too. - // This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH. - lookup := strings.TrimSuffix(importPath, "/...") - if i := strings.Index(lookup, "/.../"); i >= 0 { - lookup = lookup[:i] - } - rr, err = repoRootForImportDynamic(lookup, mod, security) + rr, err = repoRootForImportDynamic(importPath, mod, security) if err != nil { err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err) } @@ -667,6 +660,7 @@ func RepoRootForImportPath(importPath string, mod ModuleMode, security web.Secur } } + // Should have been taken care of above, but make sure. if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") { // Do not allow wildcards in the repo root. rr = nil @@ -966,10 +960,14 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error) // expand rewrites s to replace {k} with match[k] for each key k in match. func expand(match map[string]string, s string) string { + // We want to replace each match exactly once, and the result of expansion + // must not depend on the iteration order through the map. + // A strings.Replacer has exactly the properties we're looking for. + oldNew := make([]string, 0, 2*len(match)) for k, v := range match { - s = strings.Replace(s, "{"+k+"}", v, -1) + oldNew = append(oldNew, "{"+k+"}", v) } - return s + return strings.NewReplacer(oldNew...).Replace(s) } // vcsPaths defines the meaning of import paths referring to diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go index 9cf0e91150..2e825e7cf7 100644 --- a/src/cmd/go/internal/modfetch/coderepo.go +++ b/src/cmd/go/internal/modfetch/coderepo.go @@ -498,6 +498,11 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error } } for _, zf := range zr.File { + if !zf.FileInfo().Mode().IsRegular() { + // Skip symlinks (golang.org/issue/27093). + continue + } + if topPrefix == "" { i := strings.Index(zf.Name, "/") if i < 0 { diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index 303e6842e7..feccf23b27 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.go @@ -78,6 +78,9 @@ func runRun(cmd *base.Command, args []string) { p = load.GoFilesPackage(files) } else if len(args) > 0 && !strings.HasPrefix(args[0], "-") { pkgs := load.PackagesAndErrors(args[:1]) + if len(pkgs) == 0 { + base.Fatalf("go run: no packages loaded from %s", args[0]) + } if len(pkgs) > 1 { var names []string for _, p := range pkgs { diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 91c4816fd0..88d9e6453f 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -434,10 +434,6 @@ func (b *Builder) build(a *Action) (err error) { return fmt.Errorf("missing or invalid binary-only package; expected file %q", a.Package.Target) } - if p.Module != nil && !allowedVersion(p.Module.GoVersion) { - return fmt.Errorf("module requires Go %s", p.Module.GoVersion) - } - if err := b.Mkdir(a.Objdir); err != nil { return err } @@ -638,12 +634,19 @@ func (b *Builder) build(a *Action) (err error) { objpkg := objdir + "_pkg_.a" ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), len(sfiles) > 0, gofiles) if len(out) > 0 { - b.showOutput(a, a.Package.Dir, a.Package.Desc(), b.processOutput(out)) + output := b.processOutput(out) + if p.Module != nil && !allowedVersion(p.Module.GoVersion) { + output += "note: module requires Go " + p.Module.GoVersion + } + b.showOutput(a, a.Package.Dir, a.Package.Desc(), output) if err != nil { return errPrintedOutput } } if err != nil { + if p.Module != nil && !allowedVersion(p.Module.GoVersion) { + b.showOutput(a, a.Package.Dir, a.Package.Desc(), "note: module requires Go "+p.Module.GoVersion) + } return err } if ofile != objpkg { diff --git a/src/cmd/go/testdata/script/get_brace.txt b/src/cmd/go/testdata/script/get_brace.txt new file mode 100644 index 0000000000..be81d8f487 --- /dev/null +++ b/src/cmd/go/testdata/script/get_brace.txt @@ -0,0 +1,49 @@ +[!exec:git] skip + +# Set up some empty repositories. +cd $WORK/_origin/foo +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git commit --allow-empty -m 'create master branch' + +cd $WORK +cd '_origin/{confusing}' +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git commit --allow-empty -m 'create master branch' + +# Clone the empty repositories into GOPATH. +# This tells the Go command where to find them: it takes the place of a user's meta-tag redirector. +mkdir $GOPATH/src/example.com +cd $GOPATH/src/example.com +exec git clone $WORK/_origin/foo +exec git clone $WORK/_origin/{confusing} + +# Commit contents to the repositories. +cd $WORK/_origin/foo +exec git add main.go +exec git commit -m 'add main' + +cd $WORK +cd '_origin/{confusing}' +exec git add confusing.go +exec git commit -m 'just try to delete this!' + +# 'go get' should refuse to download or update the confusingly-named repo. +cd $GOPATH/src/example.com/foo +! go get -u 'example.com/{confusing}' +stderr 'invalid char' +! go get -u example.com/foo +stderr 'invalid import path' +! exists example.com/{confusing} + +-- $WORK/_origin/foo/main.go -- +package main +import _ "example.com/{confusing}" + +func main() {} + +-- $WORK/_origin/{confusing}/confusing.go -- +package confusing diff --git a/src/cmd/go/testdata/script/get_dotfiles.txt b/src/cmd/go/testdata/script/get_dotfiles.txt new file mode 100644 index 0000000000..1876114362 --- /dev/null +++ b/src/cmd/go/testdata/script/get_dotfiles.txt @@ -0,0 +1,61 @@ +[!exec:git] skip + +# Set up a benign repository and a repository with a dotfile name. +cd $WORK/_origin/foo +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git commit --allow-empty -m 'create master branch' + +cd $WORK/_origin/.hidden +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git commit --allow-empty -m 'create master branch' + +# Clone the empty repositories into GOPATH. +# This tells the Go command where to find them: it takes the place of a user's meta-tag redirector. +mkdir $GOPATH/src/example.com +cd $GOPATH/src/example.com +exec git clone $WORK/_origin/foo +exec git clone $WORK/_origin/.hidden + +# Add a benign commit. +cd $WORK/_origin/foo +cp _ok/main.go main.go +exec git add main.go +exec git commit -m 'add ok' + +# 'go get' should install the benign commit. +cd $GOPATH +go get -u example.com/foo + +# Now sneak in an import of a dotfile path. +cd $WORK/_origin/.hidden +exec git add hidden.go +exec git commit -m 'nothing to see here, move along' + +cd $WORK/_origin/foo +cp _sneaky/main.go main.go +exec git add main.go +exec git commit -m 'fix typo (heh heh heh)' + +# 'go get -u' should refuse to download or update the dotfile-named repo. +cd $GOPATH/src/example.com/foo +! go get -u example.com/foo +stderr 'leading dot' +! exists example.com/.hidden/hidden.go + +-- $WORK/_origin/foo/_ok/main.go -- +package main + +func main() {} + +-- $WORK/_origin/foo/_sneaky/main.go -- +package main +import _ "example.com/.hidden" + +func main() {} + +-- $WORK/_origin/.hidden/hidden.go -- +package hidden diff --git a/src/cmd/go/testdata/script/get_tilde.txt b/src/cmd/go/testdata/script/get_tilde.txt new file mode 100644 index 0000000000..08289ca405 --- /dev/null +++ b/src/cmd/go/testdata/script/get_tilde.txt @@ -0,0 +1,21 @@ +# Paths containing windows short names should be rejected before attempting to fetch. +! go get example.com/longna~1.dir/thing +stderr 'trailing tilde and digits' +! go get example.com/longna~1/thing +stderr 'trailing tilde and digits' +! go get example.com/~9999999/thing +stderr 'trailing tilde and digits' + +# A path containing an element that is just a tilde, or a tilde followed by non-digits, +# should attempt to resolve. +! go get example.com/~glenda/notfound +! stderr 'trailing tilde and digits' +stderr 'unrecognized import path' + +! go get example.com/~glenda2/notfound +! stderr 'trailing tilde and digits' +stderr 'unrecognized import path' + +! go get example.com/~/notfound +! stderr 'trailing tilde and digits' +stderr 'unrecognized import path' diff --git a/src/cmd/go/testdata/script/get_unicode.txt b/src/cmd/go/testdata/script/get_unicode.txt new file mode 100644 index 0000000000..31edcdb9f6 --- /dev/null +++ b/src/cmd/go/testdata/script/get_unicode.txt @@ -0,0 +1,37 @@ +[!exec:git] skip + +# Construct a repository that imports a non-ASCII path. +cd $WORK/_origin/example.com/unicode +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git add unicode.go +exec git commit -m 'add unicode.go' + +# Clone the repo into GOPATH so that 'go get -u' can find it. +mkdir $GOPATH/src/example.com/unicode +cd $GOPATH/src/example.com/unicode +exec git clone $WORK/_origin/example.com/unicode . + +# Construct the imported repository. +cd $WORK/_origin/example.com/испытание +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git add испытание.go +exec git commit -m 'add испытание.go' + +# Clone that repo into GOPATH too. +mkdir $GOPATH/src/example.com/испытание +cd $GOPATH/src/example.com/испытание +exec git clone $WORK/_origin/example.com/испытание . + +# Upgrading the importer should pull from the non-ASCII repo. +cd $GOPATH +go get -u example.com/unicode + +-- $WORK/_origin/example.com/unicode/unicode.go -- +package unicode +import _ "example.com/испытание" +-- $WORK/_origin/example.com/испытание/испытание.go -- +package испытание diff --git a/src/cmd/go/testdata/script/mod_go_version.txt b/src/cmd/go/testdata/script/mod_go_version.txt index f2de74cee8..37f173531b 100644 --- a/src/cmd/go/testdata/script/mod_go_version.txt +++ b/src/cmd/go/testdata/script/mod_go_version.txt @@ -3,9 +3,10 @@ env GO111MODULE=on go list -! go build -stderr 'module requires Go 1.999' +go build go build sub.1 +go build subver.1 +! stderr 'module requires' ! go build badsub.1 stderr 'module requires Go 1.11111' @@ -19,11 +20,13 @@ module m go 1.999 require ( sub.1 v1.0.0 + subver.1 v1.0.0 badsub.1 v1.0.0 versioned.1 v1.0.0 ) replace ( sub.1 => ./sub + subver.1 => ./subver badsub.1 => ./badsub versioned.1 v1.0.0 => ./versioned1 versioned.1 v1.1.0 => ./versioned2 @@ -39,12 +42,20 @@ go 1.11 -- sub/x.go -- package x +-- subver/go.mod -- +module m +go 1.11111 + +-- subver/x.go -- +package x + -- badsub/go.mod -- module m go 1.11111 -- badsub/x.go -- package x +invalid syntax -- versioned1/go.mod -- module versioned @@ -59,3 +70,4 @@ go 1.99999 -- versioned2/x.go -- package x +invalid syntax diff --git a/src/cmd/go/testdata/script/run_wildcard.txt b/src/cmd/go/testdata/script/run_wildcard.txt new file mode 100644 index 0000000000..cd401e00e6 --- /dev/null +++ b/src/cmd/go/testdata/script/run_wildcard.txt @@ -0,0 +1,5 @@ +# Fix for https://github.com/golang/go/issues/28696: +# go run x/... should not panic when directory x doesn't exist. + +! go run nonexistent/... +stderr '^go run: no packages loaded from nonexistent/...$' diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 2329ca6379..eeb1f00057 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -983,6 +983,7 @@ func hostobjCopy() (paths []string) { if err != nil { Exitf("cannot reopen %s: %v", h.pn, err) } + defer f.Close() if _, err := f.Seek(h.off, 0); err != nil { Exitf("cannot seek %s: %v", h.pn, err) } diff --git a/src/crypto/x509/cert_pool.go b/src/crypto/x509/cert_pool.go index a1646b9826..61107f86b8 100644 --- a/src/crypto/x509/cert_pool.go +++ b/src/crypto/x509/cert_pool.go @@ -65,32 +65,16 @@ func SystemCertPool() (*CertPool, error) { return loadSystemRoots() } -// findVerifiedParents attempts to find certificates in s which have signed the -// given certificate. If any candidates were rejected then errCert will be set -// to one of them, arbitrarily, and err will contain the reason that it was -// rejected. -func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) { +// findPotentialParents returns the indexes of certificates in s which might +// have signed cert. The caller must not modify the returned slice. +func (s *CertPool) findPotentialParents(cert *Certificate) []int { if s == nil { - return + return nil } - var candidates []int - if len(cert.AuthorityKeyId) > 0 { - candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)] - } - if len(candidates) == 0 { - candidates = s.byName[string(cert.RawIssuer)] + return s.bySubjectKeyId[string(cert.AuthorityKeyId)] } - - for _, c := range candidates { - if err = cert.CheckSignatureFrom(s.certs[c]); err == nil { - parents = append(parents, c) - } else { - errCert = s.certs[c] - } - } - - return + return s.byName[string(cert.RawIssuer)] } func (s *CertPool) contains(cert *Certificate) bool { diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 5a5e6abaf1..122a7c6cbf 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -777,7 +777,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e if opts.Roots.contains(c) { candidateChains = append(candidateChains, []*Certificate{c}) } else { - if candidateChains, err = c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts); err != nil { + if candidateChains, err = c.buildChains(nil, []*Certificate{c}, nil, &opts); err != nil { return nil, err } } @@ -814,58 +814,74 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate return n } -func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { - possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c) -nextRoot: - for _, rootNum := range possibleRoots { - root := opts.Roots.certs[rootNum] +// maxChainSignatureChecks is the maximum number of CheckSignatureFrom calls +// that an invocation of buildChains will (tranistively) make. Most chains are +// less than 15 certificates long, so this leaves space for multiple chains and +// for failed checks due to different intermediates having the same Subject. +const maxChainSignatureChecks = 100 +func (c *Certificate) buildChains(cache map[*Certificate][][]*Certificate, currentChain []*Certificate, sigChecks *int, opts *VerifyOptions) (chains [][]*Certificate, err error) { + var ( + hintErr error + hintCert *Certificate + ) + + considerCandidate := func(certType int, candidate *Certificate) { for _, cert := range currentChain { - if cert.Equal(root) { - continue nextRoot + if cert.Equal(candidate) { + return } } - err = root.isValid(rootCertificate, currentChain, opts) - if err != nil { - continue + if sigChecks == nil { + sigChecks = new(int) + } + *sigChecks++ + if *sigChecks > maxChainSignatureChecks { + err = errors.New("x509: signature check attempts limit reached while verifying certificate chain") + return } - chains = append(chains, appendToFreshChain(currentChain, root)) - } - possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c) -nextIntermediate: - for _, intermediateNum := range possibleIntermediates { - intermediate := opts.Intermediates.certs[intermediateNum] - for _, cert := range currentChain { - if cert.Equal(intermediate) { - continue nextIntermediate + if err := c.CheckSignatureFrom(candidate); err != nil { + if hintErr == nil { + hintErr = err + hintCert = candidate } + return } - err = intermediate.isValid(intermediateCertificate, currentChain, opts) + + err = candidate.isValid(certType, currentChain, opts) if err != nil { - continue + return } - var childChains [][]*Certificate - childChains, ok := cache[intermediateNum] - if !ok { - childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts) - cache[intermediateNum] = childChains + + switch certType { + case rootCertificate: + chains = append(chains, appendToFreshChain(currentChain, candidate)) + case intermediateCertificate: + if cache == nil { + cache = make(map[*Certificate][][]*Certificate) + } + childChains, ok := cache[candidate] + if !ok { + childChains, err = candidate.buildChains(cache, appendToFreshChain(currentChain, candidate), sigChecks, opts) + cache[candidate] = childChains + } + chains = append(chains, childChains...) } - chains = append(chains, childChains...) + } + + for _, rootNum := range opts.Roots.findPotentialParents(c) { + considerCandidate(rootCertificate, opts.Roots.certs[rootNum]) + } + for _, intermediateNum := range opts.Intermediates.findPotentialParents(c) { + considerCandidate(intermediateCertificate, opts.Intermediates.certs[intermediateNum]) } if len(chains) > 0 { err = nil } - if len(chains) == 0 && err == nil { - hintErr := rootErr - hintCert := failedRoot - if hintErr == nil { - hintErr = intermediateErr - hintCert = failedIntermediate - } err = UnknownAuthorityError{c, hintErr, hintCert} } diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index 0e24d3b5da..85f4703d4c 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -5,10 +5,15 @@ package x509 import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "crypto/x509/pkix" "encoding/pem" "errors" "fmt" + "math/big" "runtime" "strings" "testing" @@ -1889,3 +1894,117 @@ func TestValidHostname(t *testing.T) { } } } + +func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.PrivateKey) (*Certificate, crypto.PrivateKey, error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, err + } + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit) + + template := &Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{CommonName: cn}, + NotBefore: time.Now().Add(-1 * time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + + KeyUsage: KeyUsageKeyEncipherment | KeyUsageDigitalSignature | KeyUsageCertSign, + ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: isCA, + } + if issuer == nil { + issuer = template + issuerKey = priv + } + + derBytes, err := CreateCertificate(rand.Reader, template, issuer, priv.Public(), issuerKey) + if err != nil { + return nil, nil, err + } + cert, err := ParseCertificate(derBytes) + if err != nil { + return nil, nil, err + } + + return cert, priv, nil +} + +func TestPathologicalChain(t *testing.T) { + if testing.Short() { + t.Skip("skipping generation of a long chain of certificates in short mode") + } + + // Build a chain where all intermediates share the same subject, to hit the + // path building worst behavior. + roots, intermediates := NewCertPool(), NewCertPool() + + parent, parentKey, err := generateCert("Root CA", true, nil, nil) + if err != nil { + t.Fatal(err) + } + roots.AddCert(parent) + + for i := 1; i < 100; i++ { + parent, parentKey, err = generateCert("Intermediate CA", true, parent, parentKey) + if err != nil { + t.Fatal(err) + } + intermediates.AddCert(parent) + } + + leaf, _, err := generateCert("Leaf", false, parent, parentKey) + if err != nil { + t.Fatal(err) + } + + start := time.Now() + _, err = leaf.Verify(VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + }) + t.Logf("verification took %v", time.Since(start)) + + if err == nil || !strings.Contains(err.Error(), "signature check attempts limit") { + t.Errorf("expected verification to fail with a signature checks limit error; got %v", err) + } +} + +func TestLongChain(t *testing.T) { + if testing.Short() { + t.Skip("skipping generation of a long chain of certificates in short mode") + } + + roots, intermediates := NewCertPool(), NewCertPool() + + parent, parentKey, err := generateCert("Root CA", true, nil, nil) + if err != nil { + t.Fatal(err) + } + roots.AddCert(parent) + + for i := 1; i < 15; i++ { + name := fmt.Sprintf("Intermediate CA #%d", i) + parent, parentKey, err = generateCert(name, true, parent, parentKey) + if err != nil { + t.Fatal(err) + } + intermediates.AddCert(parent) + } + + leaf, _, err := generateCert("Leaf", false, parent, parentKey) + if err != nil { + t.Fatal(err) + } + + start := time.Now() + if _, err := leaf.Verify(VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + }); err != nil { + t.Error(err) + } + t.Logf("verification took %v", time.Since(start)) +} diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 5cbaba187b..6646711e7f 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -538,7 +538,25 @@ func (check *Checker) packageObjects() { // pre-allocate space for type declaration paths so that the underlying array is reused typePath := make([]*TypeName, 0, 8) + // We process non-alias declarations first, in order to avoid situations where + // the type of an alias declaration is needed before it is available. In general + // this is still not enough, as it is possible to create sufficiently convoluted + // recursive type definitions that will cause a type alias to be needed before it + // is available (see issue #25838 for examples). + // As an aside, the cmd/compiler suffers from the same problem (#25838). + var aliasList []*TypeName + // phase 1 for _, obj := range objList { + // If we have a type alias, collect it for the 2nd phase. + if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].alias { + aliasList = append(aliasList, tname) + continue + } + + check.objDecl(obj, nil, typePath) + } + // phase 2 + for _, obj := range aliasList { check.objDecl(obj, nil, typePath) } diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index 229d203099..ce6c60624f 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -176,6 +176,7 @@ func TestStdFixed(t *testing.T) { "issue22200b.go", // go/types does not have constraints on stack size "issue25507.go", // go/types does not have constraints on stack size "issue20780.go", // go/types does not have constraints on stack size + "issue27232.go", // go/types has a bug with alias type (issue #28576) ) } diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index 12cf65f109..2cd2b86df2 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -7705,6 +7705,7 @@ func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterRe default: } if err != nil { + cc.forgetStreamID(cs.ID) return nil, cs.getStartedWrite(), err } bodyWritten = true @@ -7826,6 +7827,7 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos sawEOF = true err = nil } else if err != nil { + cc.writeStreamReset(cs.ID, http2ErrCodeCancel, err) return err } diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index c85033f4bc..64a80ad1fd 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -130,12 +130,19 @@ func cgocall(fn, arg unsafe.Pointer) int32 { mp.incgo = true errno := asmcgocall(fn, arg) - // Call endcgo before exitsyscall because exitsyscall may + // Update accounting before exitsyscall because exitsyscall may // reschedule us on to a different M. - endcgo(mp) + mp.incgo = false + mp.ncgo-- exitsyscall() + // Note that raceacquire must be called only after exitsyscall has + // wired this M to a P. + if raceenabled { + raceacquire(unsafe.Pointer(&racecgosync)) + } + // From the garbage collector's perspective, time can move // backwards in the sequence above. If there's a callback into // Go code, GC will see this function at the call to @@ -153,16 +160,6 @@ func cgocall(fn, arg unsafe.Pointer) int32 { return errno } -//go:nosplit -func endcgo(mp *m) { - mp.incgo = false - mp.ncgo-- - - if raceenabled { - raceacquire(unsafe.Pointer(&racecgosync)) - } -} - // Call from C back to Go. //go:nosplit func cgocallbackg(ctxt uintptr) { @@ -346,13 +343,14 @@ func unwindm(restore *bool) { sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16)) } - // Call endcgo to do the accounting that cgocall will not have a - // chance to do during an unwind. + // Do the accounting that cgocall will not have a chance to do + // during an unwind. // // In the case where a Go call originates from C, ncgo is 0 // and there is no matching cgocall to end. if mp.ncgo > 0 { - endcgo(mp) + mp.incgo = false + mp.ncgo-- } releasem(mp) diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index 28a0319f10..59f3df47fe 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -29,7 +29,17 @@ func archauxv(tag, val uintptr) { randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 case _AT_HWCAP: - cpu_hwcap = uint(val) + // arm64 doesn't have a 'cpuid' instruction equivalent and relies on + // HWCAP/HWCAP2 bits for hardware capabilities. + hwcap := uint(val) + if GOOS == "android" { + // The Samsung S9+ kernel reports support for atomics, but not all cores + // actually support them, resulting in SIGILL. See issue #28431. + // TODO(elias.naur): Only disable the optimization on bad chipsets. + const hwcap_ATOMICS = 1 << 8 + hwcap &= ^uint(hwcap_ATOMICS) + } + cpu_hwcap = hwcap case _AT_HWCAP2: cpu_hwcap2 = uint(val) } diff --git a/src/runtime/stack.go b/src/runtime/stack.go index c7bfc0434b..7bb82038ab 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -1221,7 +1221,14 @@ func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args // Arguments. if frame.arglen > 0 { if frame.argmap != nil { + // argmap is set when the function is reflect.makeFuncStub or reflect.methodValueCall. + // In this case, arglen specifies how much of the args section is actually live. + // (It could be either all the args + results, or just the args.) args = *frame.argmap + n := int32(frame.arglen / sys.PtrSize) + if n < args.n { + args.n = n // Don't use more of the arguments than arglen. + } } else { stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) if stackmap == nil || stackmap.n <= 0 { diff --git a/test/fixedbugs/issue18640.go b/test/fixedbugs/issue18640.go index 60abd31f76..091bbe596b 100644 --- a/test/fixedbugs/issue18640.go +++ b/test/fixedbugs/issue18640.go @@ -20,8 +20,7 @@ type ( d = c ) -// The compiler reports an incorrect (non-alias related) -// type cycle here (via dowith()). Disabled for now. +// The compiler cannot handle these cases. Disabled for now. // See issue #25838. /* type ( @@ -32,7 +31,6 @@ type ( i = j j = e ) -*/ type ( a1 struct{ *b1 } @@ -45,3 +43,4 @@ type ( b2 = c2 c2 struct{ *b2 } ) +*/ diff --git a/test/fixedbugs/issue23823.go b/test/fixedbugs/issue23823.go index 2f802d0988..9297966cbd 100644 --- a/test/fixedbugs/issue23823.go +++ b/test/fixedbugs/issue23823.go @@ -1,4 +1,4 @@ -// errorcheck +// compile // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -6,10 +6,14 @@ package p +// The compiler cannot handle this. Disabled for now. +// See issue #25838. +/* type I1 = interface { I2 } -type I2 interface { // ERROR "invalid recursive type" +type I2 interface { I1 } +*/ diff --git a/test/fixedbugs/issue24939.go b/test/fixedbugs/issue24939.go index 26530e95b2..0ae6f2b9f2 100644 --- a/test/fixedbugs/issue24939.go +++ b/test/fixedbugs/issue24939.go @@ -15,7 +15,9 @@ type M interface { } type P = interface { - I() M + // The compiler cannot handle this case. Disabled for now. + // See issue #25838. + // I() M } func main() {} diff --git a/test/fixedbugs/issue27232.go b/test/fixedbugs/issue27232.go new file mode 100644 index 0000000000..3a1cc87e4c --- /dev/null +++ b/test/fixedbugs/issue27232.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type F = func(T) + +type T interface { + m(F) +} + +type t struct{} + +func (t) m(F) {} + +var _ T = &t{} diff --git a/test/fixedbugs/issue27267.go b/test/fixedbugs/issue27267.go new file mode 100644 index 0000000000..ebae44f48f --- /dev/null +++ b/test/fixedbugs/issue27267.go @@ -0,0 +1,21 @@ +// compile + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// 1st test case from issue +type F = func(E) // compiles if not type alias or moved below E struct +type E struct { + f F +} + +var x = E{func(E) {}} + +// 2nd test case from issue +type P = *S +type S struct { + p P +} diff --git a/test/fixedbugs/issue27695c.go b/test/fixedbugs/issue27695c.go new file mode 100644 index 0000000000..948191cc96 --- /dev/null +++ b/test/fixedbugs/issue27695c.go @@ -0,0 +1,65 @@ +// run + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure return values aren't scanned until they +// are initialized, when calling functions and methods +// via reflect. + +package main + +import ( + "io" + "reflect" + "runtime" + "unsafe" +) + +var badPtr uintptr + +var sink []byte + +func init() { + // Allocate large enough to use largeAlloc. + b := make([]byte, 1<<16-1) + sink = b // force heap allocation + // Any space between the object and the end of page is invalid to point to. + badPtr = uintptr(unsafe.Pointer(&b[len(b)-1])) + 1 +} + +func f(d func(error) error) error { + // Initialize callee args section with a bad pointer. + g(badPtr, badPtr, badPtr, badPtr) + + // Then call a function which returns a pointer. + // That return slot starts out holding a bad pointer. + return d(io.EOF) +} + +//go:noinline +func g(x, y, z, w uintptr) { +} + +type T struct { +} + +func (t *T) Foo(e error) error { + runtime.GC() + return e +} + +func main() { + // Functions + d := reflect.MakeFunc(reflect.TypeOf(func(e error) error { return e }), + func(args []reflect.Value) []reflect.Value { + runtime.GC() + return args + }).Interface().(func(error) error) + f(d) + + // Methods + x := reflect.ValueOf(&T{}).Method(0).Interface().(func(error) error) + f(x) +} diff --git a/test/fixedbugs/issue28616.go b/test/fixedbugs/issue28616.go new file mode 100644 index 0000000000..f1ba974797 --- /dev/null +++ b/test/fixedbugs/issue28616.go @@ -0,0 +1,25 @@ +// compile + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure we don't dead code eliminate a label. + +package p + +var i int + +func f() { + + if true { + + if i == 1 { + goto label + } + + return + } + +label: +} diff --git a/test/fixedbugs/issue28688.go b/test/fixedbugs/issue28688.go new file mode 100644 index 0000000000..0d2000e149 --- /dev/null +++ b/test/fixedbugs/issue28688.go @@ -0,0 +1,31 @@ +// run -gcflags=-d=softfloat + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +// When using soft-float, OMUL might be rewritten to function +// call so we should ensure it was evaluated first. Stack frame +// setup for "test" function call should happen after call to runtime.fmul32 + +var x int32 = 1 + +func main() { + var y float32 = 1.0 + test(x, y*y) +} + +//go:noinline +func test(id int32, a float32) { + + if id != x { + fmt.Printf("got: %d, want: %d\n", id, x) + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue28797.go b/test/fixedbugs/issue28797.go new file mode 100644 index 0000000000..480c1059b8 --- /dev/null +++ b/test/fixedbugs/issue28797.go @@ -0,0 +1,53 @@ +// run + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +// test expects f to panic, but not to run out of memory, +// which is a non-panic fatal error. OOM results from failure +// to properly check negative limit. +func test(f func()) { + defer func() { + r := recover() + if r == nil { + panic("panic wasn't recoverable") + } + }() + f() +} + +//go:noinline +func id(x int) int { + return x +} + +func main() { + test(foo) + test(bar) +} + +func foo() { + b := make([]byte, 0) + b = append(b, 1) + id(len(b)) + id(len(b) - 2) + s := string(b[1 : len(b)-2]) + fmt.Println(s) +} + +func bar() { + b := make([]byte, 1) + b = append(b, 1) + i := id(-1) + if i < len(b) { // establish value is not too large. + s := string(b[1:i]) // should check for negative also. + fmt.Println(s) + } +} diff --git a/test/loopbce.go b/test/loopbce.go index b4bf797497..81f2524e95 100644 --- a/test/loopbce.go +++ b/test/loopbce.go @@ -6,7 +6,7 @@ package main func f0a(a []int) int { x := 0 for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - x += a[i] // ERROR "Proved IsInBounds$" + x += a[i] // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -14,7 +14,7 @@ func f0a(a []int) int { func f0b(a []int) int { x := 0 for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - b := a[i:] // ERROR "Proved IsSliceInBounds$" + b := a[i:] // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" x += b[0] } return x @@ -23,7 +23,7 @@ func f0b(a []int) int { func f0c(a []int) int { x := 0 for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - b := a[:i+1] // ERROR "Proved IsSliceInBounds$" + b := a[:i+1] // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" x += b[0] } return x @@ -40,7 +40,7 @@ func f1(a []int) int { func f2(a []int) int { x := 0 for i := 1; i < len(a); i++ { // ERROR "Induction variable: limits \[1,\?\), increment 1$" - x += a[i] // ERROR "Proved IsInBounds$" + x += a[i] // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -48,7 +48,7 @@ func f2(a []int) int { func f4(a [10]int) int { x := 0 for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" - x += a[i] // ERROR "Proved IsInBounds$" + x += a[i] // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -63,7 +63,7 @@ func f5(a [10]int) int { func f6(a []int) { for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - b := a[0:i] // ERROR "Proved IsSliceInBounds$" + b := a[0:i] // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" "(\([0-9]+\) )?Proved Geq64$" f6(b) } } @@ -71,7 +71,7 @@ func f6(a []int) { func g0a(a string) int { x := 0 for i := 0; i < len(a); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - x += int(a[i]) // ERROR "Proved IsInBounds$" + x += int(a[i]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -79,7 +79,7 @@ func g0a(a string) int { func g0b(a string) int { x := 0 for i := 0; len(a) > i; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - x += int(a[i]) // ERROR "Proved IsInBounds$" + x += int(a[i]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -87,7 +87,7 @@ func g0b(a string) int { func g0c(a string) int { x := 0 for i := len(a); i > 0; i-- { // ERROR "Induction variable: limits \(0,\?\], increment 1$" - x += int(a[i-1]) // ERROR "Proved IsInBounds$" + x += int(a[i-1]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -95,7 +95,7 @@ func g0c(a string) int { func g0d(a string) int { x := 0 for i := len(a); 0 < i; i-- { // ERROR "Induction variable: limits \(0,\?\], increment 1$" - x += int(a[i-1]) // ERROR "Proved IsInBounds$" + x += int(a[i-1]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -103,7 +103,7 @@ func g0d(a string) int { func g0e(a string) int { x := 0 for i := len(a) - 1; i >= 0; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1$" - x += int(a[i]) // ERROR "Proved IsInBounds$" + x += int(a[i]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -111,7 +111,7 @@ func g0e(a string) int { func g0f(a string) int { x := 0 for i := len(a) - 1; 0 <= i; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1$" - x += int(a[i]) // ERROR "Proved IsInBounds$" + x += int(a[i]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -120,7 +120,7 @@ func g1() int { a := "evenlength" x := 0 for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" - x += int(a[i]) // ERROR "Proved IsInBounds$" + x += int(a[i]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x } @@ -130,7 +130,7 @@ func g2() int { x := 0 for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" j := i - if a[i] == 'e' { // ERROR "Proved IsInBounds$" + if a[i] == 'e' { // ERROR "(\([0-9]+\) )?Proved IsInBounds$" j = j + 1 } x += int(a[j]) @@ -141,27 +141,27 @@ func g2() int { func g3a() { a := "this string has length 25" for i := 0; i < len(a); i += 5 { // ERROR "Induction variable: limits \[0,25\), increment 5$" - useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" useString(a[:i+3]) } } func g3b(a string) { for i := 0; i < len(a); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - useString(a[i+1:]) // ERROR "Proved IsSliceInBounds$" + useString(a[i+1:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" } } func g3c(a string) { for i := 0; i < len(a); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - useString(a[:i+1]) // ERROR "Proved IsSliceInBounds$" + useString(a[:i+1]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" } } func h1(a []byte) { c := a[:128] for i := range c { // ERROR "Induction variable: limits \[0,128\), increment 1$" - c[i] = byte(i) // ERROR "Proved IsInBounds$" + c[i] = byte(i) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } } @@ -174,11 +174,11 @@ func h2(a []byte) { func k0(a [100]int) [100]int { for i := 10; i < 90; i++ { // ERROR "Induction variable: limits \[10,90\), increment 1$" a[i-11] = i - a[i-10] = i // ERROR "Proved IsInBounds$" - a[i-5] = i // ERROR "Proved IsInBounds$" - a[i] = i // ERROR "Proved IsInBounds$" - a[i+5] = i // ERROR "Proved IsInBounds$" - a[i+10] = i // ERROR "Proved IsInBounds$" + a[i-10] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" + a[i-5] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" + a[i] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" + a[i+5] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" + a[i+10] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" a[i+11] = i } return a @@ -186,13 +186,13 @@ func k0(a [100]int) [100]int { func k1(a [100]int) [100]int { for i := 10; i < 90; i++ { // ERROR "Induction variable: limits \[10,90\), increment 1$" - useSlice(a[:i-11]) - useSlice(a[:i-10]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[:i-5]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[:i]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[:i+5]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[:i+10]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[:i+11]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i-11]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[:i-10]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[:i-5]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[:i]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" "(\([0-9]+\) )?Proved Geq64$" + useSlice(a[:i+5]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[:i+10]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[:i+11]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" useSlice(a[:i+12]) } @@ -202,12 +202,12 @@ func k1(a [100]int) [100]int { func k2(a [100]int) [100]int { for i := 10; i < 90; i++ { // ERROR "Induction variable: limits \[10,90\), increment 1$" useSlice(a[i-11:]) - useSlice(a[i-10:]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[i-5:]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[i:]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[i+5:]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[i+10:]) // ERROR "Proved IsSliceInBounds$" - useSlice(a[i+11:]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[i-10:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[i-5:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[i+5:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[i+10:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useSlice(a[i+11:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" useSlice(a[i+12:]) } return a @@ -216,7 +216,7 @@ func k2(a [100]int) [100]int { func k3(a [100]int) [100]int { for i := -10; i < 90; i++ { // ERROR "Induction variable: limits \[-10,90\), increment 1$" a[i+9] = i - a[i+10] = i // ERROR "Proved IsInBounds$" + a[i+10] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" a[i+11] = i } return a @@ -225,7 +225,7 @@ func k3(a [100]int) [100]int { func k3neg(a [100]int) [100]int { for i := 89; i > -11; i-- { // ERROR "Induction variable: limits \(-11,89\], increment 1$" a[i+9] = i - a[i+10] = i // ERROR "Proved IsInBounds$" + a[i+10] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" a[i+11] = i } return a @@ -234,7 +234,7 @@ func k3neg(a [100]int) [100]int { func k3neg2(a [100]int) [100]int { for i := 89; i >= -10; i-- { // ERROR "Induction variable: limits \[-10,89\], increment 1$" a[i+9] = i - a[i+10] = i // ERROR "Proved IsInBounds$" + a[i+10] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" a[i+11] = i } return a @@ -243,16 +243,16 @@ func k3neg2(a [100]int) [100]int { func k4(a [100]int) [100]int { min := (-1) << 63 for i := min; i < min+50; i++ { // ERROR "Induction variable: limits \[-9223372036854775808,-9223372036854775758\), increment 1$" - a[i-min] = i // ERROR "Proved IsInBounds$" + a[i-min] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return a } func k5(a [100]int) [100]int { max := (1 << 63) - 1 - for i := max - 50; i < max; i++ { // ERROR "Induction variable: limits \[9223372036854775757,9223372036854775807\), increment 1" - a[i-max+50] = i // ERROR "Proved IsInBounds$" - a[i-(max-70)] = i // ERROR "Proved IsInBounds$" + for i := max - 50; i < max; i++ { // ERROR "Induction variable: limits \[9223372036854775757,9223372036854775807\), increment 1$" + a[i-max+50] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" + a[i-(max-70)] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return a } @@ -275,17 +275,17 @@ func nobce1() { func nobce2(a string) { for i := int64(0); i < int64(len(a)); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" } for i := int64(0); i < int64(len(a))-31337; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" } for i := int64(0); i < int64(len(a))+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" } j := int64(len(a)) - 123 for i := int64(0); i < j+123+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" - useString(a[i:]) // ERROR "Proved IsSliceInBounds$" + useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" } for i := int64(0); i < j+122+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" // len(a)-123+122+MinInt overflows when len(a) == 0, so a bound check is needed here diff --git a/test/prove.go b/test/prove.go index 79256893b3..0de6bd63b4 100644 --- a/test/prove.go +++ b/test/prove.go @@ -62,7 +62,7 @@ func f1c(a []int, i int64) int { } func f2(a []int) int { - for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1" + for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" a[i+1] = i a[i+1] = i // ERROR "Proved IsInBounds$" } @@ -269,7 +269,7 @@ func f11b(a []int, i int) { func f11c(a []int, i int) { useSlice(a[:i]) - useSlice(a[:i]) // ERROR "Proved IsSliceInBounds$" + useSlice(a[:i]) // ERROR "Proved Geq64$" "Proved IsSliceInBounds$" } func f11d(a []int, i int) { @@ -464,12 +464,12 @@ func f16(s []int) []int { } func f17(b []int) { - for i := 0; i < len(b); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1" + for i := 0; i < len(b); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" // This tests for i <= cap, which we can only prove // using the derived relation between len and cap. // This depends on finding the contradiction, since we // don't query this condition directly. - useSlice(b[:i]) // ERROR "Proved IsSliceInBounds$" + useSlice(b[:i]) // ERROR "Proved Geq64$" "Proved IsSliceInBounds$" } } @@ -579,18 +579,18 @@ func fence4(x, y int64) { func trans1(x, y int64) { if x > 5 { if y > x { - if y > 2 { // ERROR "Proved Greater64" + if y > 2 { // ERROR "Proved Greater64$" return } } else if y == x { - if y > 5 { // ERROR "Proved Greater64" + if y > 5 { // ERROR "Proved Greater64$" return } } } if x >= 10 { if y > x { - if y > 10 { // ERROR "Proved Greater64" + if y > 10 { // ERROR "Proved Greater64$" return } } @@ -624,7 +624,7 @@ func natcmp(x, y []uint) (r int) { } i := m - 1 - for i > 0 && // ERROR "Induction variable: limits \(0,\?\], increment 1" + for i > 0 && // ERROR "Induction variable: limits \(0,\?\], increment 1$" x[i] == // ERROR "Proved IsInBounds$" y[i] { // ERROR "Proved IsInBounds$" i-- @@ -686,7 +686,7 @@ func range2(b [][32]int) { if i < len(b) { // ERROR "Proved Less64$" println("x") } - if i >= 0 { // ERROR "Proved Geq64" + if i >= 0 { // ERROR "Proved Geq64$" println("x") } } diff --git a/test/run.go b/test/run.go index 99ef79feb1..5a81da8d67 100644 --- a/test/run.go +++ b/test/run.go @@ -1180,7 +1180,7 @@ func (t *test) updateErrors(out, file string) { msg := errStr[colon2+2:] msg = strings.Replace(msg, file, base, -1) // normalize file mentions in error itself msg = strings.TrimLeft(msg, " \t") - for _, r := range []string{`\`, `*`, `+`, `[`, `]`, `(`, `)`} { + for _, r := range []string{`\`, `*`, `+`, `?`, `[`, `]`, `(`, `)`} { msg = strings.Replace(msg, r, `\`+r, -1) } msg = strings.Replace(msg, `"`, `.`, -1) |