diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index 9f47775c9aec5f..e3083b2b0d9c25 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -7,6 +7,7 @@ package tar import ( "bytes" "io" + "math" "path/filepath" "strconv" "strings" @@ -276,10 +277,16 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { hdr.Gname = v case paxUid: id64, err = strconv.ParseInt(v, 10, 64) - hdr.Uid = int(id64) // Integer overflow possible + if err != nil || id64 > math.MaxInt || id64 < math.MinInt { + return ErrHeader + } + hdr.Uid = int(id64) case paxGid: id64, err = strconv.ParseInt(v, 10, 64) - hdr.Gid = int(id64) // Integer overflow possible + if err != nil || id64 > math.MaxInt || id64 < math.MinInt { + return ErrHeader + } + hdr.Gid = int(id64) case paxAtime: hdr.AccessTime, err = parsePAXTime(v) case paxMtime: diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go index a324674cb7bb19..621cf29f3e3cd6 100644 --- a/src/archive/tar/reader_test.go +++ b/src/archive/tar/reader_test.go @@ -1702,3 +1702,50 @@ func TestDisableInsecurePathCheck(t *testing.T) { t.Fatalf("tr.Next with tarinsecurepath=1: got name %q, want %q", h.Name, name) } } + +func TestMergePAXIntegerOverflow(t *testing.T) { + vectors := []struct { + paxHdrs map[string]string + wantErr bool + }{ + {map[string]string{paxUid: "0"}, false}, + {map[string]string{paxUid: "1000"}, false}, + {map[string]string{paxUid: "4294967296"}, math.MaxInt < 4294967296}, + {map[string]string{paxGid: "4294967296"}, math.MaxInt < 4294967296}, + {map[string]string{paxUid: "2147483648"}, math.MaxInt < 2147483648}, + {map[string]string{paxGid: "2147483648"}, math.MaxInt < 2147483648}, + {map[string]string{paxUid: "9223372036854775808"}, true}, + } + + for _, tt := range vectors { + testname := fmt.Sprintf("%v", tt.paxHdrs) + t.Run(testname, func(t *testing.T) { + hdr := new(Header) + err := mergePAX(hdr, tt.paxHdrs) + if tt.wantErr { + if err == nil { + t.Fatal("Expected a non-nil error") + } + if !errors.Is(err, ErrHeader) { + t.Fatalf("Expected error of type ErrHeader, got instead %v", err) + } + if hdr.Gid != 0 { + t.Fatalf("Gid was unexpectedly set after error: %v", hdr.Gid) + } + if hdr.Uid != 0 { + t.Fatalf("Uid was unexpectedly set after error: %v", hdr.Uid) + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if hdr.Gid < 0 { + t.Fatalf("Gid was unexpectedly set after overflow: %v", hdr.Gid) + } + if hdr.Uid < 0 { + t.Fatalf("Uid was unexpectedly set after overflow: %v", hdr.Uid) + } + }) + } +} + diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go index 2c01fd10f124e5..b9999cd9ea4732 100644 --- a/src/cmd/compile/internal/walk/stmt.go +++ b/src/cmd/compile/internal/walk/stmt.go @@ -7,6 +7,7 @@ package walk import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/reflectdata" ) // The result of walkStmt MUST be assigned back to n, e.g. @@ -138,6 +139,16 @@ func walkStmt(n ir.Node) ir.Node { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) + // Since go.dev/cl/751465, the compiler emits tail calls for wrappers + // for embedded interfaces. But a tail call never reaches walkCall, so + // the interface calls are not marked as used, causing the linker to + // drop the callee. See issues #81089 and #81340. + // TODO: Should we just call walkCall here? + if n.Call.Op() == ir.OCALLINTER { + usemethod(n.Call) + reflectdata.MarkUsedIfaceMethod(n.Call) + } + var init ir.Nodes n.Call.Fun = walkExpr(n.Call.Fun, &init) diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index c6d30841b6c2cf..d48316017806ae 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -99,12 +99,27 @@ type Conn struct { clientProtocol string // input/output - in, out halfConn - rawInput bytes.Buffer // raw input, starting with a record header - input bytes.Reader // application data waiting to be read, from rawInput.Next - hand bytes.Buffer // handshake data waiting to be read - buffering bool // whether records are buffered in sendBuf - sendBuf []byte // a buffer of records waiting to be sent + in, out halfConn + // rawInput holds raw input, starting with a record header. + // It is nil when no input is buffered, in which case the buffer has + // been returned to rawInputPool so that connections idle in Read do + // not pin a record-sized buffer. It is lazily repopulated from the + // pool by readFromUntil. + rawInput *bytes.Buffer + // smallInput is a small buffer that serves as rawInput while + // waiting for a record header after rawInput has been returned to + // rawInputPool. It is lazily allocated by readFromUntil and then + // kept for the life of the connection. + smallInput *bytes.Buffer + // input holds application data waiting to be read, from rawInput.Next. + input bytes.Reader + // hand holds handshake data waiting to be read. + // It is nil when no handshake data is buffered, in which case the + // buffer has been returned to handPool. Use handBuf and handLen to + // access it. + hand *bytes.Buffer + buffering bool // whether records are buffered in sendBuf + sendBuf []byte // a buffer of records waiting to be sent // bytesSent counts the bytes of application data sent. // packetsSent counts packets. @@ -580,7 +595,9 @@ func (e RecordHeaderError) Error() string { return "tls: " + e.Msg } func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) { err.Msg = msg err.Conn = conn - copy(err.RecordHeader[:], c.rawInput.Bytes()) + if c.rawInput != nil { + copy(err.RecordHeader[:], c.rawInput.Bytes()) + } return err } @@ -622,6 +639,19 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with QUIC transport")) } + // If rawInput is empty, we are about to block in a Read on the + // underlying connection waiting for the next record, possibly for a + // long time. A previous record may have grown rawInput to the maximum + // record size; don't pin that memory while idle. Return the buffer to + // the pool, and let readFromUntil read the header into a small buffer + // and switch back to a pooled record-sized buffer only once the + // payload length is known. + if c.rawInput != nil && c.rawInput.Len() == 0 && c.rawInput != c.smallInput && c.rawInput.Cap() > maxIdleInputCap { + c.rawInput.Reset() + rawInputPool.Put(c.rawInput) + c.rawInput = nil + } + // Read header, payload. if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil { // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify @@ -702,7 +732,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { } // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3. - if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 { + if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.handLen() > 0 { return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } @@ -747,7 +777,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { return c.in.setErrorLocked(c.sendAlert(alertDecodeError)) } // Handshake messages are not allowed to fragment across the CCS. - if c.hand.Len() > 0 { + if c.handLen() > 0 { return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } // In TLS 1.3, change_cipher_spec records are ignored until the @@ -783,7 +813,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { if len(data) == 0 || expectChangeCipherSpec { return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } - c.hand.Write(data) + c.handBuf().Write(data) } return nil @@ -800,13 +830,85 @@ func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error { return c.readRecordOrCCS(expectChangeCipherSpec) } +// rawInputPool pools the record-sized buffers that back Conn.rawInput +// while records are being received. A connection returns its buffer to +// the pool before blocking to wait for a new record, often for a long +// time, so that idle connections do not each pin a record-sized buffer. +// Only buffers with capacity above maxIdleInputCap are pooled; smaller +// buffers stay attached to their connection. +var rawInputPool = sync.Pool{New: func() any { return new(bytes.Buffer) }} + +// maxIdleInputCap is the largest rawInput capacity that a connection +// keeps while waiting for a new record to arrive. It is large enough to +// hold a record header and small records, so that only connections +// receiving larger records pay for the pooled buffer switch below. +const maxIdleInputCap = 1024 + +// handPool pools the buffers that back Conn.hand, which typically grow +// to hold the peer's largest flight of handshake messages. A connection +// returns its buffer to the pool once the handshake completes and after +// buffered post-handshake messages have been consumed, so that +// established connections do not pin it. +var handPool = sync.Pool{New: func() any { return new(bytes.Buffer) }} + +// handBuf returns c.hand for writing, getting a buffer from handPool if +// c.hand is nil. +func (c *Conn) handBuf() *bytes.Buffer { + if c.hand == nil { + c.hand = handPool.Get().(*bytes.Buffer) + } + return c.hand +} + +// handLen returns the number of buffered handshake bytes. +func (c *Conn) handLen() int { + if c.hand == nil { + return 0 + } + return c.hand.Len() +} + +// releaseHand returns c.hand to handPool if it is empty. +func (c *Conn) releaseHand() { + if c.hand != nil && c.hand.Len() == 0 { + c.hand.Reset() + handPool.Put(c.hand) + c.hand = nil + } +} + // readFromUntil reads from r into c.rawInput until c.rawInput contains // at least n bytes or else returns an error. func (c *Conn) readFromUntil(r io.Reader, n int) error { + if c.rawInput == nil { + // The record buffer was released while waiting for a new + // record. Block for the header using the connection's small + // buffer; the switch to a pooled record-sized buffer below + // happens only once the payload length is known and data is + // flowing. + if c.smallInput == nil { + c.smallInput = new(bytes.Buffer) + } + c.rawInput = c.smallInput + } if c.rawInput.Len() >= n { return nil } needs := n - c.rawInput.Len() + if want := c.rawInput.Len() + needs + bytes.MinRead; want > maxIdleInputCap && want > c.rawInput.Cap() { + // Growing past maxIdleInputCap: switch to a pooled buffer so + // that record-sized buffers are recycled across connections + // rather than allocated for every record. + b := rawInputPool.Get().(*bytes.Buffer) + b.Write(c.rawInput.Bytes()) + if c.rawInput == c.smallInput { + c.smallInput.Reset() + } else if c.rawInput.Cap() > maxIdleInputCap { + c.rawInput.Reset() + rawInputPool.Put(c.rawInput) + } + c.rawInput = b + } // There might be extra input waiting on the wire. Make a best effort // attempt to fetch it so that it can be used in (*Conn).Read to // "predict" closeNotify alerts. @@ -1079,7 +1181,7 @@ func (c *Conn) readHandshakeBytes(n int) error { if c.quic != nil { return c.quicReadHandshakeBytes(n) } - for c.hand.Len() < n { + for c.handLen() < n { if err := c.readRecord(); err != nil { return err } @@ -1392,11 +1494,12 @@ func (c *Conn) Read(b []byte) (int, error) { if err := c.readRecord(); err != nil { return 0, err } - for c.hand.Len() > 0 { + for c.handLen() > 0 { if err := c.handlePostHandshakeMessage(); err != nil { return 0, err } } + c.releaseHand() } n, _ := c.input.Read(b) @@ -1574,6 +1677,14 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) { panic("tls: internal error: handshake returned an error but is marked successful") } + // The handshake buffer typically grew to hold the peer's largest + // flight of handshake messages and is now empty. Post-handshake + // messages are rare and small, so release the buffer rather than + // pinning it for the life of the connection. + if c.handshakeErr == nil { + c.releaseHand() + } + if c.quic != nil { if c.handshakeErr == nil { c.quicHandshakeComplete() @@ -1685,7 +1796,7 @@ func (c *Conn) setReadTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptio // Ensure that there are no buffered handshake messages before changing the // read keys, since that can cause messages to be parsed that were encrypted // using old keys which are no longer appropriate. - if c.hand.Len() != 0 { + if c.handLen() != 0 { if locked { c.sendAlertLocked(alertUnexpectedMessage) } else { diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 2c96e2435ae156..b5a7ea6ae4f1e5 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -528,7 +528,7 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) // Make sure the client didn't send extra handshake messages alongside // their initial client_hello. If they sent two client_hello messages, // we will consume the second before they respond to the server_hello. - if c.hand.Len() != 0 { + if c.handLen() != 0 { c.sendAlert(alertUnexpectedMessage) return nil, errors.New("tls: handshake buffer not empty before HelloRetryRequest") } diff --git a/src/crypto/tls/quic.go b/src/crypto/tls/quic.go index b872d4fe0174a0..6755ebf58dedd3 100644 --- a/src/crypto/tls/quic.go +++ b/src/crypto/tls/quic.go @@ -288,9 +288,9 @@ func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error { // The handshake goroutine has exited. c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() - c.hand.Write(c.quic.readbuf) + c.handBuf().Write(c.quic.readbuf) c.quic.readbuf = nil - for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil { + for q.conn.handLen() >= 4 && q.conn.handshakeErr == nil { b := q.conn.hand.Bytes() n := int(b[1])<<16 | int(b[2])<<8 | int(b[3]) if n > maxHandshake { @@ -304,6 +304,7 @@ func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error { q.conn.handshakeErr = err } } + q.conn.releaseHand() if q.conn.handshakeErr != nil { return quicError(q.conn.handshakeErr) } @@ -394,7 +395,7 @@ func quicError(err error) error { } func (c *Conn) quicReadHandshakeBytes(n int) error { - for c.hand.Len() < n { + for c.handLen() < n { if err := c.quicWaitForSignal(); err != nil { return err } @@ -407,7 +408,7 @@ func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret // read keys, since that can cause messages to be parsed that were encrypted // using old keys which are no longer appropriate. // TODO(roland): we should merge this check with the similar one in setReadTrafficSecret. - if c.hand.Len() != 0 { + if c.handLen() != 0 { c.sendAlert(alertUnexpectedMessage) return errors.New("tls: handshake buffer not empty before setting read traffic secret") } @@ -520,7 +521,7 @@ func (c *Conn) quicWaitForSignal() error { // The connection has been canceled. return c.sendAlertLocked(alertCloseNotify) } - c.hand.Write(c.quic.readbuf) + c.handBuf().Write(c.quic.readbuf) c.quic.readbuf = nil return nil } diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go index 46db4fade8780a..2195dfc4d44a61 100644 --- a/src/encoding/json/encode_test.go +++ b/src/encoding/json/encode_test.go @@ -1111,6 +1111,30 @@ func TestNilMarshalerTextMapKey(t *testing.T) { } } +// textMarshalerString is a string kind that implements encoding.TextMarshaler. +type textMarshalerString string + +func (s textMarshalerString) MarshalText() ([]byte, error) { + return []byte("X_" + string(s)), nil +} + +func (s textMarshalerString) AppendText(b []byte) ([]byte, error) { + return append(b, ("X_" + string(s))...), nil +} + +// Issue 81355: string-kind map keys are used directly even if the key type +// implements encoding.TextMarshaler. MarshalText is still called for values. +func TestStringKindTextMarshalerMapKey(t *testing.T) { + got, err := Marshal(map[textMarshalerString]textMarshalerString{"foo": "bar"}) + if err != nil { + t.Fatalf("Marshal error: %v", err) + } + const want = `{"foo":"X_bar"}` + if string(got) != want { + t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) + } +} + var re = regexp.MustCompile // syntactic checks on form of marshaled floating point numbers. diff --git a/src/encoding/json/v2/arshal_methods.go b/src/encoding/json/v2/arshal_methods.go index 092637b6b64f54..c194f7f84b9ede 100644 --- a/src/encoding/json/v2/arshal_methods.go +++ b/src/encoding/json/v2/arshal_methods.go @@ -178,7 +178,9 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { prevMarshal := fncs.marshal fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && - (needAddr && va.forcedAddr) { + ((needAddr && va.forcedAddr) || + (export.Encoder(enc).Tokens.Last.NeedObjectName()) && t.Kind() == reflect.String) { + // Do not call MarshalText on unaddressable values and map keys of string kind. return prevMarshal(enc, va, mo) } marshaler, _ := reflect.TypeAssert[encoding.TextMarshaler](va.Addr()) @@ -204,7 +206,9 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { prevMarshal := fncs.marshal fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) { if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && - (needAddr && va.forcedAddr) { + ((needAddr && va.forcedAddr) || + (export.Encoder(enc).Tokens.Last.NeedObjectName()) && t.Kind() == reflect.String) { + // Do not call AppendText on unaddressable values and map keys of string kind. return prevMarshal(enc, va, mo) } appender, _ := reflect.TypeAssert[encoding.TextAppender](va.Addr()) @@ -228,6 +232,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && ((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) { + // Do not call MarshalJSON on unaddressable values and map keys. return prevMarshal(enc, va, mo) } marshaler, _ := reflect.TypeAssert[Marshaler](va.Addr()) @@ -259,6 +264,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error { if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && ((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) { + // Do not call MarshalJSONTo on unaddressable values and map keys. return prevMarshal(enc, va, mo) } xe := export.Encoder(enc) @@ -330,6 +336,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error { if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && export.Decoder(dec).Tokens.Last.NeedObjectName() { + // Do not call UnmarshalJSON on map keys. return prevUnmarshal(dec, va, uo) } val, err := dec.ReadValue() @@ -355,6 +362,7 @@ func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler { fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error { if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) && export.Decoder(dec).Tokens.Last.NeedObjectName() { + // Do not call UnmarshalJSONFrom on map keys. return prevUnmarshal(dec, va, uo) } xd := export.Decoder(dec) diff --git a/src/encoding/json/v2_encode_test.go b/src/encoding/json/v2_encode_test.go index 4b1a5648f24158..a89848a08be1c0 100644 --- a/src/encoding/json/v2_encode_test.go +++ b/src/encoding/json/v2_encode_test.go @@ -1116,6 +1116,30 @@ func TestNilMarshalerTextMapKey(t *testing.T) { } } +// textMarshalerString is a string kind that implements encoding.TextMarshaler. +type textMarshalerString string + +func (s textMarshalerString) MarshalText() ([]byte, error) { + return []byte("X_" + string(s)), nil +} + +func (s textMarshalerString) AppendText(b []byte) ([]byte, error) { + return append(b, ("X_" + string(s))...), nil +} + +// Issue 81355: string-kind map keys are used directly even if the key type +// implements encoding.TextMarshaler. MarshalText is still called for values. +func TestStringKindTextMarshalerMapKey(t *testing.T) { + got, err := Marshal(map[textMarshalerString]textMarshalerString{"foo": "bar"}) + if err != nil { + t.Fatalf("Marshal error: %v", err) + } + const want = `{"foo":"X_bar"}` + if string(got) != want { + t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) + } +} + var re = regexp.MustCompile // syntactic checks on form of marshaled floating point numbers. diff --git a/src/internal/runtime/cgroup/cgroup.go b/src/internal/runtime/cgroup/cgroup.go index 46a25ad28b3b61..88920958a89da2 100644 --- a/src/internal/runtime/cgroup/cgroup.go +++ b/src/internal/runtime/cgroup/cgroup.go @@ -448,7 +448,7 @@ func unescapedLen(in []byte) int { // // Returns the number of bytes written to out. // -// Also see escapePath in cgroup_linux_test.go. +// Also see escapePath in cgroup_test.go. func unescapePath(out []byte, in []byte) (int, error) { var outi, ini int for ini < len(in) { diff --git a/src/internal/runtime/maps/map.go b/src/internal/runtime/maps/map.go index 0ee5e09bf05bc5..424bb3d319b1f3 100644 --- a/src/internal/runtime/maps/map.go +++ b/src/internal/runtime/maps/map.go @@ -265,7 +265,7 @@ type Map struct { } // Use 64-bit hash on 64-bit systems, except on Wasm, where we use -// 32-bit hash (see runtime/hash32.go). +// 32-bit hash (see runtime_hash32.go). const Use64BitHash = goarch.PtrSize == 8 && goarch.IsWasm == 0 func depthToShift(depth uint8) uint8 { diff --git a/src/math/all_test.go b/src/math/all_test.go index afd97da956c81d..b952df278c4904 100644 --- a/src/math/all_test.go +++ b/src/math/all_test.go @@ -1553,6 +1553,36 @@ var logSC = []float64{ NaN(), } +// Inputs near 1 and their correctly rounded Log2 values. +var vflog2NearOne = []float64{ + 0x1.0000000000001p+0, + 0x1.fffffffffffffp-1, + 0x1.0000000001p+0, + 0x1.fffffffffep-1, + 0x1.00001p+0, + 0x1.ffffep-1, + 0x1.004p+0, + 0x1.ff8p-1, + 0x1.4p+0, + 0x1.8p-1, + 0x1.fffffffffffffp+0, + 0x1.0000000000001p-1, +} +var log2NearOne = []float64{ + 3.203426503814917e-16, + -1.6017132519074588e-16, + 1.3121234959619935e-12, + -1.312123495963187e-12, + 1.375860550841138e-06, + -1.3758618629646341e-06, + 0.0014081943928083889, + -0.0014095702546713536, + 0.32192809488736235, + -0.4150374992788438, + 0.9999999999999999, + -0.9999999999999997, +} + var vflogbSC = []float64{ Inf(-1), 0, @@ -2853,6 +2883,11 @@ func TestLog2(t *testing.T) { t.Errorf("Log2(%g) = %g, want %g", vflogSC[i], f, logSC[i]) } } + for i := 0; i < len(vflog2NearOne); i++ { + if f := Log2(vflog2NearOne[i]); !veryclose(log2NearOne[i], f) { + t.Errorf("Log2(%g) = %g, want %g", vflog2NearOne[i], f, log2NearOne[i]) + } + } for i := -1074; i <= 1023; i++ { f := Ldexp(1, i) l := Log2(f) diff --git a/src/math/big/decimal.go b/src/math/big/decimal.go index 9e391adef94fc2..8a630ed72c1604 100644 --- a/src/math/big/decimal.go +++ b/src/math/big/decimal.go @@ -9,12 +9,12 @@ // decimal and rounding. // // The key observation and some code (shr) is borrowed from -// strconv/decimal.go: conversion of binary fractional values can be done +// internal/strconv/decimal.go: conversion of binary fractional values can be done // precisely in multi-precision decimal because 2 divides 10 (required for // >> of mantissa); but conversion of decimal floating-point values cannot // be done precisely in binary representation. // -// In contrast to strconv/decimal.go, only right shift is implemented in +// In contrast to internal/strconv/decimal.go, only right shift is implemented in // decimal format - left shift can be done precisely in binary format. package big diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go index fbf234f5d98350..726ce906155c3a 100644 --- a/src/math/big/floatconv_test.go +++ b/src/math/big/floatconv_test.go @@ -237,7 +237,7 @@ func TestFloat64Text(t *testing.T) { {1024.0, 'p', 0, "0x.8p+11"}, {-1024.0, 'p', 0, "-0x.8p+11"}, - // all test cases below from strconv/ftoa_test.go + // all test cases below from internal/strconv/ftoa_test.go {1, 'e', 5, "1.00000e+00"}, {1, 'f', 5, "1.00000"}, {1, 'g', 5, "1"}, diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go index 93e89ad1c8ebc0..25be0456db553d 100644 --- a/src/math/big/ratconv_test.go +++ b/src/math/big/ratconv_test.go @@ -351,7 +351,7 @@ var float64inputs = []string{ "75224575729e-45", "459926601011e+15", - // Constants plundered from strconv/atof_test.go. + // Constants plundered from internal/strconv/atof_test.go. "0", "1", diff --git a/src/math/log10.go b/src/math/log10.go index 02c3a757c012be..e5cf69adbdf0d1 100644 --- a/src/math/log10.go +++ b/src/math/log10.go @@ -33,5 +33,9 @@ func log2(x float64) float64 { if frac == 0.5 { return float64(exp - 1) } + // Avoid cancellation near 1. x-1 is exact for x in [0.5, 2). + if exp == 0 || exp == 1 { + return Log1p(x-1) * (1 / Ln2) + } return Log(frac)*(1/Ln2) + float64(exp) } diff --git a/src/math/sincos.go b/src/math/sincos.go index e3fb96094fa75f..e3c434a3f90f43 100644 --- a/src/math/sincos.go +++ b/src/math/sincos.go @@ -4,7 +4,7 @@ package math -// Coefficients _sin[] and _cos[] are found in pkg/math/sin.go. +// Coefficients _sin[] and _cos[] are found in sin.go. // Sincos returns Sin(x), Cos(x). // diff --git a/src/net/http/internal/http2/transport.go b/src/net/http/internal/http2/transport.go index 4b2d6bad5466c6..1cc32b740ce051 100644 --- a/src/net/http/internal/http2/transport.go +++ b/src/net/http/internal/http2/transport.go @@ -134,7 +134,7 @@ type ClientConn struct { t *Transport tconn net.Conn // usually *tls.Conn, except specialized impls tlsState *tls.ConnectionState // nil only for specialized impls - atomicReused uint32 // whether conn is being reused; atomic + reused atomic.Bool // whether conn is being reused singleUse bool // whether being used for a single http.Request getConnCalled bool // used by clientConnPool @@ -449,7 +449,7 @@ func (t *Transport) RoundTripOpt(req *ClientRequest, opt RoundTripOpt) (*ClientR t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) return nil, err } - reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1) + reused := !cc.reused.CompareAndSwap(false, true) traceGotConn(req, cc, reused) res, err := cc.RoundTrip(req) if err != nil && retry <= 6 { @@ -2113,7 +2113,7 @@ func (rl *clientConnReadLoop) cleanup() { unusedWaitTime = cc.idleTimeout } idleTime := time.Now().Sub(cc.lastActive) - if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime && !cc.closedOnIdle { + if !cc.reused.Load() && idleTime < unusedWaitTime && !cc.closedOnIdle { cc.idleTimer = time.AfterFunc(unusedWaitTime-idleTime, func() { cc.t.connPool.MarkDead(cc) }) diff --git a/src/runtime/_mkmalloc/mksizeclasses.go b/src/runtime/_mkmalloc/mksizeclasses.go index 2c39617c6b3ef7..aff81228d855b3 100644 --- a/src/runtime/_mkmalloc/mksizeclasses.go +++ b/src/runtime/_mkmalloc/mksizeclasses.go @@ -36,7 +36,7 @@ import ( "math/bits" ) -// Generate internal/runtime/gc/msize.go +// Generate internal/runtime/gc/sizeclasses.go func generateSizeClasses(classes []class) []byte { flag.Parse() diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 4a5a11594b072d..1c040a468d4d16 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -14,7 +14,7 @@ import ( ) const ( - // We use 32-bit hash on Wasm, see hash32.go. + // We use 32-bit hash on Wasm, see internal/runtime/maps/runtime_hash32.go. hashSize = (1-goarch.IsWasm)*goarch.PtrSize + goarch.IsWasm*4 c0 = uintptr((8-hashSize)/4*2860486313 + (hashSize-4)/4*33054211828000289) c1 = uintptr((8-hashSize)/4*3267000013 + (hashSize-4)/4*23344194077549503) @@ -24,8 +24,8 @@ func trimHash(h uintptr) uintptr { if goarch.IsWasm != 0 { // On Wasm, we use 32-bit hash, despite that uintptr is 64-bit. // memhash* always returns a uintptr with high 32-bit being 0 - // (see hash32.go). We trim the hash in other places where we - // compute the hash manually, e.g. in interhash. + // (see internal/runtime/maps/runtime_hash32.go). + // We trim the hash in other places where we compute the hash manually, e.g. in interhash. return uintptr(uint32(h)) } return h diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go index 5a1ca52a8c3b03..a88e6498edbf21 100644 --- a/src/runtime/chan_test.go +++ b/src/runtime/chan_test.go @@ -595,7 +595,7 @@ func TestMultiConsumer(t *testing.T) { func TestShrinkStackDuringBlockedSend(t *testing.T) { // make sure that channel operations still work when we are // blocked on a channel send and we shrink the stack. - // NOTE: this test probably won't fail unless stack1.go:stackDebug + // NOTE: this test probably won't fail unless stack.go:stackDebug // is set to >= 1. const n = 10 c := make(chan int) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 7c05cd6ea997c1..4a90668c7fe37a 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -1211,7 +1211,7 @@ func (s *mspan) isFreeOrNewlyAllocated(index uintptr) bool { func (s *mspan) divideByElemSize(n uintptr) uintptr { const doubleCheck = false - // See explanation in mksizeclasses.go's computeDivMagic. + // See explanation in runtime/_mkmalloc/mksizeclasses.go's computeDivMagic. q := uintptr((uint64(n) * uint64(s.divMul)) >> 32) if doubleCheck && q != n/s.elemsize { diff --git a/src/runtime/msize.go b/src/runtime/msize.go index 09da7459b24745..802508f4655f41 100644 --- a/src/runtime/msize.go +++ b/src/runtime/msize.go @@ -5,7 +5,7 @@ // Malloc small size classes. // // See malloc.go for overview. -// See also mksizeclasses.go for how we decide what size classes to use. +// See also runtime/_mkmalloc/mksizeclasses.go for how we decide what size classes to use. package runtime diff --git a/src/runtime/traceevent.go b/src/runtime/traceevent.go index 8651275476128d..9a5931897332cd 100644 --- a/src/runtime/traceevent.go +++ b/src/runtime/traceevent.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Trace event writing API for trace2runtime.go. +// Trace event writing API for traceruntime.go. package runtime diff --git a/src/runtime/type.go b/src/runtime/type.go index 58349e921a709b..89cf538a04df40 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -525,7 +525,7 @@ func moduleTypelinks(md *moduledata) []*_type { td := md.types // We have to increment by the pointer size to match the - // increment in cmd/link/internal/data.go createRelroSect + // increment in cmd/link/internal/ld/data.go createRelroSect // in allocateDataSections. // // The linker doesn't do that increment when runtime.types diff --git a/src/simd/archsimd/internal/simd_test/simd_amd64_test.go b/src/simd/archsimd/internal/simd_test/simd_amd64_test.go index 73656d4ccfdca4..50e6e11f1820da 100644 --- a/src/simd/archsimd/internal/simd_test/simd_amd64_test.go +++ b/src/simd/archsimd/internal/simd_test/simd_amd64_test.go @@ -1565,3 +1565,88 @@ func TestMaskOr(t *testing.T) { testMaskOr32x4(t) testMaskOr64x2(t) } + +func TestReduceSumFloat32x8(t *testing.T) { + // 256-bit float available with plain AVX + tests := []struct { + in []float32 + want float32 + }{ + {in: []float32{1, 2, 3, 4, 5, 6, 7, 8}, want: 36}, + {in: []float32{0.5, -0.5, 1.25, -1.25, 2.125, -2.125, 4, 8}, want: 12}, + {in: []float32{0, 0, 0, 0, 0, 0, 0, 0}, want: 0}, + {in: []float32{-1, -2, -3, -4, -5, -6, -7, -8}, want: -36}, + } + for _, tc := range tests { + v := archsimd.LoadFloat32x8(tc.in) + got := v.ReduceSum() + if got != tc.want { + t.Errorf("%v.ReduceSum() = %v, want %v", tc.in, got, tc.want) + } + } +} + +func TestReduceSumFloat64x4(t *testing.T) { + // 256-bit float available with plain AVX + tests := []struct { + in []float64 + want float64 + }{ + {in: []float64{10, 20, 30, 40}, want: 100}, + {in: []float64{0.5, -0.5, 1.25, -1.25}, want: 0}, + {in: []float64{0, 0, 0, 0}, want: 0}, + {in: []float64{1.125, 2.25, 3.5, 4.0}, want: 10.875}, + {in: []float64{-10, -20, -30, -40}, want: -100}, + } + for _, tc := range tests { + v := archsimd.LoadFloat64x4(tc.in) + got := v.ReduceSum() + if got != tc.want { + t.Errorf("%v.ReduceSum() = %v, want %v", tc.in, got, tc.want) + } + } +} + +func TestReduceSumFloat32x16(t *testing.T) { + if !archsimd.X86.AVX512() { + t.Skip("Test requires X86.AVX512, not available on this hardware") + return + } + tests := []struct { + in []float32 + want float32 + }{ + {in: []float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, want: 136}, + {in: []float32{1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, 9}, want: 17}, + {in: make([]float32, 16), want: 0}, + } + for _, tc := range tests { + v := archsimd.LoadFloat32x16(tc.in) + got := v.ReduceSum() + if got != tc.want { + t.Errorf("%v.ReduceSum() = %v, want %v", tc.in, got, tc.want) + } + } +} + +func TestReduceSumFloat64x8(t *testing.T) { + if !archsimd.X86.AVX512() { + t.Skip("Test requires X86.AVX512, not available on this hardware") + return + } + tests := []struct { + in []float64 + want float64 + }{ + {in: []float64{1, 2, 3, 4, 5, 6, 7, 8}, want: 36}, + {in: []float64{1, -1, 2, -2, 3, -3, 4, 5}, want: 9}, + {in: make([]float64, 8), want: 0}, + } + for _, tc := range tests { + v := archsimd.LoadFloat64x8(tc.in) + got := v.ReduceSum() + if got != tc.want { + t.Errorf("%v.ReduceSum() = %v, want %v", tc.in, got, tc.want) + } + } +} diff --git a/src/simd/archsimd/internal/simd_test/simd_test.go b/src/simd/archsimd/internal/simd_test/simd_test.go index 908fbb4e6e02a9..879a9a518b0a02 100644 --- a/src/simd/archsimd/internal/simd_test/simd_test.go +++ b/src/simd/archsimd/internal/simd_test/simd_test.go @@ -384,3 +384,45 @@ func TestIssue81264(t *testing.T) { for range 1 { } } + +func TestReduceSumFloat32x4(t *testing.T) { + tests := []struct { + in []float32 + want float32 + }{ + {in: []float32{1, 2, 3, 4}, want: 10}, + {in: []float32{1.5, -2.5, 3.25, -0.25}, want: 2.0}, + {in: []float32{0, 0, 0, 0}, want: 0}, + {in: []float32{0.125, 0.25, 0.5, 1.0}, want: 1.875}, + {in: []float32{-10, -20, -30, -40}, want: -100}, + {in: []float32{100, 200, 300, 400}, want: 1000}, + } + for _, tc := range tests { + v := archsimd.LoadFloat32x4(tc.in) + got := v.ReduceSum() + if got != tc.want { + t.Errorf("%v.ReduceSum() = %v, want %v", tc.in, got, tc.want) + } + } +} + +func TestReduceSumFloat64x2(t *testing.T) { + tests := []struct { + in []float64 + want float64 + }{ + {in: []float64{1, 2}, want: 3}, + {in: []float64{1.5, -2.5}, want: -1.0}, + {in: []float64{0, 0}, want: 0}, + {in: []float64{0.125, 0.375}, want: 0.5}, + {in: []float64{-100, 250}, want: 150}, + {in: []float64{1e10, 2e10}, want: 3e10}, + } + for _, tc := range tests { + v := archsimd.LoadFloat64x2(tc.in) + got := v.ReduceSum() + if got != tc.want { + t.Errorf("%v.ReduceSum() = %v, want %v", tc.in, got, tc.want) + } + } +} diff --git a/src/simd/archsimd/ops_emulated_amd64.go b/src/simd/archsimd/ops_emulated_amd64.go index cc45326d0a58bf..738093cebc8b92 100644 --- a/src/simd/archsimd/ops_emulated_amd64.go +++ b/src/simd/archsimd/ops_emulated_amd64.go @@ -203,3 +203,47 @@ func (x Uint8x64) Mul(y Uint8x64) Uint8x64 { po := xo.Mul(yo).And(mask16).ShiftAllLeft(8) return pe.Or(po).ReshapeToUint8s() } + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: AVX +func (x Float32x4) ReduceSum() float32 { + x = x.ConcatAddPairs(x) // [x0+x1, x2+x3, x0+x1, x2+x3] + x = x.ConcatAddPairs(x) // [(x0+x1)+(x2+x3), ...] + return x.GetElem(0) +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: AVX +func (x Float64x2) ReduceSum() float64 { + return x.ConcatAddPairs(x).GetElem(0) // [x0+x1, x0+x1] +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: AVX +func (x Float32x8) ReduceSum() float32 { + return x.GetLo().Add(x.GetHi()).ReduceSum() +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: AVX +func (x Float64x4) ReduceSum() float64 { + return x.GetLo().Add(x.GetHi()).ReduceSum() +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: AVX512 +func (x Float32x16) ReduceSum() float32 { + return x.GetLo().Add(x.GetHi()).ReduceSum() +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: AVX512 +func (x Float64x8) ReduceSum() float64 { + return x.GetLo().Add(x.GetHi()).ReduceSum() +} diff --git a/src/simd/archsimd/ops_emulated_arm64.go b/src/simd/archsimd/ops_emulated_arm64.go new file mode 100644 index 00000000000000..f0eea003b349f1 --- /dev/null +++ b/src/simd/archsimd/ops_emulated_arm64.go @@ -0,0 +1,23 @@ +// Copyright 2026 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. + +//go:build goexperiment.simd && arm64 + +package archsimd + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: NEON +func (x Float32x4) ReduceSum() float32 { + x = x.ConcatAddPairs(x) // [x0+x1, x2+x3, x0+x1, x2+x3] + x = x.ConcatAddPairs(x) // [(x0+x1)+(x2+x3), ...] + return x.GetElem(0) +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated, CPU Feature: NEON +func (x Float64x2) ReduceSum() float64 { + return x.ConcatAddPairs(x).GetElem(0) // [x0+x1, x0+x1] +} diff --git a/src/simd/archsimd/ops_emulated_wasm.go b/src/simd/archsimd/ops_emulated_wasm.go index b8f05526335212..c6e51651b74a5f 100644 --- a/src/simd/archsimd/ops_emulated_wasm.go +++ b/src/simd/archsimd/ops_emulated_wasm.go @@ -210,3 +210,19 @@ func (x Uint64x2) CarrylessMultiplyOdd(y Uint64x2) Uint64x2 { y = y.SetElem(0, x.GetElem(1)) return x.carrylessMultiply(y) } + +// ReduceSum returns the sum of all elements in x. +// +// Emulated +func (x Float32x4) ReduceSum() float32 { + // (x0+x1) + (x2 + x3) is a shorter evaluation tree, + // and associates the same as horizontal addition + return (x.GetElem(0) + x.GetElem(1)) + (x.GetElem(2) + x.GetElem(3)) +} + +// ReduceSum returns the sum of all elements in x. +// +// Emulated +func (x Float64x2) ReduceSum() float64 { + return x.GetElem(0) + x.GetElem(1) +} diff --git a/src/unicode/utf16/utf16.go b/src/unicode/utf16/utf16.go index 0293bbf639bc84..869d54a5136b78 100644 --- a/src/unicode/utf16/utf16.go +++ b/src/unicode/utf16/utf16.go @@ -69,7 +69,7 @@ func RuneLen(r rune) int { func Encode(s []rune) []uint16 { n := len(s) for _, v := range s { - if v >= surrSelf { + if surrSelf <= v && v <= maxRune { n++ } } diff --git a/src/unicode/utf16/utf16_test.go b/src/unicode/utf16/utf16_test.go index 3d434275afc592..bb6374fc9844e9 100644 --- a/src/unicode/utf16/utf16_test.go +++ b/src/unicode/utf16/utf16_test.go @@ -64,6 +64,21 @@ func TestEncode(t *testing.T) { } } +func TestEncodeCapacity(t *testing.T) { + for _, tt := range []struct { + in []rune + want int + }{ + {[]rune{MaxRune + 1}, 1}, + {[]rune{MaxRune}, 2}, + } { + out := Encode(tt.in) + if cap(out) != tt.want { + t.Errorf("cap(Encode(%x)) = %d; want %d", tt.in, cap(out), tt.want) + } + } +} + func TestAppendRune(t *testing.T) { for _, tt := range encodeTests { var out []uint16 @@ -242,6 +257,13 @@ func BenchmarkEncodeValidJapaneseChars(b *testing.B) { } } +func BenchmarkEncodeMixedRunes(b *testing.B) { + data := []rune{'h', 'e', '日', MaxRune + 1, '本', '語', MaxRune + 1, MaxRune + 2, 'b', 'e', 'n'} + for i := 0; i < b.N; i++ { + Encode(data) + } +} + func BenchmarkAppendRuneValidASCII(b *testing.B) { data := []rune{'h', 'e', 'l', 'l', 'o'} a := make([]uint16, 0, len(data)*2) diff --git a/test/fixedbugs/issue81089.go b/test/fixedbugs/issue81089.go new file mode 100644 index 00000000000000..347a5884b4ef0b --- /dev/null +++ b/test/fixedbugs/issue81089.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2026 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 + +type Conn interface{ Hello() } +type Pool[T Conn] struct{} + +func (p *Pool[T]) Hello(conn T) { conn.Hello() } + +type PoolConn struct{ Conn } +type CustomConn struct{} + +func (p CustomConn) Hello() { called = true } + +func NewPool[T Conn]() *Pool[T] { return &Pool[T]{} } + +var called bool + +func main() { + NewPool[*PoolConn]().Hello(&PoolConn{Conn: CustomConn{}}) + if !called { + panic("the embedded interface method Hello was not called") + } +}