-
Notifications
You must be signed in to change notification settings - Fork 25
/
bzip2.go
145 lines (132 loc) · 3.7 KB
/
bzip2.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2016, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// +build gofuzz
package bzip2
import (
"bytes"
"errors"
"io/ioutil"
"github.com/dsnet/compress"
gbzip2 "github.com/dsnet/compress/bzip2"
cbzip2 "github.com/dsnet/compress/internal/cgo/bzip2"
)
func Fuzz(data []byte) int {
data, ok := testDecoders(data, true)
for i := 1; i <= 9; i++ {
testGoEncoder(data, i)
testCEncoder(data, i)
}
if ok {
return 1 // Favor valid inputs
}
return 0
}
// testDecoders tests that the input can be handled by both Go and C decoders.
// This test does not panic if both decoders run into an error, since it
// means that they both agree that the input is bad.
//
// If updateCRCs is set, then the Go bzip2 implementation will ignore all
// checksum errors and manually adjust the checksum values before running the
// C implementation. This hack drastically increases the probability that
// gofuzz can generate a "valid" file.
func testDecoders(data []byte, updateCRCs bool) ([]byte, bool) {
// Decompress using the Go decoder.
gr, err := gbzip2.NewReader(bytes.NewReader(data), nil)
if err != nil {
panic(err)
}
gb, gerr := ioutil.ReadAll(gr)
if err := gr.Close(); gerr == nil {
gerr = err
} else if gerr != nil && err == nil {
panic("nil on Close after non-nil error")
}
// Check or update the checksums.
if gerr == nil {
if updateCRCs {
data = gr.Checksums.Apply(data)
} else if !gr.Checksums.Verify(data) {
gerr = errors.New("bzip2: checksum error")
}
}
// Decompress using the C decoder.
cr := cbzip2.NewReader(bytes.NewReader(data))
cb, cerr := ioutil.ReadAll(cr)
if err := cr.Close(); cerr == nil {
cerr = err
} else if cerr != nil && err == nil {
panic("nil on Close after non-nil error")
}
switch {
case gerr == nil && cerr == nil:
if !bytes.Equal(gb, cb) {
panic("mismatching bytes")
}
return gb, true
case gerr != nil && cerr == nil:
// Ignore deprecated errors since there are no plans to provide
// these features in the Go implementation.
if err, ok := gerr.(compress.Error); ok && err.IsDeprecated() {
return cb, false
}
panic(gerr)
case gerr == nil && cerr != nil:
panic(cerr)
default:
// Ensure that both gb and cb have the same common prefix.
if !bytes.HasPrefix(gb, cb) && !bytes.HasPrefix(cb, gb) {
panic("mismatching leading bytes")
}
return nil, false
}
}
// testGoEncoder encodes the input data with the Go encoder and then checks that
// both the Go and C decoders can properly decompress the output.
func testGoEncoder(data []byte, level int) {
// Compress using the Go encoder.
bb := new(bytes.Buffer)
gw, err := gbzip2.NewWriter(bb, &gbzip2.WriterConfig{Level: level})
if err != nil {
panic(err)
}
defer gw.Close()
n, err := gw.Write(data)
if n != len(data) || err != nil {
panic(err)
}
if err := gw.Close(); err != nil {
panic(err)
}
// Decompress using both the Go and C decoders.
b, ok := testDecoders(bb.Bytes(), false)
if !ok {
panic("decoder error")
}
if !bytes.Equal(b, data) {
panic("mismatching bytes")
}
}
// testCEncoder encodes the input data with the C encoder and then checks that
// both the Go and C decoders can properly decompress the output.
func testCEncoder(data []byte, level int) {
// Compress using the C encoder.
bb := new(bytes.Buffer)
cw := cbzip2.NewWriter(bb, level)
defer cw.Close()
n, err := cw.Write(data)
if n != len(data) || err != nil {
panic(err)
}
if err := cw.Close(); err != nil {
panic(err)
}
// Decompress using both the Go and C decoders.
b, ok := testDecoders(bb.Bytes(), false)
if !ok {
panic("decoder error")
}
if !bytes.Equal(b, data) {
panic("mismatching bytes")
}
}