[dev.simd] all: merge master (740857f) into dev.simd

Merge List:

+ 2025-06-30 740857f529 runtime: stash allpSnapshot on the M
+ 2025-06-30 9ae38be302 sync: disassociate WaitGroups from bubbles on Wait
+ 2025-06-30 4731832342 crypto/hmac: wrap ErrUnsupported returned by Clone
+ 2025-06-30 03ad694dcb runtime: update skips for TestGdbBacktrace
+ 2025-06-30 9d1cd0b881 iter: add missing type parameter in doc
+ 2025-06-29 acb914f2c2 cmd/doc: fix -http on Windows
+ 2025-06-27 b51f1cdb87 runtime: remove arbitrary 5-second timeout in TestNeedmDeadlock
+ 2025-06-27 f1e6ae2f6f reflect: fix TypeAssert on nil interface values
+ 2025-06-27 e81c624656 os: use minimal file permissions when opening parent directory in RemoveAll
+ 2025-06-27 2a22aefa1f encoding/json: add security section to doc
+ 2025-06-27 742fda9524 runtime: account for missing frame pointer in preamble
+ 2025-06-27 fdc076ce76 net/http: fix RoundTrip context cancellation for js/wasm
+ 2025-06-27 d9d2cadd63 encoding/json: fix typo in hotlink for jsontext.PreserveRawStrings
+ 2025-06-26 0f8ab2db17 cmd/link: permit a larger size BSS reference to a smaller DATA symbol
+ 2025-06-26 988a20c8c5 cmd/compile/internal/escape: evaluate any side effects when rewriting with literals
+ 2025-06-25 b5d555991a encoding/json/jsontext: remove Encoder.UnusedBuffer
+ 2025-06-25 0b4d2eab2f encoding/json/jsontext: rename Encoder.UnusedBuffer as Encoder.AvailableBuffer

Change-Id: Iea44ab825bdf087fbe7570df8d2d66d1d3327c31
This commit is contained in:
Cherry Mui 2025-06-30 15:10:56 -04:00
commit f849225b3b
39 changed files with 537 additions and 139 deletions

View file

@ -42,6 +42,8 @@ func TestASAN(t *testing.T) {
{src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"},
{src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"},
{src: "asan_global5.go"},
{src: "asan_global_asm"},
{src: "asan_global_asm2_fail", memoryAccessError: "global-buffer-overflow", errorLocation: "main.go:17"},
{src: "arena_fail.go", memoryAccessError: "use-after-poison", errorLocation: "arena_fail.go:26", experiments: []string{"arenas"}},
}
for _, tc := range cases {

View file

@ -554,7 +554,7 @@ func (c *config) checkRuntime() (skip bool, err error) {
// srcPath returns the path to the given file relative to this test's source tree.
func srcPath(path string) string {
return filepath.Join("testdata", path)
return "./testdata/" + path
}
// A tempDir manages a temporary directory within a test.

View file

@ -0,0 +1,8 @@
// Copyright 2025 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.
#include "textflag.h"
DATA ·x(SB)/8, $123
GLOBL ·x(SB), NOPTR, $8

View file

@ -0,0 +1,11 @@
// Copyright 2025 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
var x uint64
func main() {
println(x)
}

View file

@ -0,0 +1,8 @@
// Copyright 2025 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.
#include "textflag.h"
DATA ·x(SB)/8, $123
GLOBL ·x(SB), NOPTR, $8

View file

@ -0,0 +1,20 @@
// Copyright 2025 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 "unsafe"
var x uint64
func main() {
bar(&x)
}
func bar(a *uint64) {
p := (*uint64)(unsafe.Add(unsafe.Pointer(a), 1*unsafe.Sizeof(uint64(1))))
if *p == 10 { // BOOM
println("its value is 10")
}
}

View file

@ -545,6 +545,14 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
}
assignTemp := func(n ir.Node, init *ir.Nodes) {
// Preserve any side effects of n by assigning it to an otherwise unused temp.
pos := n.Pos()
tmp := typecheck.TempAt(pos, fn, n.Type())
init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, n)))
}
switch n.Op() {
case ir.OMAKESLICE:
// Check if we can replace a non-constant argument to make with
@ -556,13 +564,17 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
r = &n.Len
}
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
lit, ok := s.(*ir.BasicLit)
if !ok || lit.Val().Kind() != constant.Int {
base.Fatalf("unexpected BasicLit Kind")
}
if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
*r = lit
if (*r).Op() != ir.OLITERAL {
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
lit, ok := s.(*ir.BasicLit)
if !ok || lit.Val().Kind() != constant.Int {
base.Fatalf("unexpected BasicLit Kind")
}
if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
// Preserve any side effects of the original expression, then replace it.
assignTemp(*r, n.PtrInit())
*r = lit
}
}
}
case ir.OCONVIFACE:
@ -575,6 +587,8 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
if base.Debug.EscapeDebug >= 3 {
base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
}
// Preserve any side effects of the original expression, then replace it.
assignTemp(conv.X, conv.PtrInit())
v := v.(*ir.BasicLit)
conv.X = ir.NewBasicLit(conv.X.Pos(), conv.X.Type(), v.Val())
typecheck.Expr(conv)

View file

@ -405,6 +405,8 @@
//
// go doc
// Show documentation for current package.
// go doc -http
// Serve HTML documentation over HTTP for the current package.
// go doc Foo
// Show documentation for Foo in the current package.
// (Foo starts with a capital letter so it cannot match
@ -439,26 +441,28 @@
//
// Flags:
//
// -all
// Show all the documentation for the package.
// -c
// Respect case when matching symbols.
// -cmd
// Treat a command (package main) like a regular package.
// Otherwise package main's exported symbols are hidden
// when showing the package's top-level documentation.
// -short
// One-line representation for each symbol.
// -src
// Show the full source code for the symbol. This will
// display the full Go source of its declaration and
// definition, such as a function definition (including
// the body), type declaration or enclosing const
// block. The output may therefore include unexported
// details.
// -u
// Show documentation for unexported as well as exported
// symbols, methods, and fields.
// -all
// Show all the documentation for the package.
// -c
// Respect case when matching symbols.
// -cmd
// Treat a command (package main) like a regular package.
// Otherwise package main's exported symbols are hidden
// when showing the package's top-level documentation.
// -http
// Serve HTML docs over HTTP.
// -short
// One-line representation for each symbol.
// -src
// Show the full source code for the symbol. This will
// display the full Go source of its declaration and
// definition, such as a function definition (including
// the body), type declaration or enclosing const
// block. The output may therefore include unexported
// details.
// -u
// Show documentation for unexported as well as exported
// symbols, methods, and fields.
//
// # Print Go environment information
//

View file

@ -75,6 +75,8 @@ different cases. If this occurs, documentation for all matches is printed.
Examples:
go doc
Show documentation for current package.
go doc -http
Serve HTML documentation over HTTP for the current package.
go doc Foo
Show documentation for Foo in the current package.
(Foo starts with a capital letter so it cannot match
@ -116,6 +118,8 @@ Flags:
Treat a command (package main) like a regular package.
Otherwise package main's exported symbols are hidden
when showing the package's top-level documentation.
-http
Serve HTML docs over HTTP.
-short
One-line representation for each symbol.
-src

View file

@ -227,8 +227,16 @@ func doPkgsite(urlPath string) error {
fields := strings.Fields(vars)
if err == nil && len(fields) == 2 {
goproxy, gomodcache := fields[0], fields[1]
goproxy = "file://" + filepath.Join(gomodcache, "cache", "download") + "," + goproxy
env = append(env, "GOPROXY="+goproxy)
gomodcache = filepath.Join(gomodcache, "cache", "download")
// Convert absolute path to file URL. pkgsite will not accept
// Windows absolute paths because they look like a host:path remote.
// TODO(golang.org/issue/32456): use url.FromFilePath when implemented.
if strings.HasPrefix(gomodcache, "/") {
gomodcache = "file://" + gomodcache
} else {
gomodcache = "file:///" + filepath.ToSlash(gomodcache)
}
env = append(env, "GOPROXY="+gomodcache+","+goproxy)
}
const version = "v0.0.0-20250608123103-82c52f1754cd"

View file

@ -253,6 +253,12 @@ type Loader struct {
WasmExports []Sym
// sizeFixups records symbols that we need to fix up the size
// after loading. It is very rarely needed, only for a DATA symbol
// and a BSS symbol with the same name, and the BSS symbol has
// larger size.
sizeFixups []symAndSize
flags uint32
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
@ -469,18 +475,17 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in
// In summary, the "overwrite" variable and the final result are
//
// new sym old sym result
// ---------------------------------------------
// -------------------------------------------------------
// TEXT BSS new wins
// DATA DATA ERROR
// DATA lg/eq BSS sm/eq new wins
// DATA small BSS large ERROR
// BSS large DATA small ERROR
// DATA small BSS large merge: new with larger size
// BSS large DATA small merge: old with larger size
// BSS large BSS small new wins
// BSS sm/eq D/B lg/eq old wins
// BSS TEXT old wins
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
oldIsText := oldtyp.IsText()
newIsText := newtyp.IsText()
oldHasContent := oldr.DataSize(oldli) != 0
newHasContent := r.DataSize(li) != 0
@ -488,12 +493,28 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in
newIsBSS := newtyp.IsData() && !newHasContent
switch {
case newIsText && oldIsBSS,
newHasContent && oldIsBSS && sz >= oldsz,
newHasContent && oldIsBSS,
newIsBSS && oldIsBSS && sz > oldsz:
// new symbol overwrites old symbol.
l.objSyms[oldi] = objSym{r.objidx, li}
case newIsBSS && (oldsz >= sz || oldIsText):
if oldsz > sz {
// If the BSS symbol has a larger size, expand the data
// symbol's size so access from the BSS side cannot overrun.
// It is hard to modify the symbol size until all Go objects
// (potentially read-only) are loaded, so we record it in
// a fixup table and apply them later. This is very rare.
// One case is a global variable with a Go declaration and an
// assembly definition, which typically have the same size,
// but in ASAN mode the Go declaration has a larger size due
// to the inserted red zone.
l.sizeFixups = append(l.sizeFixups, symAndSize{oldi, uint32(oldsz)})
}
case newIsBSS:
// old win, just ignore the new symbol.
if sz > oldsz {
// See the comment above for sizeFixups.
l.sizeFixups = append(l.sizeFixups, symAndSize{oldi, uint32(sz)})
}
default:
log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz)
}
@ -2277,6 +2298,10 @@ func (l *Loader) LoadSyms(arch *sys.Arch) {
st.preloadSyms(r, hashedDef)
st.preloadSyms(r, nonPkgDef)
}
for _, sf := range l.sizeFixups {
pp := l.cloneToExternal(sf.sym)
pp.size = int64(sf.size)
}
for _, vr := range st.linknameVarRefs {
l.checkLinkname(vr.pkg, vr.name, vr.sym)
}
@ -2498,7 +2523,7 @@ func topLevelSym(sname string, skind sym.SymKind) bool {
// a symbol originally discovered as part of an object file, it's
// easier to do this if we make the updates to an external symbol
// payload.
func (l *Loader) cloneToExternal(symIdx Sym) {
func (l *Loader) cloneToExternal(symIdx Sym) *extSymPayload {
if l.IsExternal(symIdx) {
panic("sym is already external, no need for clone")
}
@ -2550,6 +2575,8 @@ func (l *Loader) cloneToExternal(symIdx Sym) {
// Some attributes were encoded in the object file. Copy them over.
l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
l.SetAttrShared(symIdx, r.Shared())
return pp
}
// Copy the payload of symbol src to dst. Both src and dst must be external

View file

@ -11,6 +11,7 @@ import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
"hash"
"testing"
@ -583,6 +584,18 @@ func TestHMAC(t *testing.T) {
}
}
func TestNoClone(t *testing.T) {
h := New(func() hash.Hash { return justHash{sha256.New()} }, []byte("key"))
if _, ok := h.(hash.Cloner); !ok {
t.Skip("no Cloner support")
}
h.Write([]byte("test"))
_, err := h.(hash.Cloner).Clone()
if !errors.Is(err, errors.ErrUnsupported) {
t.Errorf("Clone() = %v, want ErrUnsupported", err)
}
}
func TestNonUniqueHash(t *testing.T) {
if boring.Enabled {
t.Skip("hash.Hash provided by boringcrypto are not comparable")

View file

@ -130,26 +130,36 @@ func (h *HMAC) Reset() {
h.marshaled = true
}
type errCloneUnsupported struct{}
func (e errCloneUnsupported) Error() string {
return "crypto/hmac: hash does not support hash.Cloner"
}
func (e errCloneUnsupported) Unwrap() error {
return errors.ErrUnsupported
}
// Clone implements [hash.Cloner] if the underlying hash does.
// Otherwise, it returns [errors.ErrUnsupported].
// Otherwise, it returns an error wrapping [errors.ErrUnsupported].
func (h *HMAC) Clone() (hash.Cloner, error) {
r := *h
ic, ok := h.inner.(hash.Cloner)
if !ok {
return nil, errors.ErrUnsupported
return nil, errCloneUnsupported{}
}
oc, ok := h.outer.(hash.Cloner)
if !ok {
return nil, errors.ErrUnsupported
return nil, errCloneUnsupported{}
}
var err error
r.inner, err = ic.Clone()
if err != nil {
return nil, errors.ErrUnsupported
return nil, errCloneUnsupported{}
}
r.outer, err = oc.Clone()
if err != nil {
return nil, errors.ErrUnsupported
return nil, errCloneUnsupported{}
}
return &r, nil
}

View file

@ -43,11 +43,14 @@ import (
// and the input is a JSON quoted string, Unmarshal calls
// [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string.
//
// To unmarshal JSON into a struct, Unmarshal matches incoming object
// keys to the keys used by [Marshal] (either the struct field name or its tag),
// preferring an exact match but also accepting a case-insensitive match. By
// default, object keys which don't have a corresponding struct field are
// ignored (see [Decoder.DisallowUnknownFields] for an alternative).
// To unmarshal JSON into a struct, Unmarshal matches incoming object keys to
// the keys used by [Marshal] (either the struct field name or its tag),
// ignoring case. If multiple struct fields match an object key, an exact case
// match is preferred over a case-insensitive one.
//
// Incoming object members are processed in the order observed. If an object
// includes duplicate keys, later duplicates will replace or be merged into
// prior values.
//
// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:

View file

@ -4,12 +4,44 @@
//go:build !goexperiment.jsonv2
// Package json implements encoding and decoding of JSON as defined in
// RFC 7159. The mapping between JSON and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
// Package json implements encoding and decoding of JSON as defined in RFC 7159.
// The mapping between JSON and Go values is described in the documentation for
// the Marshal and Unmarshal functions.
//
// See "JSON and Go" for an introduction to this package:
// https://golang.org/doc/articles/json_and_go.html
//
// # Security Considerations
//
// The JSON standard (RFC 7159) is lax in its definition of a number of parser
// behaviors. As such, many JSON parsers behave differently in various
// scenarios. These differences in parsers mean that systems that use multiple
// independent JSON parser implementations may parse the same JSON object in
// differing ways.
//
// Systems that rely on a JSON object being parsed consistently for security
// purposes should be careful to understand the behaviors of this parser, as
// well as how these behaviors may cause interoperability issues with other
// parser implementations.
//
// Due to the Go Backwards Compatibility promise (https://go.dev/doc/go1compat)
// there are a number of behaviors this package exhibits that may cause
// interopability issues, but cannot be changed. In particular the following
// parsing behaviors may cause issues:
//
// - If a JSON object contains duplicate keys, keys are processed in the order
// they are observed, meaning later values will replace or be merged into
// prior values, depending on the field type (in particular maps and structs
// will have values merged, while other types have values replaced).
// - When parsing a JSON object into a Go struct, keys are considered in a
// case-insensitive fashion.
// - When parsing a JSON object into a Go struct, unknown keys in the JSON
// object are ignored (unless a [Decoder] is used and
// [Decoder.DisallowUnknownFields] has been called).
// - Invalid UTF-8 bytes in JSON strings are replaced by the Unicode
// replacement character.
// - Large JSON number integers will lose precision when unmarshaled into
// floating-point types.
package json
import (

View file

@ -74,8 +74,8 @@ type encodeBuffer struct {
// maxValue is the approximate maximum Value size passed to WriteValue.
maxValue int
// unusedCache is the buffer returned by the UnusedBuffer method.
unusedCache []byte
// availBuffer is the buffer returned by the AvailableBuffer method.
availBuffer []byte // always has zero length
// bufStats is statistics about buffer utilization.
// It is only used with pooled encoders in pools.go.
bufStats bufferStatistics
@ -465,9 +465,9 @@ func (e *encoderState) AppendRaw(k Kind, safeASCII bool, appendFn func([]byte) (
isVerbatim := safeASCII || !jsonwire.NeedEscape(b[pos+len(`"`):len(b)-len(`"`)])
if !isVerbatim {
var err error
b2 := append(e.unusedCache, b[pos+len(`"`):len(b)-len(`"`)]...)
b2 := append(e.availBuffer, b[pos+len(`"`):len(b)-len(`"`)]...)
b, err = jsonwire.AppendQuote(b[:pos], string(b2), &e.Flags)
e.unusedCache = b2[:0]
e.availBuffer = b2[:0]
if err != nil {
return wrapSyntacticError(e, err, pos, +1)
}
@ -900,20 +900,20 @@ func (e *Encoder) OutputOffset() int64 {
return e.s.previousOffsetEnd()
}
// UnusedBuffer returns a zero-length buffer with a possible non-zero capacity.
// AvailableBuffer returns a zero-length buffer with a possible non-zero capacity.
// This buffer is intended to be used to populate a [Value]
// being passed to an immediately succeeding [Encoder.WriteValue] call.
//
// Example usage:
//
// b := d.UnusedBuffer()
// b := d.AvailableBuffer()
// b = append(b, '"')
// b = appendString(b, v) // append the string formatting of v
// b = append(b, '"')
// ... := d.WriteValue(b)
//
// It is the user's responsibility to ensure that the value is valid JSON.
func (e *Encoder) UnusedBuffer() []byte {
func (e *Encoder) AvailableBuffer() []byte {
// NOTE: We don't return e.buf[len(e.buf):cap(e.buf)] since WriteValue would
// need to take special care to avoid mangling the data while reformatting.
// WriteValue can't easily identify whether the input Value aliases e.buf
@ -921,10 +921,10 @@ func (e *Encoder) UnusedBuffer() []byte {
// Should this ever alias e.buf, we need to consider how it operates with
// the specialized performance optimization for bytes.Buffer.
n := 1 << bits.Len(uint(e.s.maxValue|63)) // fast approximation for max length
if cap(e.s.unusedCache) < n {
e.s.unusedCache = make([]byte, 0, n)
if cap(e.s.availBuffer) < n {
e.s.availBuffer = make([]byte, 0, n)
}
return e.s.unusedCache
return e.s.availBuffer
}
// StackDepth returns the depth of the state machine for written JSON data.

View file

@ -113,7 +113,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
mk := newAddressableValue(m.Type().Key())
mv := newAddressableValue(m.Type().Elem())
marshalKey := func(mk addressableValue) error {
b, err := jsonwire.AppendQuote(enc.UnusedBuffer(), mk.String(), &mo.Flags)
b, err := jsonwire.AppendQuote(enc.AvailableBuffer(), mk.String(), &mo.Flags)
if err != nil {
return newMarshalErrorBefore(enc, m.Type().Key(), err)
}

View file

@ -199,7 +199,7 @@ func (n Number) MarshalJSONTo(enc *jsontext.Encoder) error {
}
n = cmp.Or(n, "0")
var num []byte
val := enc.UnusedBuffer()
val := enc.AvailableBuffer()
if stringify {
val = append(val, '"')
val = append(val, n...)

View file

@ -221,7 +221,7 @@ type Options = jsonopts.Options
// - [jsontext.AllowInvalidUTF8]
// - [jsontext.EscapeForHTML]
// - [jsontext.EscapeForJS]
// - [jsontext.PreserveRawString]
// - [jsontext.PreserveRawStrings]
//
// All other boolean options are set to false.
// All non-boolean options are set to the zero value,

View file

@ -57,13 +57,14 @@ type Hash64 interface {
Sum64() uint64
}
// A Cloner is a hash function whose state can be cloned.
// A Cloner is a hash function whose state can be cloned, returning a value with
// equivalent and independent state.
//
// All [Hash] implementations in the standard library implement this interface,
// unless GOFIPS140=v1.0.0 is set.
//
// If a hash can only determine at runtime if it can be cloned,
// (e.g., if it wraps another hash), it may return [errors.ErrUnsupported].
// If a hash can only determine at runtime if it can be cloned (e.g. if it wraps
// another hash), it may return an error wrapping [errors.ErrUnsupported].
type Cloner interface {
Hash
Clone() (Cloner, error)

View file

@ -654,6 +654,17 @@ func TestWaitGroupInBubble(t *testing.T) {
})
}
// https://go.dev/issue/74386
func TestWaitGroupRacingAdds(t *testing.T) {
synctest.Run(func() {
var wg sync.WaitGroup
for range 100 {
wg.Go(func() {})
}
wg.Wait()
})
}
func TestWaitGroupOutOfBubble(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
@ -705,29 +716,35 @@ func TestWaitGroupMovedBetweenBubblesWithNonZeroCount(t *testing.T) {
})
}
func TestWaitGroupMovedBetweenBubblesWithZeroCount(t *testing.T) {
func TestWaitGroupDisassociateInWait(t *testing.T) {
var wg sync.WaitGroup
synctest.Run(func() {
wg.Add(1)
wg.Done()
// Count and waiters are 0, so Wait disassociates the WaitGroup.
wg.Wait()
})
synctest.Run(func() {
// Reusing the WaitGroup is safe, because its count is zero.
// Reusing the WaitGroup is safe, because it is no longer bubbled.
wg.Add(1)
wg.Done()
})
}
func TestWaitGroupMovedBetweenBubblesAfterWait(t *testing.T) {
func TestWaitGroupDisassociateInAdd(t *testing.T) {
var wg sync.WaitGroup
synctest.Run(func() {
wg.Go(func() {})
wg.Wait()
wg.Add(1)
go wg.Wait()
synctest.Wait() // wait for Wait to block
// Count is 0 and waiters != 0, so Done wakes the waiters and
// disassociates the WaitGroup.
wg.Done()
})
synctest.Run(func() {
// Reusing the WaitGroup is safe, because its count is zero.
wg.Go(func() {})
wg.Wait()
// Reusing the WaitGroup is safe, because it is no longer bubbled.
wg.Add(1)
wg.Done()
})
}

View file

@ -97,6 +97,11 @@ func main() {
rp.Read(data[:])
pipeReadDone <- true
}()
go func() { // func12
for {
syncPreemptPoint()
}
}()
time.Sleep(100 * time.Millisecond)
runtime.GC()
@ -127,3 +132,12 @@ func main() {
runtime.GOMAXPROCS(oldGoMaxProcs)
}
//go:noinline
func syncPreemptPoint() {
if never {
syncPreemptPoint()
}
}
var never bool

View file

@ -326,7 +326,8 @@ func TestTraceStacks(t *testing.T) {
const mainLine = 21
want := []evDesc{
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
{"main.main", mainLine + 82},
{"runtime.Gosched", 0},
{"main.main", mainLine + 87},
}},
{trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{
{"main.main", mainLine + 11},
@ -349,7 +350,7 @@ func TestTraceStacks(t *testing.T) {
}},
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
{"runtime.chansend1", 0},
{"main.main", mainLine + 84},
{"main.main", mainLine + 89},
}},
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
{"runtime.chansend1", 0},
@ -357,7 +358,7 @@ func TestTraceStacks(t *testing.T) {
}},
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
{"runtime.chanrecv1", 0},
{"main.main", mainLine + 85},
{"main.main", mainLine + 90},
}},
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
{"runtime.selectgo", 0},
@ -365,7 +366,7 @@ func TestTraceStacks(t *testing.T) {
}},
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
{"runtime.selectgo", 0},
{"main.main", mainLine + 86},
{"main.main", mainLine + 91},
}},
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
{"sync.(*Mutex).Lock", 0},
@ -382,7 +383,7 @@ func TestTraceStacks(t *testing.T) {
{trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{
{"sync.(*WaitGroup).Add", 0},
{"sync.(*WaitGroup).Done", 0},
{"main.main", mainLine + 91},
{"main.main", mainLine + 96},
}},
{trace.EventStateTransition, "Goroutine Running->Waiting", []frame{
{"sync.(*Cond).Wait", 0},
@ -402,6 +403,10 @@ func TestTraceStacks(t *testing.T) {
{"runtime.GOMAXPROCS", 0},
{"main.main", 0},
}},
{trace.EventStateTransition, "Goroutine Running->Runnable", []frame{
{"main.syncPreemptPoint", 0},
{"main.main.func12", 0},
}},
}
if !stress {
// Only check for this stack if !stress because traceAdvance alone could

View file

@ -180,7 +180,7 @@ with the extra operations and then provide an iterator over positions.
For example, a tree implementation might provide:
// Positions returns an iterator over positions in the sequence.
func (t *Tree[V]) Positions() iter.Seq[*Pos]
func (t *Tree[V]) Positions() iter.Seq[*Pos[V]]
// A Pos represents a position in the sequence.
// It is only valid during the yield call it is passed to.

View file

@ -236,6 +236,14 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
if !ac.IsUndefined() {
// Abort the Fetch request.
ac.Call("abort")
// Wait for fetch promise to be rejected prior to exiting. See
// https://github.com/golang/go/issues/57098 for more details.
select {
case resp := <-respCh:
resp.Body.Close()
case <-errCh:
}
}
return nil, req.Context().Err()
case resp := <-respCh:

View file

@ -8,6 +8,7 @@ package os
import (
"io"
"runtime"
"syscall"
)
@ -34,7 +35,15 @@ func removeAll(path string) error {
// its parent directory
parentDir, base := splitPath(path)
parent, err := Open(parentDir)
flag := O_RDONLY
if runtime.GOOS == "windows" {
// On Windows, the process might not have read permission on the parent directory,
// but still can delete files in it. See https://go.dev/issue/74134.
// We can open a file even if we don't have read permission by passing the
// O_WRONLY | O_RDWR flag, which is mapped to FILE_READ_ATTRIBUTES.
flag = O_WRONLY | O_RDWR
}
parent, err := OpenFile(parentDir, flag, 0)
if IsNotExist(err) {
// If parent does not exist, base cannot exist. Fail silently
return nil

View file

@ -8719,6 +8719,11 @@ func TestTypeAssert(t *testing.T) {
testTypeAssert(t, any(int(1)), int(1), true)
testTypeAssert(t, any(int(1)), byte(0), false)
testTypeAssert(t, fmt.Stringer(vv), vv, true)
testTypeAssert(t, any(nil), any(nil), false)
testTypeAssert(t, any(nil), error(nil), false)
testTypeAssert(t, error(nil), any(nil), false)
testTypeAssert(t, error(nil), error(nil), false)
}
func testTypeAssert[T comparable, V any](t *testing.T, val V, wantVal T, wantOk bool) {

View file

@ -1514,46 +1514,46 @@ func TypeAssert[T any](v Value) (T, bool) {
}
typ := abi.TypeFor[T]()
// If v is an interface, return the element inside the interface.
//
// T is a concrete type and v is an interface. For example:
//
// var v any = int(1)
// val := ValueOf(&v).Elem()
// TypeAssert[int](val) == val.Interface().(int)
//
// T is a interface and v is a non-nil interface value. For example:
//
// var v any = &someError{}
// val := ValueOf(&v).Elem()
// TypeAssert[error](val) == val.Interface().(error)
//
// T is a interface and v is a nil interface value. For example:
//
// var v error = nil
// val := ValueOf(&v).Elem()
// TypeAssert[error](val) == val.Interface().(error)
if v.kind() == Interface {
v, ok := packIfaceValueIntoEmptyIface(v).(T)
return v, ok
}
// If T is an interface and v is a concrete type. For example:
//
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
if typ.Kind() == abi.Interface {
v, ok := packEface(v).(T)
return v, ok
}
// Both v and T must be concrete types.
// The only way for an type-assertion to match is if the types are equal.
if typ != v.typ() {
// We can't just return false here:
//
// var zero T
// return zero, false
//
// since this function should work in the same manner as v.Interface().(T) does.
// Thus we have to handle two cases specially.
// Return the element inside the interface.
//
// T is a concrete type and v is an interface. For example:
//
// var v any = int(1)
// val := ValueOf(&v).Elem()
// TypeAssert[int](val) == val.Interface().(int)
//
// T is a interface and v is an interface, but the iface types are different. For example:
//
// var v any = &someError{}
// val := ValueOf(&v).Elem()
// TypeAssert[error](val) == val.Interface().(error)
if v.kind() == Interface {
v, ok := packIfaceValueIntoEmptyIface(v).(T)
return v, ok
}
// T is an interface, v is a concrete type. For example:
//
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
if typ.Kind() == abi.Interface {
v, ok := packEface(v).(T)
return v, ok
}
var zero T
return zero, false
}
if v.flag&flagIndir == 0 {
return *(*T)(unsafe.Pointer(&v.ptr)), true
}

View file

@ -1059,6 +1059,28 @@ func (mp *m) becomeSpinning() {
sched.needspinning.Store(0)
}
// Take a snapshot of allp, for use after dropping the P.
//
// Must be called with a P, but the returned slice may be used after dropping
// the P. The M holds a reference on the snapshot to keep the backing array
// alive.
//
//go:yeswritebarrierrec
func (mp *m) snapshotAllp() []*p {
mp.allpSnapshot = allp
return mp.allpSnapshot
}
// Clear the saved allp snapshot. Should be called as soon as the snapshot is
// no longer required.
//
// Must be called after reacquiring a P, as it requires a write barrier.
//
//go:yeswritebarrierrec
func (mp *m) clearAllpSnapshot() {
mp.allpSnapshot = nil
}
func (mp *m) hasCgoOnStack() bool {
return mp.ncgo > 0 || mp.isextra
}
@ -3307,10 +3329,10 @@ func execute(gp *g, inheritTime bool) {
tryRecordGoroutineProfile(gp, nil, osyield)
}
// Assign gp.m before entering _Grunning so running Gs have an
// M.
// Assign gp.m before entering _Grunning so running Gs have an M.
mp.curg = gp
gp.m = mp
gp.syncSafePoint = false // Clear the flag, which may have been set by morestack.
casgstatus(gp, _Grunnable, _Grunning)
gp.waitsince = 0
gp.preempt = false
@ -3346,6 +3368,11 @@ func findRunnable() (gp *g, inheritTime, tryWakeP bool) {
// an M.
top:
// We may have collected an allp snapshot below. The snapshot is only
// required in each loop iteration. Clear it to all GC to collect the
// slice.
mp.clearAllpSnapshot()
pp := mp.p.ptr()
if sched.gcwaiting.Load() {
gcstopm()
@ -3527,7 +3554,11 @@ top:
// which can change underfoot once we no longer block
// safe-points. We don't need to snapshot the contents because
// everything up to cap(allp) is immutable.
allpSnapshot := allp
//
// We clear the snapshot from the M after return via
// mp.clearAllpSnapshop (in schedule) and on each iteration of the top
// loop.
allpSnapshot := mp.snapshotAllp()
// Also snapshot masks. Value changes are OK, but we can't allow
// len to change out from under us.
idlepMaskSnapshot := idlepMask
@ -3668,6 +3699,9 @@ top:
pollUntil = checkTimersNoP(allpSnapshot, timerpMaskSnapshot, pollUntil)
}
// We don't need allp anymore at this pointer, but can't clear the
// snapshot without a P for the write barrier..
// Poll network until next timer.
if netpollinited() && (netpollAnyWaiters() || pollUntil != 0) && sched.lastpoll.Swap(0) != 0 {
sched.pollUntil.Store(pollUntil)
@ -4103,6 +4137,11 @@ top:
gp, inheritTime, tryWakeP := findRunnable() // blocks until work is available
// findRunnable may have collected an allp snapshot. The snapshot is
// only required within findRunnable. Clear it to all GC to collect the
// slice.
mp.clearAllpSnapshot()
if debug.dontfreezetheworld > 0 && freezing.Load() {
// See comment in freezetheworld. We don't want to perturb
// scheduler state, so we didn't gcstopm in findRunnable, but

View file

@ -528,11 +528,12 @@ func TestGdbBacktrace(t *testing.T) {
got, err := cmd.CombinedOutput()
t.Logf("gdb output:\n%s", got)
if err != nil {
noProcessRE := regexp.MustCompile(`Couldn't get [a-zA-Z_ -]* ?registers: No such process\.`)
switch {
case bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")):
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28551
testenv.SkipFlaky(t, 43068)
case bytes.Contains(got, []byte("Couldn't get registers: No such process.")),
case noProcessRE.Match(got),
bytes.Contains(got, []byte("Unable to fetch general registers.: No such process.")),
bytes.Contains(got, []byte("reading register pc (#64): No such process.")):
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=9086

View file

@ -466,6 +466,7 @@ type g struct {
runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking
lockedm muintptr
fipsIndicator uint8
syncSafePoint bool // set if g is stopped at a synchronous safe point.
runningCleanups atomic.Bool
sig uint32
writebuf []byte
@ -568,6 +569,7 @@ type m struct {
needextram bool
g0StackAccurate bool // whether the g0 stack has accurate bounds
traceback uint8
allpSnapshot []*p // Snapshot of allp for use after dropping P in findRunnable, nil otherwise.
ncgocall uint64 // number of cgo calls in total
ncgo int32 // number of cgo calls currently in progress
cgoCallersUse atomic.Uint32 // if non-zero, cgoCallers in use temporarily

View file

@ -1115,6 +1115,9 @@ func newstack() {
shrinkstack(gp)
}
// Set a flag indicated that we've been synchronously preempted.
gp.syncSafePoint = true
if gp.preemptStop {
preemptPark(gp) // never returns
}

View file

@ -70,8 +70,6 @@ import "C"
import (
"fmt"
"os"
"time"
)
func init() {
@ -84,12 +82,8 @@ func GoNeedM() {
func NeedmDeadlock() {
// The failure symptom is that the program hangs because of a
// deadlock in needm, so set an alarm.
go func() {
time.Sleep(5 * time.Second)
fmt.Println("Hung for 5 seconds")
os.Exit(1)
}()
// deadlock in needm. Instead of using an arbitrary timeout,
// we let the test deadline expire if it deadlocks.
C.runNeedmSignalThread()
fmt.Println("OK")

View file

@ -457,7 +457,7 @@ func (tl traceLocker) GoPreempt() {
// GoStop emits a GoStop event with the provided reason.
func (tl traceLocker) GoStop(reason traceGoStopReason) {
tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1))
tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(0))
}
// GoPark emits a GoBlock event with the provided reason.

View file

@ -109,7 +109,22 @@ func traceStack(skip int, gp *g, gen uintptr) uint64 {
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.syscallbp), pcBuf[2:])
} else {
pcBuf[1] = gp.sched.pc
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[2:])
if gp.syncSafePoint {
// We're stopped in morestack, which is an odd state because gp.sched.bp
// refers to our parent frame, since we haven't had the chance to push our
// frame pointer to the stack yet. If we just start walking from gp.sched.bp,
// we'll skip a frame as a result. Luckily, we can find the PC we want right
// at gp.sched.sp on non-LR platforms, and we have it directly on LR platforms.
// See issue go.dev/issue/68090.
if usesLR {
pcBuf[2] = gp.sched.lr
} else {
pcBuf[2] = *(*uintptr)(unsafe.Pointer(gp.sched.sp))
}
nstk += 2 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[3:])
} else {
nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[2:])
}
}
}
}

View file

@ -120,13 +120,6 @@ func (wg *WaitGroup) Add(delta int) {
if w != 0 && delta > 0 && v == int32(delta) {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
if v == 0 && bubbled {
// Disassociate the WaitGroup from its bubble.
synctest.Disassociate(wg)
if w == 0 {
wg.state.Store(0)
}
}
if v > 0 || w == 0 {
return
}
@ -140,6 +133,11 @@ func (wg *WaitGroup) Add(delta int) {
}
// Reset waiters count to 0.
wg.state.Store(0)
if bubbled {
// Adds must not happen concurrently with wait when counter is 0,
// so we can safely disassociate wg from its current bubble.
synctest.Disassociate(wg)
}
for ; w != 0; w-- {
runtime_Semrelease(&wg.sema, false, 0)
}
@ -166,13 +164,20 @@ func (wg *WaitGroup) Wait() {
for {
state := wg.state.Load()
v := int32(state >> 32)
w := uint32(state)
w := uint32(state & 0x7fffffff)
if v == 0 {
// Counter is 0, no need to wait.
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(wg))
}
if w == 0 && state&waitGroupBubbleFlag != 0 && synctest.IsAssociated(wg) {
// Adds must not happen concurrently with wait when counter is 0,
// so we can disassociate wg from its current bubble.
if wg.state.CompareAndSwap(state, 0) {
synctest.Disassociate(wg)
}
}
return
}
// Increment waiters count.

View file

@ -0,0 +1,30 @@
// run
// Copyright 2025 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 (
"errors"
"fmt"
"os"
)
func crashOnErr(err error) bool {
if err != nil {
panic(err)
}
return false
}
func main() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
fmt.Println(crashOnErr(errors.New("test error")))
}

View file

@ -0,0 +1,32 @@
// run
// Copyright 2025 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 (
"errors"
"fmt"
"os"
)
func crashOnErr(err error) int {
if err != nil {
panic(err)
}
return 10
}
func main() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
s := make([]int, crashOnErr(errors.New("test error")))
println("unreachable: len(s) =", len(s))
}

View file

@ -0,0 +1,54 @@
// run
// Copyright 2025 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 (
"errors"
"fmt"
"os"
)
type S struct{ a, b int }
func crashOnErr1(err error) S {
if err != nil {
panic(err)
}
return S{} // zero value struct
}
func f1() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
fmt.Println(crashOnErr1(errors.New("test error")))
}
func crashOnErr2(err error) S {
if err != nil {
panic(err)
}
return S{1, 2} // not zero value struct
}
func f2() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
fmt.Println(crashOnErr2(errors.New("test error")))
}
func main() {
f1()
f2()
}