• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Eyevinn / mp4ff / 12847668439

18 Jan 2025 09:43PM UTC coverage: 80.668% (+0.4%) from 80.313%
12847668439

push

github

tobbee
fix: add fuzzer for mp4 box decoding and fix discovered issues

This greatly reduced the memory usage for corrupted mp4 files by checking for
sizes of the box compared to the sizes of elements..

229 of 273 new or added lines in 27 files covered. (83.88%)

2 existing lines in 2 files now uncovered.

15907 of 19719 relevant lines covered (80.67%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.55
/bits/fixedslicereader.go
1
package bits
2

3
import (
4
        "encoding/binary"
5
        "errors"
6
        "fmt"
7
)
8

9
// FixedSliceReader - read integers and other data from a fixed slice.
10
// Accumulates error, and the first error can be retrieved.
11
// If err != nil, 0 or empty string is returned
12
type FixedSliceReader struct {
13
        err   error
14
        slice []byte
15
        pos   int
16
        len   int
17
}
18

19
// bits.NewFixedSliceReader creates a new slice reader reading from data
20
func NewFixedSliceReader(data []byte) *FixedSliceReader {
1✔
21
        return &FixedSliceReader{
1✔
22
                slice: data,
1✔
23
                pos:   0,
1✔
24
                len:   len(data),
1✔
25
                err:   nil,
1✔
26
        }
1✔
27
}
1✔
28

29
// AccError - get accumulated error after read operations
30
func (s *FixedSliceReader) AccError() error {
1✔
31
        return s.err
1✔
32
}
1✔
33

34
// ReadUint8 - read uint8 from slice
35
func (s *FixedSliceReader) ReadUint8() byte {
1✔
36
        if s.err != nil {
2✔
37
                return 0
1✔
38
        }
1✔
39
        if s.pos > s.len-1 {
2✔
40
                s.err = ErrSliceRead
1✔
41
                return 0
1✔
42
        }
1✔
43
        res := s.slice[s.pos]
1✔
44
        s.pos++
1✔
45
        return res
1✔
46
}
47

48
// ReadUint16 - read uint16 from slice
49
func (s *FixedSliceReader) ReadUint16() uint16 {
1✔
50
        if s.err != nil {
2✔
51
                return 0
1✔
52
        }
1✔
53
        if s.pos > s.len-2 {
2✔
54
                s.err = ErrSliceRead
1✔
55
                return 0
1✔
56
        }
1✔
57
        res := binary.BigEndian.Uint16(s.slice[s.pos : s.pos+2])
1✔
58
        s.pos += 2
1✔
59
        return res
1✔
60
}
61

62
// ReadInt16 - read int16 from slice
63
func (s *FixedSliceReader) ReadInt16() int16 {
1✔
64
        if s.err != nil {
2✔
65
                return 0
1✔
66
        }
1✔
67
        if s.pos > s.len-2 {
2✔
68
                s.err = ErrSliceRead
1✔
69
                return 0
1✔
70
        }
1✔
71
        res := binary.BigEndian.Uint16(s.slice[s.pos : s.pos+2])
1✔
72
        s.pos += 2
1✔
73
        return int16(res)
1✔
74
}
75

76
// ReadUint24 - read uint24 from slice
77
func (s *FixedSliceReader) ReadUint24() uint32 {
1✔
78
        if s.err != nil {
2✔
79
                return 0
1✔
80
        }
1✔
81
        if s.pos > s.len-3 {
2✔
82
                s.err = ErrSliceRead
1✔
83
                return 0
1✔
84
        }
1✔
85
        res := uint32(binary.BigEndian.Uint16(s.slice[s.pos : s.pos+2]))
1✔
86
        res = res<<8 | uint32(s.slice[s.pos+2])
1✔
87
        s.pos += 3
1✔
88
        return res
1✔
89
}
90

91
// ReadUint32 - read uint32 from slice
92
func (s *FixedSliceReader) ReadUint32() uint32 {
1✔
93
        if s.err != nil {
2✔
94
                return 0
1✔
95
        }
1✔
96
        if s.pos > s.len-4 {
2✔
97
                s.err = ErrSliceRead
1✔
98
                return 0
1✔
99
        }
1✔
100
        res := binary.BigEndian.Uint32(s.slice[s.pos : s.pos+4])
1✔
101
        s.pos += 4
1✔
102
        return res
1✔
103
}
104

105
// ReadInt32 - read int32 from slice
106
func (s *FixedSliceReader) ReadInt32() int32 {
1✔
107
        if s.err != nil {
2✔
108
                return 0
1✔
109
        }
1✔
110
        if s.pos > s.len-4 {
2✔
111
                s.err = ErrSliceRead
1✔
112
                return 0
1✔
113
        }
1✔
114
        res := binary.BigEndian.Uint32(s.slice[s.pos : s.pos+4])
1✔
115
        s.pos += 4
1✔
116
        return int32(res)
1✔
117
}
118

119
// ReadUint64 - read uint64 from slice
120
func (s *FixedSliceReader) ReadUint64() uint64 {
1✔
121
        if s.err != nil {
2✔
122
                return 0
1✔
123
        }
1✔
124
        if s.pos > s.len-8 {
2✔
125
                s.err = ErrSliceRead
1✔
126
                return 0
1✔
127
        }
1✔
128
        res := binary.BigEndian.Uint64(s.slice[s.pos : s.pos+8])
1✔
129
        s.pos += 8
1✔
130
        return res
1✔
131
}
132

133
// ReadInt64 - read int64 from slice
134
func (s *FixedSliceReader) ReadInt64() int64 {
1✔
135
        if s.err != nil {
2✔
136
                return 0
1✔
137
        }
1✔
138
        if s.pos > s.len-8 {
2✔
139
                s.err = ErrSliceRead
1✔
140
                return 0
1✔
141
        }
1✔
142
        res := binary.BigEndian.Uint64(s.slice[s.pos : s.pos+8])
1✔
143
        s.pos += 8
1✔
144
        return int64(res)
1✔
145
}
146

147
// ReadFixedLengthString - read string of specified length n.
148
// Sets err and returns empty string if full length not available
149
func (s *FixedSliceReader) ReadFixedLengthString(n int) string {
1✔
150
        if s.err != nil {
2✔
151
                return ""
1✔
152
        }
1✔
153
        if s.pos > s.len-n {
2✔
154
                s.err = ErrSliceRead
1✔
155
                return ""
1✔
156
        }
1✔
157
        res := string(s.slice[s.pos : s.pos+n])
1✔
158
        s.pos += n
1✔
159
        return res
1✔
160
}
161

162
// ReadZeroTerminatedString - read string until zero byte but at most maxLen
163
// Set err and return empty string if no zero byte found.
164
func (s *FixedSliceReader) ReadZeroTerminatedString(maxLen int) string {
1✔
165
        if s.err != nil {
2✔
166
                return ""
1✔
167
        }
1✔
168
        startPos := s.pos
1✔
169
        maxPos := startPos + maxLen
1✔
170
        if maxPos > s.len {
1✔
NEW
171
                maxPos = s.len
×
NEW
172
        }
×
173
        for {
2✔
174
                if s.pos >= maxPos {
2✔
175
                        s.err = errors.New("did not find terminating zero")
1✔
176
                        return ""
1✔
177
                }
1✔
178
                c := s.slice[s.pos]
1✔
179
                if c == 0 {
2✔
180
                        str := string(s.slice[startPos:s.pos])
1✔
181
                        s.pos++ // Next position to read
1✔
182
                        return str
1✔
183
                }
1✔
184
                s.pos++
1✔
185
        }
186
}
187

188
// ReadPossiblyZeroTerminatedString - read string until zero byte but at most maxLen.
189
// If maxLen is reached and no zero-byte, return string and ok = false
190
func (s *FixedSliceReader) ReadPossiblyZeroTerminatedString(maxLen int) (str string, ok bool) {
1✔
191
        startPos := s.pos
1✔
192
        maxPos := startPos + maxLen
1✔
193
        for {
2✔
194
                if s.pos == maxPos {
2✔
195
                        return string(s.slice[startPos:s.pos]), true
1✔
196
                }
1✔
197
                if s.pos > maxPos {
2✔
198
                        s.err = errors.New("did not find terminating zero")
1✔
199
                        return "", false
1✔
200
                }
1✔
201
                c := s.slice[s.pos]
1✔
202
                if c == 0 {
2✔
203
                        str = string(s.slice[startPos:s.pos])
1✔
204
                        s.pos++ // Next position to read
1✔
205
                        return str, true
1✔
206
                }
1✔
207
                s.pos++
1✔
208
        }
209
}
210

211
// ReadBytes - read a slice of n bytes
212
// Return empty slice if n bytes not available
213
func (s *FixedSliceReader) ReadBytes(n int) []byte {
1✔
214
        if n < 0 {
1✔
NEW
215
                s.err = fmt.Errorf("attempt to read negative number of bytes: %d", n)
×
NEW
216
                return []byte{}
×
NEW
217
        }
×
218
        if s.err != nil {
2✔
219
                return []byte{}
1✔
220
        }
1✔
221
        if s.pos > s.len-n {
2✔
222
                s.err = ErrSliceRead
1✔
223
                return []byte{}
1✔
224
        }
1✔
225
        res := s.slice[s.pos : s.pos+n]
1✔
226
        s.pos += n
1✔
227
        return res
1✔
228
}
229

230
// RemainingBytes - return remaining bytes of this slice
231
func (s *FixedSliceReader) RemainingBytes() []byte {
1✔
232
        if s.err != nil {
2✔
233
                return []byte{}
1✔
234
        }
1✔
235
        res := s.slice[s.pos:]
1✔
236
        s.pos = s.Length()
1✔
237
        return res
1✔
238
}
239

240
// NrRemainingBytes - return number of bytes remaining
241
func (s *FixedSliceReader) NrRemainingBytes() int {
1✔
242
        if s.err != nil {
2✔
243
                return 0
1✔
244
        }
1✔
245
        return s.Length() - s.GetPos()
1✔
246
}
247

248
// SkipBytes - skip passed n bytes
249
func (s *FixedSliceReader) SkipBytes(n int) {
1✔
250
        if s.err != nil {
2✔
251
                return
1✔
252
        }
1✔
253
        if s.pos+n > s.Length() {
2✔
254
                s.err = fmt.Errorf("attempt to skip bytes to pos %d beyond slice len %d", s.pos+n, s.len)
1✔
255
                return
1✔
256
        }
1✔
257
        s.pos += n
1✔
258
}
259

260
// SetPos - set read position is slice
261
func (s *FixedSliceReader) SetPos(pos int) {
1✔
262
        if pos > s.len {
2✔
263
                s.err = fmt.Errorf("attempt to set pos %d beyond slice len %d", pos, s.len)
1✔
264
                return
1✔
265
        }
1✔
266
        s.pos = pos
1✔
267
}
268

269
// GetPos - get read position is slice
270
func (s *FixedSliceReader) GetPos() int {
1✔
271
        return s.pos
1✔
272
}
1✔
273

274
// Length - get length of slice
275
func (s *FixedSliceReader) Length() int {
1✔
276
        return s.len
1✔
277
}
1✔
278

279
// LookAhead returns data ahead of current pos if within bounds.
280
func (s *FixedSliceReader) LookAhead(offset int, data []byte) error {
1✔
281
        if s.pos+offset+len(data) > s.len {
2✔
282
                return fmt.Errorf("out of bounds")
1✔
283
        }
1✔
284
        copy(data, s.slice[s.pos+offset:])
1✔
285
        return nil
1✔
286
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc