@@ -6,6 +6,8 @@ Licensed under the GPL, like the original.
6
6
*/
7
7
package quicklz
8
8
9
+ import "errors"
10
+
9
11
const (
10
12
// Streaming mode not supported
11
13
QLZ_STREAMING_BUFFER = 0
@@ -36,27 +38,30 @@ func headerLen(source []byte) int {
36
38
return 3
37
39
}
38
40
39
- func sizeDecompressed (source []byte ) int {
41
+ func sizeDecompressed (source []byte ) ( int , error ) {
40
42
if headerLen (source ) == 9 {
41
43
return fastRead (source , 5 , 4 )
42
44
}
43
45
return fastRead (source , 2 , 1 )
44
46
45
47
}
46
48
47
- func sizeCompressed (source []byte ) int {
49
+ func sizeCompressed (source []byte ) ( int , error ) {
48
50
if headerLen (source ) == 9 {
49
51
return fastRead (source , 1 , 4 )
50
52
}
51
53
return fastRead (source , 1 , 1 )
52
54
}
53
55
54
- func fastRead (a []byte , i , numbytes int ) int {
56
+ func fastRead (a []byte , i , numbytes int ) ( int , error ) {
55
57
l := 0
58
+ if len (a ) < i + numbytes {
59
+ return 0 , ErrCorrupt
60
+ }
56
61
for j := 0 ; j < numbytes ; j ++ {
57
62
l |= int (a [i + j ]) << (uint (j ) * 8 )
58
63
}
59
- return l
64
+ return l , nil
60
65
}
61
66
62
67
func fastWrite (a []byte , i , value , numbytes int ) {
@@ -112,7 +117,7 @@ func Compress(source []byte, level int) []byte {
112
117
}
113
118
114
119
if src <= lastMatchStart {
115
- fetch = fastRead (source , src , 3 )
120
+ fetch , _ = fastRead (source , src , 3 )
116
121
}
117
122
118
123
for src <= lastMatchStart {
@@ -180,7 +185,7 @@ func Compress(source []byte, level int) []byte {
180
185
}
181
186
}
182
187
lits = 0
183
- fetch = fastRead (source , src , 3 )
188
+ fetch , _ = fastRead (source , src , 3 )
184
189
} else {
185
190
lits ++
186
191
hashCounter [hash ] = 1
@@ -191,7 +196,7 @@ func Compress(source []byte, level int) []byte {
191
196
fetch = (fetch >> 8 )& 0xffff | int (source [src + 2 ])<< 16
192
197
}
193
198
} else {
194
- fetch = fastRead (source , src , 3 )
199
+ fetch , _ = fastRead (source , src , 3 )
195
200
196
201
var o , offset2 int
197
202
var matchlen , k , m int
@@ -230,7 +235,7 @@ func Compress(source []byte, level int) []byte {
230
235
if matchlen >= 3 && src - o < 131071 {
231
236
offset := src - o
232
237
for u := 1 ; u < matchlen ; u ++ {
233
- fetch = fastRead (source , src + u , 3 )
238
+ fetch , _ = fastRead (source , src + u , 3 )
234
239
hash = ((fetch >> 12 ) ^ fetch ) & (HASH_VALUES - 1 )
235
240
c = hashCounter [hash ]
236
241
hashCounter [hash ]++
@@ -289,8 +294,16 @@ func Compress(source []byte, level int) []byte {
289
294
return d2
290
295
}
291
296
292
- func Decompress (source []byte ) []byte {
293
- size := sizeDecompressed (source )
297
+ var (
298
+ ErrCorrupt = errors .New ("quicklz: corrupt document" )
299
+ ErrInvalidVersion = errors .New ("quicklz: unsupported compression version" )
300
+ )
301
+
302
+ func Decompress (source []byte ) ([]byte , error ) {
303
+ size , err := sizeDecompressed (source )
304
+ if err != nil || size < 0 {
305
+ return nil , ErrCorrupt
306
+ }
294
307
src := headerLen (source )
295
308
var dst int
296
309
var cwordVal = 1
@@ -305,24 +318,35 @@ func Decompress(source []byte) []byte {
305
318
level := (source [0 ] >> 2 ) & 0x3
306
319
307
320
if level != 1 && level != 3 {
308
- panic ( "Go version only supports level 1 and 3" )
321
+ return nil , ErrInvalidVersion
309
322
}
310
323
311
324
if (source [0 ] & 1 ) != 1 {
312
325
d2 := make ([]byte , size )
313
- copy (d2 , source [headerLen (source ):])
314
- return d2
326
+ l := headerLen (source )
327
+ if len (source ) < l {
328
+ return nil , ErrCorrupt
329
+ }
330
+ copy (d2 , source [l :])
331
+ return d2 , nil
315
332
}
316
333
317
334
for {
318
335
if cwordVal == 1 {
319
- cwordVal = fastRead (source , src , 4 )
336
+ var err error
337
+ cwordVal , err = fastRead (source , src , 4 )
338
+ if err != nil {
339
+ return nil , ErrCorrupt
340
+ }
320
341
src += 4
321
342
if dst <= lastMatchStart {
322
343
if level == 1 {
323
- fetch = fastRead (source , src , 3 )
344
+ fetch , err = fastRead (source , src , 3 )
324
345
} else {
325
- fetch = fastRead (source , src , 4 )
346
+ fetch , err = fastRead (source , src , 4 )
347
+ }
348
+ if err != nil {
349
+ return nil , ErrCorrupt
326
350
}
327
351
}
328
352
}
@@ -341,6 +365,9 @@ func Decompress(source []byte) []byte {
341
365
matchlen = (fetch & 0xf ) + 2
342
366
src += 2
343
367
} else {
368
+ if len (source ) <= src + 2 {
369
+ return nil , ErrCorrupt
370
+ }
344
371
matchlen = int (source [src + 2 ]) & 0xff
345
372
src += 3
346
373
}
@@ -371,6 +398,10 @@ func Decompress(source []byte) []byte {
371
398
offset2 = int (dst - offset )
372
399
}
373
400
401
+ if matchlen < 0 || offset2 < 0 || len (destination ) <= dst + 2 || len (destination ) <= offset2 + matchlen || len (destination ) <= dst + matchlen {
402
+ return nil , ErrCorrupt
403
+ }
404
+
374
405
destination [dst + 0 ] = destination [offset2 + 0 ]
375
406
destination [dst + 1 ] = destination [offset2 + 1 ]
376
407
destination [dst + 2 ] = destination [offset2 + 2 ]
@@ -381,17 +412,29 @@ func Decompress(source []byte) []byte {
381
412
dst += matchlen
382
413
383
414
if level == 1 {
384
- fetch = fastRead (destination , lastHashed + 1 , 3 ) // destination[lastHashed + 1] | (destination[lastHashed + 2] << 8) | (destination[lastHashed + 3] << 16);
415
+ fetch , err = fastRead (destination , lastHashed + 1 , 3 ) // destination[lastHashed + 1] | (destination[lastHashed + 2] << 8) | (destination[lastHashed + 3] << 16);
416
+ if err != nil {
417
+ return nil , ErrCorrupt
418
+ }
385
419
for lastHashed < dst - matchlen {
386
420
lastHashed ++
387
421
hash = ((fetch >> 12 ) ^ fetch ) & (HASH_VALUES - 1 )
388
422
hashtable [hash ] = lastHashed
389
423
hashCounter [hash ] = 1
424
+ if len (destination ) <= lastHashed + 3 {
425
+ return nil , ErrCorrupt
426
+ }
390
427
fetch = (fetch >> 8 & 0xffff ) | (int (destination [lastHashed + 3 ]) << 16 )
391
428
}
392
- fetch = fastRead (source , src , 3 )
429
+ fetch , err = fastRead (source , src , 3 )
430
+ if err != nil {
431
+ return nil , ErrCorrupt
432
+ }
393
433
} else {
394
- fetch = fastRead (source , src , 4 )
434
+ fetch , err = fastRead (source , src , 4 )
435
+ if err != nil {
436
+ return nil , ErrCorrupt
437
+ }
395
438
}
396
439
lastHashed = dst - 1
397
440
} else {
@@ -404,13 +447,22 @@ func Decompress(source []byte) []byte {
404
447
if level == 1 {
405
448
for lastHashed < dst - 3 {
406
449
lastHashed ++
407
- fetch2 := fastRead (destination , lastHashed , 3 )
450
+ fetch2 , err := fastRead (destination , lastHashed , 3 )
451
+ if err != nil {
452
+ return nil , ErrCorrupt
453
+ }
408
454
hash = ((fetch2 >> 12 ) ^ fetch2 ) & (HASH_VALUES - 1 )
409
455
hashtable [hash ] = lastHashed
410
456
hashCounter [hash ] = 1
411
457
}
458
+ if len (source ) <= src + 2 {
459
+ return nil , ErrCorrupt
460
+ }
412
461
fetch = fetch >> 8 & 0xffff | int (source [src + 2 ])<< 16
413
462
} else {
463
+ if len (source ) <= src + 3 {
464
+ return nil , ErrCorrupt
465
+ }
414
466
fetch = fetch >> 8 & 0xffff | int (source [src + 2 ])<< 16 | int (source [src + 3 ])<< 24
415
467
}
416
468
} else {
@@ -420,12 +472,15 @@ func Decompress(source []byte) []byte {
420
472
cwordVal = 0x80000000
421
473
}
422
474
475
+ if len (destination ) <= dst || len (source ) <= src {
476
+ return nil , ErrCorrupt
477
+ }
423
478
destination [dst ] = source [src ]
424
479
dst ++
425
480
src ++
426
481
cwordVal = cwordVal >> 1
427
482
}
428
- return destination
483
+ return destination , nil
429
484
}
430
485
}
431
486
}
0 commit comments