Visitar URL original
pgtype: bound range, multirange, and tsvector binary decoders · jackc/pgx@b4d6d4d · GitHub
Skip to content

Commit b4d6d4d

Browse files
sean-jackc
authored andcommitted
pgtype: bound range, multirange, and tsvector binary decoders
The same length-prefix-vs-remaining-bytes pattern that affects array and hstore binary decoding also affected three more decoders: - parseUntypedBinaryRange sliced lower/upper bound bytes without checking the server-supplied valueLen against remaining bytes. - MultirangeCodec.decodeBinary looped elementCount times reading and slicing per-element bytes without bounds checks. - tsvector binary decode read an int32 entryCount and called make([]TSVectorLexeme, entryCount) without checking the negative or oversized cases. Add the same remaining-bytes checks as F05/F06: validate Uint32 reads have at least 4 bytes available, validate per-value/per-element slice lengths against remaining bytes, reject negative tsvector lexeme counts, and bound entryCount against len(src[rp:]) / 3 (each lexeme needs at minimum a 1-byte NUL terminator and a 2-byte position count). Reported by CrowdStrike (F11).
1 parent 0639b37 commit b4d6d4d

3 files changed

Lines changed: 22 additions & 0 deletions

File tree

‎pgtype/multirange.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,16 @@ func (c *MultirangeCodec) decodeBinary(m *Map, multirangeOID uint32, src []byte,
231231

232232
for i := range elementCount {
233233
elem := multirange.ScanIndex(i)
234+
if len(src[rp:]) < 4 {
235+
return fmt.Errorf("multirange body truncated at element %d", i)
236+
}
234237
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
235238
rp += 4
236239
var elemSrc []byte
237240
if elemLen >= 0 {
241+
if len(src[rp:]) < elemLen {
242+
return fmt.Errorf("multirange element %d length %d exceeds remaining %d bytes", i, elemLen, len(src[rp:]))
243+
}
238244
elemSrc = src[rp : rp+elemLen]
239245
rp += elemLen
240246
}

‎pgtype/range.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ func parseUntypedBinaryRange(src []byte) (*untypedBinaryRange, error) {
249249
valueLen := int(binary.BigEndian.Uint32(src[rp:]))
250250
rp += 4
251251

252+
if valueLen < 0 || len(src[rp:]) < valueLen {
253+
return nil, fmt.Errorf("range lower bound length %d exceeds remaining %d bytes", valueLen, len(src[rp:]))
254+
}
252255
val := src[rp : rp+valueLen]
253256
rp += valueLen
254257

@@ -268,6 +271,9 @@ func parseUntypedBinaryRange(src []byte) (*untypedBinaryRange, error) {
268271
}
269272
valueLen := int(binary.BigEndian.Uint32(src[rp:]))
270273
rp += 4
274+
if valueLen < 0 || len(src[rp:]) < valueLen {
275+
return nil, fmt.Errorf("range upper bound length %d exceeds remaining %d bytes", valueLen, len(src[rp:]))
276+
}
271277
ubr.Upper = src[rp : rp+valueLen]
272278
rp += valueLen
273279
}

‎pgtype/tsvector.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@ func (scanPlanBinaryTSVectorToTSVectorScanner) Scan(src []byte, dst any) error {
203203
entryCount := int(int32(binary.BigEndian.Uint32(src[rp:])))
204204
rp += uint32Len
205205

206+
if entryCount < 0 {
207+
return fmt.Errorf("tsvector invalid lexeme count: %d", entryCount)
208+
}
209+
// Each lexeme carries at minimum a 1-byte NUL terminator and a 2-byte position count, so
210+
// entryCount cannot exceed remaining/3. This bounds the up-front make() against a malicious
211+
// server claiming a huge lexeme count in a small message.
212+
if maxEntries := len(src[rp:]) / 3; entryCount > maxEntries {
213+
return fmt.Errorf("tsvector invalid lexeme count %d for %d remaining bytes", entryCount, len(src[rp:]))
214+
}
215+
206216
var tsv TSVector
207217
if entryCount > 0 {
208218
tsv.Lexemes = make([]TSVectorLexeme, entryCount)

0 commit comments

Comments
 (0)