Skip to content

Commit cc05954

Browse files
authored
Merge pull request #2138 from zenkovev/message_body_size_limit
add message body size limits in frontend and backend
2 parents 123b59a + 10e1195 commit cc05954

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

pgproto3/backend.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,13 @@ func (b *Backend) Receive() (FrontendMessage, error) {
175175
}
176176

177177
b.msgType = header[0]
178-
b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
178+
179+
msgLength := int(binary.BigEndian.Uint32(header[1:]))
180+
if msgLength < 4 {
181+
return nil, fmt.Errorf("invalid message length: %d", msgLength)
182+
}
183+
184+
b.bodyLen = msgLength - 4
179185
if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
180186
return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
181187
}
@@ -282,9 +288,10 @@ func (b *Backend) SetAuthType(authType uint32) error {
282288
return nil
283289
}
284290

285-
// SetMaxBodyLen sets the maximum length of a message body in octets. If a message body exceeds this length, Receive will return
286-
// an error. This is useful for protecting against malicious clients that send large messages with the intent of
287-
// causing memory exhaustion.
291+
// SetMaxBodyLen sets the maximum length of a message body in octets.
292+
// If a message body exceeds this length, Receive will return an error.
293+
// This is useful for protecting against malicious clients that send
294+
// large messages with the intent of causing memory exhaustion.
288295
// The default value is 0.
289296
// If maxBodyLen is 0, then no maximum is enforced.
290297
func (b *Backend) SetMaxBodyLen(maxBodyLen int) {

pgproto3/frontend.go

+14
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type Frontend struct {
5454
portalSuspended PortalSuspended
5555

5656
bodyLen int
57+
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.
5758
msgType byte
5859
partialMsg bool
5960
authType uint32
@@ -317,6 +318,9 @@ func (f *Frontend) Receive() (BackendMessage, error) {
317318
}
318319

319320
f.bodyLen = msgLength - 4
321+
if f.maxBodyLen > 0 && f.bodyLen > f.maxBodyLen {
322+
return nil, &ExceededMaxBodyLenErr{f.maxBodyLen, f.bodyLen}
323+
}
320324
f.partialMsg = true
321325
}
322326

@@ -452,3 +456,13 @@ func (f *Frontend) GetAuthType() uint32 {
452456
func (f *Frontend) ReadBufferLen() int {
453457
return f.cr.wp - f.cr.rp
454458
}
459+
460+
// SetMaxBodyLen sets the maximum length of a message body in octets.
461+
// If a message body exceeds this length, Receive will return an error.
462+
// This is useful for protecting against a corrupted server that sends
463+
// messages with incorrect length, which can cause memory exhaustion.
464+
// The default value is 0.
465+
// If maxBodyLen is 0, then no maximum is enforced.
466+
func (f *Frontend) SetMaxBodyLen(maxBodyLen int) {
467+
f.maxBodyLen = maxBodyLen
468+
}

pgproto3/frontend_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,21 @@ func TestErrorResponse(t *testing.T) {
115115
require.NoError(t, err)
116116
assert.Equal(t, want, got)
117117
}
118+
119+
func TestFrontendReceiveExceededMaxBodyLen(t *testing.T) {
120+
t.Parallel()
121+
122+
client := &interruptReader{}
123+
client.push([]byte{'D', 0, 0, 10, 10})
124+
125+
frontend := pgproto3.NewFrontend(client, nil)
126+
127+
// Set max body len to 5
128+
frontend.SetMaxBodyLen(5)
129+
130+
// Receive regular msg
131+
msg, err := frontend.Receive()
132+
assert.Nil(t, msg)
133+
var invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr
134+
assert.ErrorAs(t, err, &invalidBodyLenErr)
135+
}

0 commit comments

Comments
 (0)