Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8f8470e

Browse files
author
zene
committed
add message body size limits in frontend and backend
1 parent a95cfbb commit 8f8470e

3 files changed

Lines changed: 94 additions & 5 deletions

File tree

pgproto3/backend.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,27 @@ import (
55
"encoding/binary"
66
"fmt"
77
"io"
8+
"sync/atomic"
89
)
910

11+
var (
12+
// When using the PostgreSQL driver, it is impossible to set a limit using
13+
// the structure method, however, sometimes it is necessary to set a limit
14+
// for the safety of the application. A similar functionality
15+
// has been made for client messages.
16+
commonMaxBackendBodyLen atomic.Uint32
17+
)
18+
19+
// SetCommonMaxBackendBodyLen sets the maximum length of a message body in octets.
20+
// If a message body exceeds this length, Receive will return an error.
21+
// This is useful for protecting against malicious clients that send
22+
// large messages with the intent of causing memory exhaustion.
23+
// The default value is 0.
24+
// If value is 0, then no maximum is enforced.
25+
func SetCommonMaxBackendBodyLen(value uint32) {
26+
commonMaxBackendBodyLen.Store(value)
27+
}
28+
1029
// Backend acts as a server for the PostgreSQL wire protocol version 3.
1130
type Backend struct {
1231
cr *chunkReader
@@ -39,7 +58,9 @@ type Backend struct {
3958
terminate Terminate
4059

4160
bodyLen int
42-
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.
61+
maxBodyLen int
62+
// maxBodyLen is the maximum length of a message body in octets.
63+
// If a message body exceeds this length, Receive will return an error.
4364
msgType byte
4465
partialMsg bool
4566
authType uint32
@@ -175,10 +196,20 @@ func (b *Backend) Receive() (FrontendMessage, error) {
175196
}
176197

177198
b.msgType = header[0]
178-
b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
199+
200+
msgLength := int(binary.BigEndian.Uint32(header[1:]))
201+
if msgLength < 4 {
202+
return nil, fmt.Errorf("invalid message length: %d", msgLength)
203+
}
204+
205+
b.bodyLen = msgLength - 4
179206
if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
180207
return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
181208
}
209+
commonMaxBodyLen := int(commonMaxBackendBodyLen.Load())
210+
if commonMaxBodyLen > 0 && b.bodyLen > commonMaxBodyLen {
211+
return nil, &ExceededMaxBodyLenErr{commonMaxBodyLen, b.bodyLen}
212+
}
182213
b.partialMsg = true
183214
}
184215

@@ -282,9 +313,10 @@ func (b *Backend) SetAuthType(authType uint32) error {
282313
return nil
283314
}
284315

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.
316+
// SetMaxBodyLen sets the maximum length of a message body in octets.
317+
// If a message body exceeds this length, Receive will return an error.
318+
// This is useful for protecting against malicious clients that send
319+
// large messages with the intent of causing memory exhaustion.
288320
// The default value is 0.
289321
// If maxBodyLen is 0, then no maximum is enforced.
290322
func (b *Backend) SetMaxBodyLen(maxBodyLen int) {

pgproto3/frontend.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,27 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"sync/atomic"
910
)
1011

12+
var (
13+
// When using the PostgreSQL driver, it is impossible to set a limit using
14+
// the structure method, however, sometimes it is necessary to set a limit
15+
// for the safety of the application. A similar functionality
16+
// has been made for client messages.
17+
commonMaxFrontendBodyLen atomic.Uint32
18+
)
19+
20+
// SetCommonMaxFrontendBodyLen sets the maximum length of a message body in octets.
21+
// If a message body exceeds this length, Receive will return an error.
22+
// This is useful for protecting against a corrupted server that sends
23+
// messages with incorrect length, which can cause memory exhaustion.
24+
// The default value is 0.
25+
// If value is 0, then no maximum is enforced.
26+
func SetCommonMaxFrontendBodyLen(value uint32) {
27+
commonMaxFrontendBodyLen.Store(value)
28+
}
29+
1130
// Frontend acts as a client for the PostgreSQL wire protocol version 3.
1231
type Frontend struct {
1332
cr *chunkReader
@@ -54,6 +73,9 @@ type Frontend struct {
5473
portalSuspended PortalSuspended
5574

5675
bodyLen int
76+
maxBodyLen int
77+
// maxBodyLen is the maximum length of a message body in octets.
78+
// If a message body exceeds this length, Receive will return an error.
5779
msgType byte
5880
partialMsg bool
5981
authType uint32
@@ -317,6 +339,13 @@ func (f *Frontend) Receive() (BackendMessage, error) {
317339
}
318340

319341
f.bodyLen = msgLength - 4
342+
if f.maxBodyLen > 0 && f.bodyLen > f.maxBodyLen {
343+
return nil, &ExceededMaxBodyLenErr{f.maxBodyLen, f.bodyLen}
344+
}
345+
commonMaxBodyLen := int(commonMaxFrontendBodyLen.Load())
346+
if commonMaxBodyLen > 0 && f.bodyLen > commonMaxBodyLen {
347+
return nil, &ExceededMaxBodyLenErr{commonMaxBodyLen, f.bodyLen}
348+
}
320349
f.partialMsg = true
321350
}
322351

@@ -452,3 +481,13 @@ func (f *Frontend) GetAuthType() uint32 {
452481
func (f *Frontend) ReadBufferLen() int {
453482
return f.cr.wp - f.cr.rp
454483
}
484+
485+
// SetMaxBodyLen sets the maximum length of a message body in octets.
486+
// If a message body exceeds this length, Receive will return an error.
487+
// This is useful for protecting against a corrupted server that sends
488+
// messages with incorrect length, which can cause memory exhaustion.
489+
// The default value is 0.
490+
// If maxBodyLen is 0, then no maximum is enforced.
491+
func (f *Frontend) SetMaxBodyLen(maxBodyLen int) {
492+
f.maxBodyLen = maxBodyLen
493+
}

pgproto3/frontend_test.go

Lines changed: 18 additions & 0 deletions
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)