CVE-2024-27304 (Go, published 2024-03-06T19:15Z)
Description: value: pgx is a PostgreSQL driver and toolkit for Go. SQL injection can occur if an attacker can cause a single query or bind message to exceed 4 GB in size. An integer overflow in the calculated message size can cause the one large message to be sent as multiple messages under the attacker's control. The problem is resolved in v4.18.2 and v5.5.4. As a workaround, reject user input large enough to cause a single query or bind message to exceed 4 GB in size.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/jackc/pgx
Files changed:
pgconn/pgconn.go
pgconn/pgconn_test.go
pgproto3/authentication_cleartext_password.go
pgproto3/authentication_gss.go
pgproto3/authentication_gss_continue.go
pgproto3/authentication_md5_password.go
pgproto3/authentication_ok.go
pgproto3/authentication_sasl.go
pgproto3/authentication_sasl_continue.go
pgproto3/authentication_sasl_final.go
pgproto3/backend.go
pgproto3/backend_key_data.go
pgproto3/backend_test.go
pgproto3/bind.go
pgproto3/bind_complete.go
pgproto3/bind_test.go
pgproto3/cancel_request.go
pgproto3/close.go
pgproto3/close_complete.go
pgproto3/command_complete.go
pgproto3/copy_both_response.go
pgproto3/copy_both_response_test.go
pgproto3/copy_data.go
pgproto3/copy_done.go
pgproto3/copy_fail.go
pgproto3/copy_in_response.go
pgproto3/copy_out_response.go
pgproto3/data_row.go
pgproto3/describe.go
pgproto3/empty_query_response.go
pgproto3/error_response.go
pgproto3/example/pgfortune/server.go
pgproto3/execute.go
pgproto3/flush.go
pgproto3/frontend.go
pgproto3/function_call.go
pgproto3/function_call_response.go
pgproto3/function_call_test.go
pgproto3/gss_enc_request.go
pgproto3/gss_response.go
pgproto3/no_data.go
pgproto3/notice_response.go
pgproto3/notification_response.go
pgproto3/parameter_description.go
pgproto3/parameter_status.go
pgproto3/parse.go
pgproto3/parse_complete.go
pgproto3/password_message.go
pgproto3/pgproto3.go
pgproto3/pgproto3_private_test.go
pgproto3/portal_suspended.go
pgproto3/query.go
pgproto3/query_test.go
pgproto3/ready_for_query.go
pgproto3/row_description.go
pgproto3/sasl_initial_response.go
pgproto3/sasl_response.go
pgproto3/ssl_request.go
pgproto3/startup_message.go
pgproto3/sync.go
pgproto3/terminate.go
Commit message: Do not allow protocol messages larger than ~1GB

The PostgreSQL server will reject messages greater than ~1 GB anyway.
However, worse than that is that a message that is larger than 4 GB
could wrap the 32-bit integer message size and be interpreted by the
server as multiple messages. This could allow a malicious client to
inject arbitrary protocol messages.

https://github.com/jackc/pgx/security/advisories/GHSA-mrww-27vc-gghv


Vulnerable code:
	batch.buf = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)
	batch.buf = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)
	batch.buf = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)
	batch.buf = (&pgproto3.Execute{}).Encode(batch.buf)
	batch.buf = (&pgproto3.Sync{}).Encode(batch.buf)
				srv.Write((&pgproto3.AuthenticationOk{}).Encode(nil))
				srv.Write((&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: 0}).Encode(nil))
				srv.Write((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(nil))
func (src *AuthenticationCleartextPassword) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	dst = pgio.AppendInt32(dst, 8)
	return dst
func (a *AuthenticationGSS) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	dst = pgio.AppendInt32(dst, 4)
	return dst
func (a *AuthenticationGSSContinue) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	dst = pgio.AppendInt32(dst, int32(len(a.Data))+8)
	return dst
func (src *AuthenticationMD5Password) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	dst = pgio.AppendInt32(dst, 12)
	return dst
func (src *AuthenticationOk) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	dst = pgio.AppendInt32(dst, 8)
	return dst
func (src *AuthenticationSASL) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *AuthenticationSASLContinue) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *AuthenticationSASLFinal) Encode(dst []byte) []byte {
	dst = append(dst, 'R')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
	wbuf []byte
// Send sends a message to the frontend (i.e. the client). The message is not guaranteed to be written until Flush is
// called.
	b.wbuf = msg.Encode(b.wbuf)
func (src *BackendKeyData) Encode(dst []byte) []byte {
	dst = append(dst, 'K')
	dst = pgio.AppendUint32(dst, 12)
	return dst
		dst := []byte{}
		dst = want.Encode(dst)
func (src *Bind) Encode(dst []byte) []byte {
	dst = append(dst, 'B')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *BindComplete) Encode(dst []byte) []byte {
	return append(dst, '2', 0, 0, 0, 4)
func (src *CancelRequest) Encode(dst []byte) []byte {
	return dst

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *Close) Encode(dst []byte) []byte {
	dst = append(dst, 'C')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *CloseComplete) Encode(dst []byte) []byte {
	return append(dst, '3', 0, 0, 0, 4)

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *CommandComplete) Encode(dst []byte) []byte {
	dst = append(dst, 'C')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *CopyBothResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'W')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
	dstBytes = dstResp.Encode(dstBytes)

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *CopyData) Encode(dst []byte) []byte {
	dst = append(dst, 'd')
	dst = pgio.AppendInt32(dst, int32(4+len(src.Data)))
	return dst
func (src *CopyDone) Encode(dst []byte) []byte {
	return append(dst, 'c', 0, 0, 0, 4)

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *CopyFail) Encode(dst []byte) []byte {
	dst = append(dst, 'f')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *CopyInResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'G')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *CopyOutResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'H')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *DataRow) Encode(dst []byte) []byte {
	dst = append(dst, 'D')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *Describe) Encode(dst []byte) []byte {
	dst = append(dst, 'D')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *EmptyQueryResponse) Encode(dst []byte) []byte {
	return append(dst, 'I', 0, 0, 0, 4)
	"encoding/binary"
func (src *ErrorResponse) Encode(dst []byte) []byte {
	return append(dst, src.marshalBinary('E')...)
func (src *ErrorResponse) marshalBinary(typeByte byte) []byte {
	var bigEndian BigEndianBuf
	buf := &bytes.Buffer{}

	buf.WriteByte(typeByte)
	buf.Write(bigEndian.Uint32(0))

		buf.WriteByte('S')
		buf.WriteString(src.Severity)
		buf.WriteByte(0)
		buf.WriteByte('V')
		buf.WriteString(src.SeverityUnlocalized)
		buf.WriteByte(0)
		buf.WriteByte('C')
		buf.WriteString(src.Code)
		buf.WriteByte(0)
		buf.WriteByte('M')
		buf.WriteString(src.Message)
		buf.WriteByte(0)
		buf.WriteByte('D')
		buf.WriteString(src.Detail)
		buf.WriteByte(0)
		buf.WriteByte('H')
		buf.WriteString(src.Hint)
		buf.WriteByte(0)
		buf.WriteByte('P')
		buf.WriteString(strconv.Itoa(int(src.Position)))
		buf.WriteByte(0)
		buf.WriteByte('p')
		buf.WriteString(strconv.Itoa(int(src.InternalPosition)))
		buf.WriteByte(0)
		buf.WriteByte('q')
		buf.WriteString(src.InternalQuery)
		buf.WriteByte(0)
		buf.WriteByte('W')
		buf.WriteString(src.Where)
		buf.WriteByte(0)
		buf.WriteByte('s')
		buf.WriteString(src.SchemaName)
		buf.WriteByte(0)
		buf.WriteByte('t')
		buf.WriteString(src.TableName)
		buf.WriteByte(0)
		buf.WriteByte('c')
		buf.WriteString(src.ColumnName)
		buf.WriteByte(0)
		buf.WriteByte('d')
		buf.WriteString(src.DataTypeName)
		buf.WriteByte(0)
		buf.WriteByte('n')
		buf.WriteString(src.ConstraintName)
		buf.WriteByte(0)
		buf.WriteByte('F')
		buf.WriteString(src.File)
		buf.WriteByte(0)
		buf.WriteByte('L')
		buf.WriteString(strconv.Itoa(int(src.Line)))
		buf.WriteByte(0)
		buf.WriteByte('R')
		buf.WriteString(src.Routine)
		buf.WriteByte(0)
		buf.WriteByte(k)
		buf.WriteString(v)
		buf.WriteByte(0)
	buf.WriteByte(0)

	binary.BigEndian.PutUint32(buf.Bytes()[1:5], uint32(buf.Len()-1))
	return buf.Bytes()
			buf := (&pgproto3.RowDescription{Fields: []pgproto3.FieldDescription{
			}}).Encode(nil)
			buf = (&pgproto3.DataRow{Values: [][]byte{response}}).Encode(buf)
			buf = (&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")}).Encode(buf)
			buf = (&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(buf)
		buf := (&pgproto3.AuthenticationOk{}).Encode(nil)
		buf = (&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(buf)
func (src *Execute) Encode(dst []byte) []byte {
	dst = append(dst, 'E')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)



	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *Flush) Encode(dst []byte) []byte {
	return append(dst, 'H', 0, 0, 0, 4)
	wbuf []byte
// Send sends a message to the backend (i.e. the server). The message is not guaranteed to be written until Flush is
// called.
	f.wbuf = msg.Encode(f.wbuf)
// SendBind sends a Bind message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
// SendParse sends a Parse message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
// SendClose sends a Close message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
// SendDescribe sends a Describe message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
// SendExecute sends an Execute message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
// SendSync sends a Sync message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
// SendQuery sends a Query message to the backend (i.e. the server). The message is not guaranteed to be written until
// Flush is called.
	f.wbuf = msg.Encode(f.wbuf)
func (src *FunctionCall) Encode(dst []byte) []byte {
	dst = append(dst, 'F')
	sp := len(dst)
	dst = pgio.AppendUint32(dst, 0) // Unknown length, set it at the end
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
	return dst
func (src *FunctionCallResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'V')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
			encoded := src.Encode([]byte{})
			err := dst.Decode(encoded[5:])
func (src *GSSEncRequest) Encode(dst []byte) []byte {
	return dst

	"github.com/jackc/pgx/v5/internal/pgio"
func (g *GSSResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'p')
	dst = pgio.AppendInt32(dst, int32(4+len(g.Data)))
	return dst
func (src *NoData) Encode(dst []byte) []byte {
	return append(dst, 'n', 0, 0, 0, 4)
func (src *NoticeResponse) Encode(dst []byte) []byte {
	return append(dst, (*ErrorResponse)(src).marshalBinary('N')...)
func (src *NotificationResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'A')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *ParameterDescription) Encode(dst []byte) []byte {
	dst = append(dst, 't')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *ParameterStatus) Encode(dst []byte) []byte {
	dst = append(dst, 'S')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)


	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *Parse) Encode(dst []byte) []byte {
	dst = append(dst, 'P')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *ParseComplete) Encode(dst []byte) []byte {
	return append(dst, '1', 0, 0, 0, 4)

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *PasswordMessage) Encode(dst []byte) []byte {
	dst = append(dst, 'p')
	dst = pgio.AppendInt32(dst, int32(4+len(src.Password)+1))


	return dst
	Encode(dst []byte) []byte
func (src *PortalSuspended) Encode(dst []byte) []byte {
	return append(dst, 's', 0, 0, 0, 4)

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *Query) Encode(dst []byte) []byte {
	dst = append(dst, 'Q')
	dst = pgio.AppendInt32(dst, int32(4+len(src.String)+1))


	return dst
func (src *ReadyForQuery) Encode(dst []byte) []byte {
	return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus)
func (src *RowDescription) Encode(dst []byte) []byte {
	dst = append(dst, 'T')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *SASLInitialResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'p')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst

	"github.com/jackc/pgx/v5/internal/pgio"
func (src *SASLResponse) Encode(dst []byte) []byte {
	dst = append(dst, 'p')
	dst = pgio.AppendInt32(dst, int32(4+len(src.Data)))


	return dst
func (src *SSLRequest) Encode(dst []byte) []byte {
	return dst
func (src *StartupMessage) Encode(dst []byte) []byte {
	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
func (src *Sync) Encode(dst []byte) []byte {
	return append(dst, 'S', 0, 0, 0, 4)
func (src *Terminate) Encode(dst []byte) []byte {
	return append(dst, 'X', 0, 0, 0, 4)

Fixed code:
	err error
	if batch.err != nil {
		return
	}

	batch.buf, batch.err = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)
	if batch.err != nil {
		return
	}
	if batch.err != nil {
		return
	}

	batch.buf, batch.err = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)
	if batch.err != nil {
		return
	}

	batch.buf, batch.err = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)
	if batch.err != nil {
		return
	}

	batch.buf, batch.err = (&pgproto3.Execute{}).Encode(batch.buf)
	if batch.err != nil {
		return
	}
	if batch.err != nil {
		return &MultiResultReader{
			closed: true,
			err:    batch.err,
		}
	}

	batch.buf, batch.err = (&pgproto3.Sync{}).Encode(batch.buf)
	if batch.err != nil {
		multiResult.closed = true
		multiResult.err = batch.err
		pgConn.unlock()
		return multiResult
	}
				srv.Write(mustEncode((&pgproto3.AuthenticationOk{}).Encode(nil)))
				srv.Write(mustEncode((&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: 0}).Encode(nil)))
				srv.Write(mustEncode((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(nil)))

func mustEncode(buf []byte, err error) []byte {
	if err != nil {
		panic(err)
	}
	return buf
}
func (src *AuthenticationCleartextPassword) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (a *AuthenticationGSS) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (a *AuthenticationGSSContinue) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (src *AuthenticationMD5Password) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (src *AuthenticationOk) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (src *AuthenticationSASL) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (src *AuthenticationSASLContinue) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
func (src *AuthenticationSASLFinal) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'R')
	return finishMessage(dst, sp)
	wbuf        []byte
	encodeError error
// Send sends a message to the frontend (i.e. the client). The message is buffered until Flush is called. Any error
// encountered will be returned from Flush.
	if b.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(b.wbuf)
	if err != nil {
		b.encodeError = err
		return
	}
	b.wbuf = newBuf

	if err := b.encodeError; err != nil {
		b.encodeError = nil
		b.wbuf = b.wbuf[:0]
		return &writeError{err: err, safeToRetry: true}
	}

func (src *BackendKeyData) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'K')
	return finishMessage(dst, sp)
		dst, err := want.Encode([]byte{})
		require.NoError(t, err)
func (src *Bind) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'B')
	return finishMessage(dst, sp)
func (src *BindComplete) Encode(dst []byte) ([]byte, error) {
	return append(dst, '2', 0, 0, 0, 4), nil
package pgproto3_test

import (
	"testing"

	"github.com/jackc/pgx/v5/pgproto3"
	"github.com/stretchr/testify/require"
)

func TestBindBiggerThanMaxMessageBodyLen(t *testing.T) {
	t.Parallel()

	// Maximum allowed size.
	_, err := (&pgproto3.Bind{Parameters: [][]byte{make([]byte, pgproto3.MaxMessageBodyLen-16)}}).Encode(nil)
	require.NoError(t, err)

	// 1 byte too big
	_, err = (&pgproto3.Bind{Parameters: [][]byte{make([]byte, pgproto3.MaxMessageBodyLen-15)}}).Encode(nil)
	require.Error(t, err)
}
func (src *CancelRequest) Encode(dst []byte) ([]byte, error) {
	return dst, nil
func (src *Close) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'C')
	return finishMessage(dst, sp)
func (src *CloseComplete) Encode(dst []byte) ([]byte, error) {
	return append(dst, '3', 0, 0, 0, 4), nil
func (src *CommandComplete) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'C')
	return finishMessage(dst, sp)
func (src *CopyBothResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'W')
	return finishMessage(dst, sp)
	"github.com/stretchr/testify/require"
	dstBytes, err = dstResp.Encode(dstBytes)
	require.NoError(t, err)
func (src *CopyData) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'd')
	return finishMessage(dst, sp)
func (src *CopyDone) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'c', 0, 0, 0, 4), nil
func (src *CopyFail) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'f')
	return finishMessage(dst, sp)
func (src *CopyInResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'G')
	return finishMessage(dst, sp)
func (src *CopyOutResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'H')
	return finishMessage(dst, sp)
func (src *DataRow) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'D')
	return finishMessage(dst, sp)
func (src *Describe) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'D')
	return finishMessage(dst, sp)
func (src *EmptyQueryResponse) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'I', 0, 0, 0, 4), nil
func (src *ErrorResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'E')
	dst = src.appendFields(dst)
	return finishMessage(dst, sp)
func (src *ErrorResponse) appendFields(dst []byte) []byte {
		dst = append(dst, 'S')
		dst = append(dst, src.Severity...)
		dst = append(dst, 0)
		dst = append(dst, 'V')
		dst = append(dst, src.SeverityUnlocalized...)
		dst = append(dst, 0)
		dst = append(dst, 'C')
		dst = append(dst, src.Code...)
		dst = append(dst, 0)
		dst = append(dst, 'M')
		dst = append(dst, src.Message...)
		dst = append(dst, 0)
		dst = append(dst, 'D')
		dst = append(dst, src.Detail...)
		dst = append(dst, 0)
		dst = append(dst, 'H')
		dst = append(dst, src.Hint...)
		dst = append(dst, 0)
		dst = append(dst, 'P')
		dst = append(dst, strconv.Itoa(int(src.Position))...)
		dst = append(dst, 0)
		dst = append(dst, 'p')
		dst = append(dst, strconv.Itoa(int(src.InternalPosition))...)
		dst = append(dst, 0)
		dst = append(dst, 'q')
		dst = append(dst, src.InternalQuery...)
		dst = append(dst, 0)
		dst = append(dst, 'W')
		dst = append(dst, src.Where...)
		dst = append(dst, 0)
		dst = append(dst, 's')
		dst = append(dst, src.SchemaName...)
		dst = append(dst, 0)
		dst = append(dst, 't')
		dst = append(dst, src.TableName...)
		dst = append(dst, 0)
		dst = append(dst, 'c')
		dst = append(dst, src.ColumnName...)
		dst = append(dst, 0)
		dst = append(dst, 'd')
		dst = append(dst, src.DataTypeName...)
		dst = append(dst, 0)
		dst = append(dst, 'n')
		dst = append(dst, src.ConstraintName...)
		dst = append(dst, 0)
		dst = append(dst, 'F')
		dst = append(dst, src.File...)
		dst = append(dst, 0)
		dst = append(dst, 'L')
		dst = append(dst, strconv.Itoa(int(src.Line))...)
		dst = append(dst, 0)
		dst = append(dst, 'R')
		dst = append(dst, src.Routine...)
		dst = append(dst, 0)
		dst = append(dst, k)
		dst = append(dst, v...)
		dst = append(dst, 0)
	dst = append(dst, 0)
	return dst
			buf := mustEncode((&pgproto3.RowDescription{Fields: []pgproto3.FieldDescription{
			}}).Encode(nil))
			buf = mustEncode((&pgproto3.DataRow{Values: [][]byte{response}}).Encode(buf))
			buf = mustEncode((&pgproto3.CommandComplete{CommandTag: []byte("SELECT 1")}).Encode(buf))
			buf = mustEncode((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(buf))
		buf := mustEncode((&pgproto3.AuthenticationOk{}).Encode(nil))
		buf = mustEncode((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(buf))

func mustEncode(buf []byte, err error) []byte {
	if err != nil {
		panic(err)
	}
	return buf
}
func (src *Execute) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'E')
	return finishMessage(dst, sp)
func (src *Flush) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'H', 0, 0, 0, 4), nil
	wbuf        []byte
	encodeError error
// Send sends a message to the backend (i.e. the server). The message is buffered until Flush is called. Any error
// encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

	if err := f.encodeError; err != nil {
		f.encodeError = nil
		f.wbuf = f.wbuf[:0]
		return &writeError{err: err, safeToRetry: true}
	}

// SendBind sends a Bind message to the backend (i.e. the server). The message is buffered until Flush is called. Any
// error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

// SendParse sends a Parse message to the backend (i.e. the server). The message is buffered until Flush is called. Any
// error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

// SendClose sends a Close message to the backend (i.e. the server). The message is buffered until Flush is called. Any
// error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

// SendDescribe sends a Describe message to the backend (i.e. the server). The message is buffered until Flush is
// called. Any error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

// SendExecute sends an Execute message to the backend (i.e. the server). The message is buffered until Flush is called.
// Any error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

// SendSync sends a Sync message to the backend (i.e. the server). The message is buffered until Flush is called. Any
// error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

// SendQuery sends a Query message to the backend (i.e. the server). The message is buffered until Flush is called. Any
// error encountered will be returned from Flush.
	if f.encodeError != nil {
		return
	}

	newBuf, err := msg.Encode(f.wbuf)
	if err != nil {
		f.encodeError = err
		return
	}
	f.wbuf = newBuf

func (src *FunctionCall) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'F')
	return finishMessage(dst, sp)
func (src *FunctionCallResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'V')
	return finishMessage(dst, sp)

	"github.com/stretchr/testify/require"
			encoded, err := src.Encode([]byte{})
			require.NoError(t, err)
			err = dst.Decode(encoded[5:])
func (src *GSSEncRequest) Encode(dst []byte) ([]byte, error) {
	return dst, nil
func (g *GSSResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'p')
	return finishMessage(dst, sp)
func (src *NoData) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'n', 0, 0, 0, 4), nil
func (src *NoticeResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'N')
	dst = (*ErrorResponse)(src).appendFields(dst)
	return finishMessage(dst, sp)
func (src *NotificationResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'A')
	return finishMessage(dst, sp)
func (src *ParameterDescription) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 't')
	return finishMessage(dst, sp)
func (src *ParameterStatus) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'S')
	return finishMessage(dst, sp)
func (src *Parse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'P')
	return finishMessage(dst, sp)
func (src *ParseComplete) Encode(dst []byte) ([]byte, error) {
	return append(dst, '1', 0, 0, 0, 4), nil
func (src *PasswordMessage) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'p')
	return finishMessage(dst, sp)

	"github.com/jackc/pgx/v5/internal/pgio"
// maxMessageBodyLen is the maximum length of a message body in bytes. See PG_LARGE_MESSAGE_LIMIT in the PostgreSQL
// source. It is defined as (MaxAllocSize - 1). MaxAllocSize is defined as 0x3fffffff.
const maxMessageBodyLen = (0x3fffffff - 1)

	Encode(dst []byte) ([]byte, error)

// beginMessage begines a new message of type t. It appends the message type and a placeholder for the message length to
// dst. It returns the new buffer and the position of the message length placeholder.
func beginMessage(dst []byte, t byte) ([]byte, int) {
	dst = append(dst, t)
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)
	return dst, sp
}

// finishMessage finishes a message that was started with beginMessage. It computes the message length and writes it to
// dst[sp]. If the message length is too large it returns an error. Otherwise it returns the final message buffer.
func finishMessage(dst []byte, sp int) ([]byte, error) {
	messageBodyLen := len(dst[sp:])
	if messageBodyLen > maxMessageBodyLen {
		return nil, errors.New("message body too large")
	}
	pgio.SetInt32(dst[sp:], int32(messageBodyLen))
	return dst, nil
}
package pgproto3

const MaxMessageBodyLen = maxMessageBodyLen
func (src *PortalSuspended) Encode(dst []byte) ([]byte, error) {
	return append(dst, 's', 0, 0, 0, 4), nil
func (src *Query) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'Q')
	return finishMessage(dst, sp)
package pgproto3_test

import (
	"testing"

	"github.com/jackc/pgx/v5/pgproto3"
	"github.com/stretchr/testify/require"
)

func TestQueryBiggerThanMaxMessageBodyLen(t *testing.T) {
	t.Parallel()

	// Maximum allowed size. 4 bytes for size and 1 byte for 0 terminated string.
	_, err := (&pgproto3.Query{String: string(make([]byte, pgproto3.MaxMessageBodyLen-5))}).Encode(nil)
	require.NoError(t, err)

	// 1 byte too big
	_, err = (&pgproto3.Query{String: string(make([]byte, pgproto3.MaxMessageBodyLen-4))}).Encode(nil)
	require.Error(t, err)
}
func (src *ReadyForQuery) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus), nil
func (src *RowDescription) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'T')
	return finishMessage(dst, sp)
func (src *SASLInitialResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'p')
	return finishMessage(dst, sp)
func (src *SASLResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'p')
	return finishMessage(dst, sp)
func (src *SSLRequest) Encode(dst []byte) ([]byte, error) {
	return dst, nil
func (src *StartupMessage) Encode(dst []byte) ([]byte, error) {
	return finishMessage(dst, sp)
func (src *Sync) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'S', 0, 0, 0, 4), nil
func (src *Terminate) Encode(dst []byte) ([]byte, error) {
	return append(dst, 'X', 0, 0, 0, 4), nil