Skip to content

Commit

Permalink
Merge pull request #2138 from zenkovev/message_body_size_limit
Browse files Browse the repository at this point in the history
add message body size limits in frontend and backend
  • Loading branch information
jackc authored Oct 5, 2024
2 parents 123b59a + 10e1195 commit cc05954
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 4 deletions.
15 changes: 11 additions & 4 deletions pgproto3/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,13 @@ func (b *Backend) Receive() (FrontendMessage, error) {
}

b.msgType = header[0]
b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4

msgLength := int(binary.BigEndian.Uint32(header[1:]))
if msgLength < 4 {
return nil, fmt.Errorf("invalid message length: %d", msgLength)
}

b.bodyLen = msgLength - 4
if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
}
Expand Down Expand Up @@ -282,9 +288,10 @@ func (b *Backend) SetAuthType(authType uint32) error {
return nil
}

// SetMaxBodyLen sets the maximum length of a message body in octets. If a message body exceeds this length, Receive will return
// an error. This is useful for protecting against malicious clients that send large messages with the intent of
// causing memory exhaustion.
// SetMaxBodyLen sets the maximum length of a message body in octets.
// If a message body exceeds this length, Receive will return an error.
// This is useful for protecting against malicious clients that send
// large messages with the intent of causing memory exhaustion.
// The default value is 0.
// If maxBodyLen is 0, then no maximum is enforced.
func (b *Backend) SetMaxBodyLen(maxBodyLen int) {
Expand Down
14 changes: 14 additions & 0 deletions pgproto3/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Frontend struct {
portalSuspended PortalSuspended

bodyLen int
maxBodyLen int // maxBodyLen is the maximum length of a message body in octets. If a message body exceeds this length, Receive will return an error.
msgType byte
partialMsg bool
authType uint32
Expand Down Expand Up @@ -317,6 +318,9 @@ func (f *Frontend) Receive() (BackendMessage, error) {
}

f.bodyLen = msgLength - 4
if f.maxBodyLen > 0 && f.bodyLen > f.maxBodyLen {
return nil, &ExceededMaxBodyLenErr{f.maxBodyLen, f.bodyLen}
}
f.partialMsg = true
}

Expand Down Expand Up @@ -452,3 +456,13 @@ func (f *Frontend) GetAuthType() uint32 {
func (f *Frontend) ReadBufferLen() int {
return f.cr.wp - f.cr.rp
}

// SetMaxBodyLen sets the maximum length of a message body in octets.
// If a message body exceeds this length, Receive will return an error.
// This is useful for protecting against a corrupted server that sends
// messages with incorrect length, which can cause memory exhaustion.
// The default value is 0.
// If maxBodyLen is 0, then no maximum is enforced.
func (f *Frontend) SetMaxBodyLen(maxBodyLen int) {
f.maxBodyLen = maxBodyLen
}
18 changes: 18 additions & 0 deletions pgproto3/frontend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,21 @@ func TestErrorResponse(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, want, got)
}

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

client := &interruptReader{}
client.push([]byte{'D', 0, 0, 10, 10})

frontend := pgproto3.NewFrontend(client, nil)

// Set max body len to 5
frontend.SetMaxBodyLen(5)

// Receive regular msg
msg, err := frontend.Receive()
assert.Nil(t, msg)
var invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr
assert.ErrorAs(t, err, &invalidBodyLenErr)
}

0 comments on commit cc05954

Please sign in to comment.