Skip to content

Commit

Permalink
feat(pgproto3/backend): add a SetMaxBodyLen to limit the max body len…
Browse files Browse the repository at this point in the history
…gth for the receive
  • Loading branch information
jeremy.spriet authored and jackc committed Dec 23, 2023
1 parent 9ab9e3c commit 603c8c1
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 0 deletions.
13 changes: 13 additions & 0 deletions pgproto3/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Backend struct {
terminate Terminate

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 @@ -158,6 +159,9 @@ func (b *Backend) Receive() (FrontendMessage, error) {

b.msgType = header[0]
b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
}
b.partialMsg = true
}

Expand Down Expand Up @@ -260,3 +264,12 @@ 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.
// The default value is 0.
// If maxBodyLen is 0, then no maximum is enforced.
func (b *Backend) SetMaxBodyLen(maxBodyLen int) {
b.maxBodyLen = maxBodyLen
}
18 changes: 18 additions & 0 deletions pgproto3/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,21 @@ func TestStartupMessage(t *testing.T) {
}
})
}

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

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

backend := pgproto3.NewBackend(server, nil)

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

// Receive regular msg
msg, err := backend.Receive()
assert.Nil(t, msg)
var invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr
assert.ErrorAs(t, err, &invalidBodyLenErr)
}
9 changes: 9 additions & 0 deletions pgproto3/pgproto3.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ func (e *writeError) Unwrap() error {
return e.err
}

type ExceededMaxBodyLenErr struct {
maxExpectedBodyLen int
actualBodyLen int
}

func (e *ExceededMaxBodyLenErr) Error() string {
return fmt.Sprintf("invalid body length: expected at most %d, but got %d", e.maxExpectedBodyLen, e.actualBodyLen)
}

// getValueFromJSON gets the value from a protocol message representation in JSON.
func getValueFromJSON(v map[string]string) ([]byte, error) {
if v == nil {
Expand Down

0 comments on commit 603c8c1

Please sign in to comment.