moq: fix several panics and OOM errors (#5964)
Check for limits before allocating memory by using sizes passed from the remote peer. Also add fuzzing to all MoQ primitives.
This commit is contained in:
@@ -172,3 +172,18 @@ func TestMarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshal(f *testing.F) {
|
||||
for _, ca := range cases {
|
||||
f.Add(ca.enc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(_ *testing.T, buf []byte) {
|
||||
m, err := Read(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.Marshal()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ import (
|
||||
|
||||
const typePublish varint.Varint = 0x1d
|
||||
|
||||
const (
|
||||
maxNamespaceFieldCount = 16
|
||||
)
|
||||
|
||||
// Publish is the PUBLISH control message.
|
||||
// spec: draft-18, section 10.10
|
||||
type Publish struct {
|
||||
@@ -29,9 +33,10 @@ func (m *Publish) unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.RequestID = uint64(requestID)
|
||||
buf = buf[n:]
|
||||
|
||||
m.RequestID = uint64(requestID)
|
||||
|
||||
var nsCount varint.Varint
|
||||
n, err = nsCount.Unmarshal(buf)
|
||||
if err != nil {
|
||||
@@ -39,6 +44,10 @@ func (m *Publish) unmarshal(buf []byte) error {
|
||||
}
|
||||
buf = buf[n:]
|
||||
|
||||
if nsCount > maxNamespaceFieldCount {
|
||||
return fmt.Errorf("too many namespace fields: %d", nsCount)
|
||||
}
|
||||
|
||||
m.Namespace = make([]string, nsCount)
|
||||
for i := range m.Namespace {
|
||||
var l varint.Varint
|
||||
@@ -47,9 +56,11 @@ func (m *Publish) unmarshal(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
if len(buf) < int(l) {
|
||||
|
||||
if uint64(len(buf)) < uint64(l) {
|
||||
return fmt.Errorf("not enough bytes for namespace part")
|
||||
}
|
||||
|
||||
m.Namespace[i] = string(buf[:l])
|
||||
buf = buf[int(l):]
|
||||
}
|
||||
@@ -60,9 +71,11 @@ func (m *Publish) unmarshal(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
if len(buf) < int(tnLen) {
|
||||
return fmt.Errorf("not enough bytes for track name")
|
||||
|
||||
if uint64(len(buf)) < uint64(tnLen) {
|
||||
return fmt.Errorf("invalid track name length: %d", tnLen)
|
||||
}
|
||||
|
||||
m.TrackName = string(buf[:tnLen])
|
||||
buf = buf[int(tnLen):]
|
||||
|
||||
@@ -71,9 +84,10 @@ func (m *Publish) unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.TrackAlias = uint64(trackAlias)
|
||||
buf = buf[n:]
|
||||
|
||||
m.TrackAlias = uint64(trackAlias)
|
||||
|
||||
var paramCount varint.Varint
|
||||
n, err = paramCount.Unmarshal(buf)
|
||||
if err != nil {
|
||||
|
||||
@@ -34,9 +34,10 @@ func (m *RequestError) unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Code = RequestErrorCode(code)
|
||||
buf = buf[n:]
|
||||
|
||||
m.Code = RequestErrorCode(code)
|
||||
|
||||
var retry varint.Varint
|
||||
n, err = retry.Unmarshal(buf)
|
||||
if err != nil {
|
||||
@@ -50,9 +51,11 @@ func (m *RequestError) unmarshal(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
if len(buf) < int(l) {
|
||||
|
||||
if uint64(len(buf)) < uint64(l) {
|
||||
return fmt.Errorf("not enough bytes")
|
||||
}
|
||||
|
||||
m.Reason = string(buf[:l])
|
||||
|
||||
return nil
|
||||
|
||||
@@ -26,9 +26,10 @@ func (m *Subscribe) unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.RequestID = uint64(requestID)
|
||||
buf = buf[n:]
|
||||
|
||||
m.RequestID = uint64(requestID)
|
||||
|
||||
var nsCount varint.Varint
|
||||
n, err = nsCount.Unmarshal(buf)
|
||||
if err != nil {
|
||||
@@ -36,6 +37,10 @@ func (m *Subscribe) unmarshal(buf []byte) error {
|
||||
}
|
||||
buf = buf[n:]
|
||||
|
||||
if nsCount > maxNamespaceFieldCount {
|
||||
return fmt.Errorf("too many namespace fields: %d", nsCount)
|
||||
}
|
||||
|
||||
m.Namespace = make([]string, nsCount)
|
||||
for i := range m.Namespace {
|
||||
var l varint.Varint
|
||||
@@ -44,9 +49,11 @@ func (m *Subscribe) unmarshal(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
if len(buf) < int(l) {
|
||||
|
||||
if uint64(len(buf)) < uint64(l) {
|
||||
return fmt.Errorf("not enough bytes for namespace part")
|
||||
}
|
||||
|
||||
m.Namespace[i] = string(buf[:l])
|
||||
buf = buf[int(l):]
|
||||
}
|
||||
@@ -57,9 +64,11 @@ func (m *Subscribe) unmarshal(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
if len(buf) < int(tnLen) {
|
||||
return fmt.Errorf("not enough bytes for track name")
|
||||
|
||||
if uint64(len(buf)) < uint64(tnLen) {
|
||||
return fmt.Errorf("invalid track name length: %d", tnLen)
|
||||
}
|
||||
|
||||
m.TrackName = string(buf[:tnLen])
|
||||
buf = buf[int(tnLen):]
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@ func (m *SubscribeOk) unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.TrackAlias = uint64(v)
|
||||
buf = buf[n:]
|
||||
|
||||
m.TrackAlias = uint64(v)
|
||||
|
||||
var numParams varint.Varint
|
||||
n, err = numParams.Unmarshal(buf)
|
||||
if err != nil {
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x04\x00\x060\x000010")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x1d\x00\f0\x01\xff\xff00000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\a\x00\x010")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x04\x00\x06000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\a\x000\x0000000000000000000000000000000000000000000000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x1d\x00\x160\x01\x03000\x030000\x001\xff\xff0000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\v0\x01\xff\xff0000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0\x00\x00")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\x150\x01\x03000\x030000\x03\xff\xff0000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x1d\x00\x160\x00\x01000\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x05\x00\x15\x9700\xff\xff0000000000000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\x15000000000000000000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x1d\x00\f0\x01\x00\xff\xff0000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x1d\x00\x03000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x1d\x00\x160\x01\x03000\x0300000\x03\b\x03\xf2000000")
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\x15\x970\x00\xff\xff0000000000000000")
|
||||
@@ -33,35 +33,41 @@ func (*AuthorizationToken) paramType() uint64 {
|
||||
|
||||
func (t *AuthorizationToken) unmarshal(buf []byte) (int, error) {
|
||||
var le varint.Varint
|
||||
llen, err := le.Unmarshal(buf)
|
||||
n1, err := le.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(buf)-llen < int(le) {
|
||||
buf = buf[n1:]
|
||||
|
||||
if uint64(len(buf)) < uint64(le) {
|
||||
return 0, fmt.Errorf("not enough bytes for parameter value")
|
||||
}
|
||||
buf = buf[llen : llen+int(le)]
|
||||
|
||||
buf = buf[:le]
|
||||
|
||||
var aliasType varint.Varint
|
||||
n, err := aliasType.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf = buf[n:]
|
||||
|
||||
t.AliasType = AuthorizationTokenAliasType(aliasType)
|
||||
|
||||
if t.AliasType != AuthorizationTokenAliasTypeUseValue {
|
||||
return 0, fmt.Errorf("unsupported token alias type: %d", aliasType)
|
||||
}
|
||||
buf = buf[n:]
|
||||
|
||||
var tokenType varint.Varint
|
||||
n, err = tokenType.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
t.TokenType = uint64(tokenType)
|
||||
t.TokenValue = buf[n:]
|
||||
return llen + int(le), nil
|
||||
|
||||
return n1 + int(le), nil
|
||||
}
|
||||
|
||||
func (t AuthorizationToken) marshalSize() int {
|
||||
|
||||
@@ -31,8 +31,8 @@ func (p *Parameters) Unmarshal(count int, buf []byte) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
|
||||
total += n
|
||||
currentType += uint64(typeDelta)
|
||||
|
||||
@@ -50,9 +50,9 @@ func (p *Parameters) Unmarshal(count int, buf []byte) (int, error) {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to unmarshal authorization token: %w", err)
|
||||
}
|
||||
buf = buf[n:]
|
||||
|
||||
*p = append(*p, param)
|
||||
buf = buf[n:]
|
||||
total += n
|
||||
}
|
||||
|
||||
|
||||
@@ -58,3 +58,20 @@ func TestMarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshal(f *testing.F) {
|
||||
for _, ca := range cases {
|
||||
f.Add(ca.count, ca.enc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(_ *testing.T, count int, buf []byte) {
|
||||
var params Parameters
|
||||
_, err := params.Unmarshal(count, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]byte, params.MarshalSize())
|
||||
params.MarshalTo(buf)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
go test fuzz v1
|
||||
int(23)
|
||||
[]byte("\x03\x03000")
|
||||
@@ -0,0 +1,3 @@
|
||||
go test fuzz v1
|
||||
int(68)
|
||||
[]byte("\x030")
|
||||
@@ -0,0 +1,3 @@
|
||||
go test fuzz v1
|
||||
int(-17)
|
||||
[]byte("00")
|
||||
@@ -31,8 +31,8 @@ func (p *Properties) Unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
|
||||
currentType += uint64(delta)
|
||||
|
||||
switch currentType {
|
||||
@@ -54,9 +54,11 @@ func (p *Properties) Unmarshal(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(buf)-n2 < int(length) {
|
||||
|
||||
if uint64(len(buf))-uint64(n2) < uint64(length) {
|
||||
return fmt.Errorf("not enough bytes for unknown property")
|
||||
}
|
||||
|
||||
buf = buf[n2+int(length):]
|
||||
} else {
|
||||
var skip varint.Varint
|
||||
|
||||
@@ -48,3 +48,20 @@ func TestMarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshal(f *testing.F) {
|
||||
for _, ca := range cases {
|
||||
f.Add(ca.enc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(_ *testing.T, buf []byte) {
|
||||
var props Properties
|
||||
err := props.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]byte, props.MarshalSize())
|
||||
props.MarshalTo(buf)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("10")
|
||||
@@ -8,7 +8,10 @@ import (
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/moq/varint"
|
||||
)
|
||||
|
||||
const maxPayloadSize = 10 * 1024 * 1024
|
||||
const (
|
||||
maxPropsLen = 128 * 1024
|
||||
maxPayloadSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Object is an object of a subgroup stream.
|
||||
// spec: draft-18, section 11.4.2
|
||||
@@ -34,6 +37,10 @@ func (o *Object) read(r io.Reader, header *Header) error {
|
||||
}
|
||||
|
||||
if propsLen > 0 {
|
||||
if propsLen > maxPropsLen {
|
||||
return fmt.Errorf("properties too large: %d", propsLen)
|
||||
}
|
||||
|
||||
props := make([]byte, propsLen)
|
||||
_, err = io.ReadFull(r, props)
|
||||
if err != nil {
|
||||
|
||||
@@ -90,3 +90,19 @@ func TestMarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshal(f *testing.F) {
|
||||
for _, ca := range cases {
|
||||
f.Add(ca.enc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(_ *testing.T, buf []byte) {
|
||||
var s SubGroup
|
||||
err := s.Read(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.Marshal()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0000\xff00000000")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0000\x0100\x0500000")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("1000\xd000")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0000\x000")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\xff0")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\xff")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\xf8\x000000")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\xfe\x00\x00\x00\x00\x0200")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\xff\x000000000")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\xc0")
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("")
|
||||
@@ -89,3 +89,35 @@ func TestMarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshal(f *testing.F) {
|
||||
for _, ca := range cases {
|
||||
f.Add(ca.enc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(_ *testing.T, buf []byte) {
|
||||
var v Varint
|
||||
_, err := v.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.Marshal()
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzRead(f *testing.F) {
|
||||
for _, ca := range cases {
|
||||
f.Add(ca.enc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(_ *testing.T, buf []byte) {
|
||||
var v Varint
|
||||
err := v.Read(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.Marshal()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user