Skip to content
11 changes: 9 additions & 2 deletions src/archive/tar/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package tar
import (
"bytes"
"io"
"math"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -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:
Expand Down
47 changes: 47 additions & 0 deletions src/archive/tar/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
})
}
}

11 changes: 11 additions & 0 deletions src/cmd/compile/internal/walk/stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)

Expand Down
137 changes: 124 additions & 13 deletions src/crypto/tls/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/tls/handshake_server_tls13.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
11 changes: 6 additions & 5 deletions src/crypto/tls/quic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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")
}
Expand Down Expand Up @@ -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
}
Loading
Loading