diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules index 4380a5efef..1d3e738c7f 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules @@ -249,7 +249,7 @@ (Leq64F ...) => (FLED ...) (Leq32F ...) => (FLES ...) -(EqPtr x y) => (SEQZ (SUB x y)) +(EqPtr x y) => (SEQZ (SUB x y)) (Eq64 x y) => (SEQZ (SUB x y)) (Eq32 x y) => (SEQZ (SUBW x y)) (Eq16 x y) => (SEQZ (SUB (ZeroExt16to64 x) (ZeroExt16to64 y))) @@ -257,7 +257,7 @@ (Eq64F ...) => (FEQD ...) (Eq32F ...) => (FEQS ...) -(NeqPtr x y) => (SNEZ (SUB x y)) +(NeqPtr x y) => (SNEZ (SUB x y)) (Neq64 x y) => (SNEZ (SUB x y)) (Neq32 x y) => (SNEZ (SUBW x y)) (Neq16 x y) => (SNEZ (SUB (ZeroExt16to64 x) (ZeroExt16to64 y))) diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index fb507b65c4..a34b59df91 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -960,13 +960,14 @@ func rewriteValueRISCV64_OpEqPtr(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + typ := &b.Func.Config.Types // match: (EqPtr x y) - // result: (SEQZ (SUB x y)) + // result: (SEQZ (SUB x y)) for { x := v_0 y := v_1 v.reset(OpRISCV64SEQZ) - v0 := b.NewValue0(v.Pos, OpRISCV64SUB, x.Type) + v0 := b.NewValue0(v.Pos, OpRISCV64SUB, typ.Uintptr) v0.AddArg2(x, y) v.AddArg(v0) return true @@ -2519,13 +2520,14 @@ func rewriteValueRISCV64_OpNeqPtr(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + typ := &b.Func.Config.Types // match: (NeqPtr x y) - // result: (SNEZ (SUB x y)) + // result: (SNEZ (SUB x y)) for { x := v_0 y := v_1 v.reset(OpRISCV64SNEZ) - v0 := b.NewValue0(v.Pos, OpRISCV64SUB, x.Type) + v0 := b.NewValue0(v.Pos, OpRISCV64SUB, typ.Uintptr) v0.AddArg2(x, y) v.AddArg(v0) return true diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go index db7496faf6..ef0acfe965 100644 --- a/src/cmd/go/internal/modfetch/coderepo.go +++ b/src/cmd/go/internal/modfetch/coderepo.go @@ -305,17 +305,46 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e // // (If the version is +incompatible, then the go.mod file must not exist: // +incompatible is not an ongoing opt-out from semantic import versioning.) - var canUseIncompatible func() bool - canUseIncompatible = func() bool { - var ok bool - if r.codeDir == "" && r.pathMajor == "" { + incompatibleOk := map[string]bool{} + canUseIncompatible := func(v string) bool { + if r.codeDir != "" || r.pathMajor != "" { + // A non-empty codeDir indicates a module within a subdirectory, + // which necessarily has a go.mod file indicating the module boundary. + // A non-empty pathMajor indicates a module path with a major-version + // suffix, which must match. + return false + } + + ok, seen := incompatibleOk[""] + if !seen { _, errGoMod := r.code.ReadFile(info.Name, "go.mod", codehost.MaxGoMod) - if errGoMod != nil { - ok = true + ok = (errGoMod != nil) + incompatibleOk[""] = ok + } + if !ok { + // A go.mod file exists at the repo root. + return false + } + + // Per https://go.dev/issue/51324, previous versions of the 'go' command + // didn't always check for go.mod files in subdirectories, so if the user + // requests a +incompatible version explicitly, we should continue to allow + // it. Otherwise, if vN/go.mod exists, expect that release tags for that + // major version are intended for the vN module. + if v != "" && !strings.HasSuffix(statVers, "+incompatible") { + major := semver.Major(v) + ok, seen = incompatibleOk[major] + if !seen { + _, errGoModSub := r.code.ReadFile(info.Name, path.Join(major, "go.mod"), codehost.MaxGoMod) + ok = (errGoModSub != nil) + incompatibleOk[major] = ok + } + if !ok { + return false } } - canUseIncompatible = func() bool { return ok } - return ok + + return true } // checkCanonical verifies that the canonical version v is compatible with the @@ -367,7 +396,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e base := strings.TrimSuffix(v, "+incompatible") var errIncompatible error if !module.MatchPathMajor(base, r.pathMajor) { - if canUseIncompatible() { + if canUseIncompatible(base) { v = base + "+incompatible" } else { if r.pathMajor != "" { @@ -495,7 +524,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e // Save the highest non-retracted canonical tag for the revision. // If we don't find a better match, we'll use it as the canonical version. if tagIsCanonical && semver.Compare(highestCanonical, v) < 0 && !isRetracted(v) { - if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible() { + if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible(v) { highestCanonical = v } } @@ -513,12 +542,12 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e // retracted versions. allowedMajor := func(major string) func(v string) bool { return func(v string) bool { - return (major == "" || semver.Major(v) == major) && !isRetracted(v) + return ((major == "" && canUseIncompatible(v)) || semver.Major(v) == major) && !isRetracted(v) } } if pseudoBase == "" { var tag string - if r.pseudoMajor != "" || canUseIncompatible() { + if r.pseudoMajor != "" || canUseIncompatible("") { tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor(r.pseudoMajor)) } else { // Allow either v1 or v0, but not incompatible higher versions. diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go index d98ea87da2..bb9268adb8 100644 --- a/src/cmd/go/internal/modfetch/coderepo_test.go +++ b/src/cmd/go/internal/modfetch/coderepo_test.go @@ -458,6 +458,54 @@ var codeRepoTests = []codeRepoTest{ rev: "v3.0.0-devel", err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`, }, + + // If v2/go.mod exists, then we should prefer to match the "v2" + // pseudo-versions to the nested module, and resolve the module in the parent + // directory to only compatible versions. + // + // However (https://go.dev/issue/51324), previous versions of the 'go' command + // didn't always do so, so if the user explicitly requests a +incompatible + // version (as would be present in an existing go.mod file), we should + // continue to allow it. + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "80beb17a1603", + version: "v0.0.0-20220222205507-80beb17a1603", + name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5", + short: "80beb17a1603", + time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC), + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.0", + err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`, + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.1-0.20220222205507-80beb17a1603", + err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`, + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.0+incompatible", + version: "v2.0.0+incompatible", + name: "5fcd3eaeeb391d399f562fd45a50dac9fc34ae8b", + short: "5fcd3eaeeb39", + time: time.Date(2022, 2, 22, 20, 53, 33, 0, time.UTC), + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.1-0.20220222205507-80beb17a1603+incompatible", + version: "v2.0.1-0.20220222205507-80beb17a1603+incompatible", + name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5", + short: "80beb17a1603", + time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC), + }, } func TestCodeRepo(t *testing.T) { diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index d7db0c8133..0aa9ad7b93 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -29,6 +29,10 @@ const ( // to be used as a useTCP parameter to exchange useTCPOnly = true useUDPOrTCP = false + + // Maximum DNS packet size. + // Value taken from https://dnsflagday.net/2020/. + maxDNSPacketSize = 1232 ) var ( @@ -81,7 +85,7 @@ func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) return dnsmessage.Parser{}, dnsmessage.Header{}, err } - b = make([]byte, 512) // see RFC 1035 + b = make([]byte, maxDNSPacketSize) for { n, err := c.Read(b) if err != nil { diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index e7f7621a09..e8afbbe29e 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -881,7 +881,7 @@ func (f *fakeDNSPacketConn) Close() error { func TestIgnoreDNSForgeries(t *testing.T) { c, s := Pipe() go func() { - b := make([]byte, 512) + b := make([]byte, maxDNSPacketSize) n, err := s.Read(b) if err != nil { t.Error(err) diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go index 7b4030935a..d7cf2afa5e 100644 --- a/src/regexp/syntax/parse.go +++ b/src/regexp/syntax/parse.go @@ -76,13 +76,29 @@ const ( opVerticalBar ) +// maxHeight is the maximum height of a regexp parse tree. +// It is somewhat arbitrarily chosen, but the idea is to be large enough +// that no one will actually hit in real use but at the same time small enough +// that recursion on the Regexp tree will not hit the 1GB Go stack limit. +// The maximum amount of stack for a single recursive frame is probably +// closer to 1kB, so this could potentially be raised, but it seems unlikely +// that people have regexps nested even this deeply. +// We ran a test on Google's C++ code base and turned up only +// a single use case with depth > 100; it had depth 128. +// Using depth 1000 should be plenty of margin. +// As an optimization, we don't even bother calculating heights +// until we've allocated at least maxHeight Regexp structures. +const maxHeight = 1000 + type parser struct { flags Flags // parse mode flags stack []*Regexp // stack of parsed expressions free *Regexp numCap int // number of capturing groups seen wholeRegexp string - tmpClass []rune // temporary char class work space + tmpClass []rune // temporary char class work space + numRegexp int // number of regexps allocated + height map[*Regexp]int // regexp height for height limit check } func (p *parser) newRegexp(op Op) *Regexp { @@ -92,16 +108,52 @@ func (p *parser) newRegexp(op Op) *Regexp { *re = Regexp{} } else { re = new(Regexp) + p.numRegexp++ } re.Op = op return re } func (p *parser) reuse(re *Regexp) { + if p.height != nil { + delete(p.height, re) + } re.Sub0[0] = p.free p.free = re } +func (p *parser) checkHeight(re *Regexp) { + if p.numRegexp < maxHeight { + return + } + if p.height == nil { + p.height = make(map[*Regexp]int) + for _, re := range p.stack { + p.checkHeight(re) + } + } + if p.calcHeight(re, true) > maxHeight { + panic(ErrInternalError) + } +} + +func (p *parser) calcHeight(re *Regexp, force bool) int { + if !force { + if h, ok := p.height[re]; ok { + return h + } + } + h := 1 + for _, sub := range re.Sub { + hsub := p.calcHeight(sub, false) + if h < 1+hsub { + h = 1 + hsub + } + } + p.height[re] = h + return h +} + // Parse stack manipulation. // push pushes the regexp re onto the parse stack and returns the regexp. @@ -137,6 +189,7 @@ func (p *parser) push(re *Regexp) *Regexp { } p.stack = append(p.stack, re) + p.checkHeight(re) return re } @@ -246,6 +299,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( re.Sub = re.Sub0[:1] re.Sub[0] = sub p.stack[n-1] = re + p.checkHeight(re) if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) { return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} @@ -693,6 +747,21 @@ func literalRegexp(s string, flags Flags) *Regexp { // Flags, and returns a regular expression parse tree. The syntax is // described in the top-level comment. func Parse(s string, flags Flags) (*Regexp, error) { + return parse(s, flags) +} + +func parse(s string, flags Flags) (_ *Regexp, err error) { + defer func() { + switch r := recover(); r { + default: + panic(r) + case nil: + // ok + case ErrInternalError: + err = &Error{Code: ErrInternalError, Expr: s} + } + }() + if flags&Literal != 0 { // Trivial parser for literal string. if err := checkUTF8(s); err != nil { @@ -704,7 +773,6 @@ func Parse(s string, flags Flags) (*Regexp, error) { // Otherwise, must do real work. var ( p parser - err error c rune op Op lastRepeat string diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go index 5581ba1ca5..1ef6d8a3fe 100644 --- a/src/regexp/syntax/parse_test.go +++ b/src/regexp/syntax/parse_test.go @@ -207,6 +207,11 @@ var parseTests = []parseTest{ // Valid repetitions. {`((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))`, ``}, {`((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})`, ``}, + + // Valid nesting. + {strings.Repeat("(", 999) + strings.Repeat(")", 999), ``}, + {strings.Repeat("(?:", 999) + strings.Repeat(")*", 999), ``}, + {"(" + strings.Repeat("|", 12345) + ")", ``}, // not nested at all } const testFlags = MatchNL | PerlX | UnicodeGroups @@ -482,6 +487,8 @@ var invalidRegexps = []string{ `a{100000}`, `a{100000,}`, "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", + strings.Repeat("(", 1000) + strings.Repeat(")", 1000), + strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), `\Q\E*`, } diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 59f72ae709..79f13d1b4b 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -1226,3 +1226,5 @@ func (th *TimeHistogram) Count(bucket, subBucket uint) (uint64, bool) { func (th *TimeHistogram) Record(duration int64) { (*timeHistogram)(th).record(duration) } + +var TimeHistogramMetricsBuckets = timeHistogramMetricsBuckets diff --git a/src/runtime/histogram.go b/src/runtime/histogram.go index da4910d341..1b45ad97df 100644 --- a/src/runtime/histogram.go +++ b/src/runtime/histogram.go @@ -47,7 +47,7 @@ const ( // │ └---- Next 4 bits -> sub-bucket 1 // └------- Bit 5 set -> super-bucket 2 // - // Following this pattern, bucket 45 will have the bit 48 set. We don't + // Following this pattern, super-bucket 44 will have the bit 47 set. We don't // have any buckets for higher values, so the highest sub-bucket will // contain values of 2^48-1 nanoseconds or approx. 3 days. This range is // more than enough to handle durations produced by the runtime. @@ -135,36 +135,30 @@ func float64NegInf() float64 { func timeHistogramMetricsBuckets() []float64 { b := make([]float64, timeHistTotalBuckets+1) b[0] = float64NegInf() - for i := 0; i < timeHistNumSuperBuckets; i++ { - superBucketMin := uint64(0) - // The (inclusive) minimum for the first non-negative bucket is 0. - if i > 0 { - // The minimum for the second bucket will be - // 1 << timeHistSubBucketBits, indicating that all - // sub-buckets are represented by the next timeHistSubBucketBits - // bits. - // Thereafter, we shift up by 1 each time, so we can represent - // this pattern as (i-1)+timeHistSubBucketBits. - superBucketMin = uint64(1) << uint(i-1+timeHistSubBucketBits) - } - // subBucketShift is the amount that we need to shift the sub-bucket - // index to combine it with the bucketMin. - subBucketShift := uint(0) - if i > 1 { - // The first two super buckets are exact with respect to integers, - // so we'll never have to shift the sub-bucket index. Thereafter, - // we shift up by 1 with each subsequent bucket. - subBucketShift = uint(i - 2) - } + // Super-bucket 0 has no bits above timeHistSubBucketBits + // set, so just iterate over each bucket and assign the + // incrementing bucket. + for i := 0; i < timeHistNumSubBuckets; i++ { + bucketNanos := uint64(i) + b[i+1] = float64(bucketNanos) / 1e9 + } + // Generate the rest of the super-buckets. It's easier to reason + // about if we cut out the 0'th bucket, so subtract one since + // we just handled that bucket. + for i := 0; i < timeHistNumSuperBuckets-1; i++ { for j := 0; j < timeHistNumSubBuckets; j++ { - // j is the sub-bucket index. By shifting the index into position to - // combine with the bucket minimum, we obtain the minimum value for that - // sub-bucket. - subBucketMin := superBucketMin + (uint64(j) << subBucketShift) - - // Convert the subBucketMin which is in nanoseconds to a float64 seconds value. + // Set the super-bucket bit. + bucketNanos := uint64(1) << (i + timeHistSubBucketBits) + // Set the sub-bucket bits. + bucketNanos |= uint64(j) << i + // The index for this bucket is going to be the (i+1)'th super bucket + // (note that we're starting from zero, but handled the first super-bucket + // earlier, so we need to compensate), and the j'th sub bucket. + // Add 1 because we left space for -Inf. + bucketIndex := (i+1)*timeHistNumSubBuckets + j + 1 + // Convert nanoseconds to seconds via a division. // These values will all be exactly representable by a float64. - b[i*timeHistNumSubBuckets+j+1] = float64(subBucketMin) / 1e9 + b[bucketIndex] = float64(bucketNanos) / 1e9 } } b[len(b)-1] = float64Inf() diff --git a/src/runtime/histogram_test.go b/src/runtime/histogram_test.go index dbc64fa559..b12b65a41e 100644 --- a/src/runtime/histogram_test.go +++ b/src/runtime/histogram_test.go @@ -68,3 +68,43 @@ func TestTimeHistogram(t *testing.T) { dummyTimeHistogram = TimeHistogram{} } + +func TestTimeHistogramMetricsBuckets(t *testing.T) { + buckets := TimeHistogramMetricsBuckets() + + nonInfBucketsLen := TimeHistNumSubBuckets * TimeHistNumSuperBuckets + expBucketsLen := nonInfBucketsLen + 2 // Count -Inf and +Inf. + if len(buckets) != expBucketsLen { + t.Fatalf("unexpected length of buckets: got %d, want %d", len(buckets), expBucketsLen) + } + // Check the first non-Inf 2*TimeHistNumSubBuckets buckets in order, skipping the + // first bucket which should be -Inf (checked later). + // + // Because of the way this scheme works, the bottom TimeHistNumSubBuckets + // buckets are fully populated, and then the next TimeHistNumSubBuckets + // have the TimeHistSubBucketBits'th bit set, while the bottom are once + // again fully populated. + for i := 1; i <= 2*TimeHistNumSubBuckets+1; i++ { + if got, want := buckets[i], float64(i-1)/1e9; got != want { + t.Errorf("expected bucket %d to have value %e, got %e", i, want, got) + } + } + // Check some values. + idxToBucket := map[int]float64{ + 0: math.Inf(-1), + 33: float64(0x10<<1) / 1e9, + 34: float64(0x11<<1) / 1e9, + 49: float64(0x10<<2) / 1e9, + 58: float64(0x19<<2) / 1e9, + 65: float64(0x10<<3) / 1e9, + 513: float64(0x10<<31) / 1e9, + 519: float64(0x16<<31) / 1e9, + expBucketsLen - 2: float64(0x1f<<43) / 1e9, + expBucketsLen - 1: math.Inf(1), + } + for idx, bucket := range idxToBucket { + if got, want := buckets[idx], bucket; got != want { + t.Errorf("expected bucket %d to have value %e, got %e", idx, want, got) + } + } +} diff --git a/test/fixedbugs/issue51101.go b/test/fixedbugs/issue51101.go new file mode 100644 index 0000000000..a390e50da4 --- /dev/null +++ b/test/fixedbugs/issue51101.go @@ -0,0 +1,36 @@ +// run + +// Copyright 2022 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. + +// Issue 51101: on RISCV64, difference of two pointers +// was marked as pointer and crashes GC. + +package main + +var a, b int + +func main() { + F(&b, &a) +} + +//go:noinline +func F(a, b *int) bool { + x := a == b + G(x) + y := a != b + return y +} + +//go:noinline +func G(bool) { + grow([1000]int{20}) +} + +func grow(x [1000]int) { + if x[0] != 0 { + x[0]-- + grow(x) + } +}