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