Skip to content

Commit 58cb282

Browse files
Fix buffer overflow panics in frame metadata parsing
- Add bounds checking in buffered.go to prevent slice out-of-range panics when processing malformed RSocket frames with invalid metadata offsets - Validate offset before accessing frame body to avoid panic on released frames - Use overflow-safe arithmetic for metadata offset calculations - Add comprehensive bounds checking in composite_metadata.go for malformed composite metadata to prevent panics during metadata parsing - Replace magic number 3 with metadataLengthFieldSize constant for clarity Fixes panic in trySliceData when offset exceeds body length, which occurred during RSocket setup frame processing with malformed metadata length fields.
1 parent 2a2c1cf commit 58cb282

2 files changed

Lines changed: 60 additions & 7 deletions

File tree

core/framing/buffered.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
uberatomic "go.uber.org/atomic"
1313
)
1414

15+
const metadataLengthFieldSize = 3 // Size of u24 metadata length field in bytes
16+
1517
// bufferedFrame is basic frame implementation.
1618
type bufferedFrame struct {
1719
innerPtr unsafe.Pointer
@@ -114,15 +116,22 @@ func (f *bufferedFrame) WriteTo(w io.Writer) (n int64, err error) {
114116
}
115117

116118
func (f *bufferedFrame) trySeekMetadataLen(offset int) (n int, hasMetadata bool) {
119+
// Validate offset before accessing frame to avoid panic on released frames
120+
if offset < 0 {
121+
return -1, false
122+
}
117123
raw := f.Body()
124+
if raw == nil || offset >= len(raw) {
125+
return -1, false
126+
}
118127
if offset > 0 {
119128
raw = raw[offset:]
120129
}
121130
hasMetadata = f.HasFlag(core.FlagMetadata)
122131
if !hasMetadata {
123132
return
124133
}
125-
if len(raw) < 3 {
134+
if len(raw) < metadataLengthFieldSize {
126135
n = -1
127136
} else {
128137
n = u24.NewUint24Bytes(raw).AsInt()
@@ -135,18 +144,44 @@ func (f *bufferedFrame) trySliceMetadata(offset int) ([]byte, bool) {
135144
if !ok || n < 0 {
136145
return nil, false
137146
}
138-
return f.Body()[offset+3 : offset+3+n], true
147+
body := f.Body()
148+
if body == nil {
149+
return nil, false
150+
}
151+
// Overflow-safe bounds check
152+
metadataOffset := offset + metadataLengthFieldSize
153+
if metadataOffset > len(body) {
154+
return nil, false
155+
}
156+
if n > len(body)-metadataOffset {
157+
return nil, false
158+
}
159+
return body[metadataOffset : metadataOffset+n], true
139160
}
140161

141162
func (f *bufferedFrame) trySliceData(offset int) []byte {
163+
body := f.Body()
164+
if body == nil {
165+
return nil
166+
}
142167
n, ok := f.trySeekMetadataLen(offset)
143168
if !ok {
144-
return f.Body()[offset:]
169+
// trySeekMetadataLen already validated offset, safe to slice
170+
return body[offset:]
145171
}
146172
if n < 0 {
147173
return nil
148174
}
149-
return f.Body()[offset+n+3:]
175+
// Overflow-safe bounds check
176+
metadataOffset := offset + metadataLengthFieldSize
177+
if metadataOffset > len(body) {
178+
return nil
179+
}
180+
if n > len(body)-metadataOffset {
181+
return nil
182+
}
183+
dataOffset := metadataOffset + n
184+
return body[dataOffset:]
150185
}
151186

152187
func (f *bufferedFrame) bodyLen() int {

extension/composite_metadata.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package extension
22

33
import (
44
"bytes"
5+
"errors"
56
"fmt"
67
"github.com/rsocket/rsocket-go/internal/bytesconv"
78
"math"
@@ -57,7 +58,12 @@ func (c *CompositeMetadataScanner) Metadata() (mimeType string, metadata []byte,
5758
return
5859
}
5960

61+
const metadataLengthFieldSize = 3 // Size of u24 metadata length field in bytes
62+
6063
func (c *CompositeMetadataScanner) decodeCompositeMetadataOnce(raw []byte) (length int, mimeType string, metadata []byte, err error) {
64+
if len(raw) < 1 {
65+
return 0, "", nil, errors.New("composite metadata: insufficient data for header")
66+
}
6167
m := raw[0]
6268
size := 1
6369
idOrLen := (m << 1) >> 1
@@ -66,11 +72,23 @@ func (c *CompositeMetadataScanner) decodeCompositeMetadataOnce(raw []byte) (leng
6672
} else {
6773
mimeTypeLen := int(idOrLen) + 1
6874
size += mimeTypeLen
75+
if size > len(raw) {
76+
return 0, "", nil, errors.New("composite metadata: insufficient data for mime type")
77+
}
6978
mimeType = string(raw[1 : 1+mimeTypeLen])
7079
}
71-
metadataLen := u24.NewUint24Bytes(raw[size : size+3]).AsInt()
72-
length = size + 3 + metadataLen
73-
metadata = raw[size+3 : length]
80+
if size+metadataLengthFieldSize > len(raw) {
81+
return 0, "", nil, errors.New("composite metadata: insufficient data for metadata length")
82+
}
83+
metadataLen := u24.NewUint24Bytes(raw[size : size+metadataLengthFieldSize]).AsInt()
84+
length = size + metadataLengthFieldSize + metadataLen
85+
if length < 0 {
86+
return 0, "", nil, errors.New("composite metadata: invalid negative length (integer overflow)")
87+
}
88+
if length > len(raw) {
89+
return 0, "", nil, errors.New("composite metadata: metadata length exceeds available data")
90+
}
91+
metadata = raw[size+metadataLengthFieldSize : length]
7492
return
7593
}
7694

0 commit comments

Comments
 (0)