8000 build: General improvements around fuzzers · go-git/go-git@661686e · GitHub
[go: up one dir, main page]

Skip to content

Commit 661686e

Browse files
committed
build: General improvements around fuzzers
Add arguments to the seed corpus, to improve overall fuzzing reachability. Some additional tests were also added for patch delta. The revision now limits its parsing to 128kb. Signed-off-by: Paulo Gomes <pjbgf@linux.com>
1 parent 922fe9b commit 661686e

File tree

9 files changed

+124
-9
lines changed

9 files changed

+124
-9
lines changed

internal/revision/parser_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,14 @@ func (s *ParserSuite) TestParseRefWithInvalidName(c *C) {
400400
}
401401

402402
func FuzzParser(f *testing.F) {
403+
f.Add("@{2016-12-16T21:42:47Z}")
404+
f.Add("@~3")
405+
f.Add("v0.99.8^{}")
406+
f.Add("master:./README")
407+
f.Add("HEAD^{/fix nasty bug}")
408+
f.Add("HEAD^{/[A-")
409+
f.Add(":/fix nasty bug")
410+
f.Add(":/[A-")
403411

404412
f.Fuzz(func(t *testing.T, input string) {
405413
parser := NewParser(bytes.NewBufferString(input))

internal/revision/scanner.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ func tokenizeExpression(ch rune, tokenType token, check runeCategoryValidator, r
4343
return tokenType, string(data), nil
4444
}
4545

46+
// maxRevisionLength holds the maximum length that will be parsed for a
47+
// revision. Git itself don't enforce a max length, but rather leans on
48+
// the OS to enforce it via its ARG_MAX.
49+
const maxRevisionLength = 128 * 1024 // 128kb
50+
4651
var zeroRune = rune(0)
4752

4853
// scanner represents a lexical scanner.
@@ -52,7 +57,7 @@ type scanner struct {
5257

5358
// newScanner returns a new instance of scanner.
5459
func newScanner(r io.Reader) *scanner {
55-
return &scanner{r: bufio.NewReader(r)}
60+
return &scanner{r: bufio.NewReader(io.LimitReader(r, maxRevisionLength))}
5661
}
5762

5863
// Scan extracts tokens and their strings counterpart

plumbing/format/packfile/delta_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,12 @@ func (s *DeltaSuite) TestMaxCopySizeDeltaReader(c *C) {
179179
}
180180

181181
func FuzzPatchDelta(f *testing.F) {
182+
f.Add([]byte("some value"), []byte("\n\f\fsomenewvalue"))
183+
f.Add([]byte("some value"), []byte("\n\x0e\x0evalue"))
184+
f.Add([]byte("some value"), []byte("\n\x0e\x0eva"))
185+
f.Add([]byte("some value"), []byte("\n\x80\x80\x80\x80\x80\x802\x7fvalue"))
182186

183-
f.Fuzz(func(t *testing.T, input []byte) {
184-
185-
input_0 := input[:len(input)/2]
186-
input_1 := input[len(input)/2:]
187-
188-
PatchDelta(input_0, input_1)
187+
f.Fuzz(func(t *testing.T, input1, input2 []byte) {
188+
PatchDelta(input1, input2)
189189
})
190190
}

plumbing/format/packfile/patch_delta.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ var (
2626
const (
2727
payload = 0x7f // 0111 1111
2828
continuation = 0x80 // 1000 0000
29+
30+
// maxPatchPreemptionSize defines what is the max size of bytes to be
31+
// premptively made available for a patch operation.
32+
maxPatchPreemptionSize uint = 65536
33+
34+
// minDeltaSize defines the smallest size for a delta.
35+
minDeltaSize = 4
2936
)
3037

3138
type offset struct {
@@ -86,9 +93,13 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
8693
}
8794

8895
// PatchDelta returns the result of applying the modification deltas in delta to src.
89-
// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command
96+
// An error will be returned if delta is corrupted (ErrInvalidDelta) or an action command
9097
// is not copy from source or copy from delta (ErrDeltaCmd).
9198
func PatchDelta(src, delta []byte) ([]byte, error) {
99+
if len(src) == 0 || len(delta) < minDeltaSize {
100+
return nil, ErrInvalidDelta
101+
}
102+
92103
b := &bytes.Buffer{}
93104
if err := patchDelta(b, src, delta); err != nil {
94105
return nil, err
@@ -239,7 +250,9 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
239250
remainingTargetSz := targetSz
240251

241252
var cmd byte
242-
dst.Grow(int(targetSz))
253+
254+
growSz := min(targetSz, maxPatchPreemptionSize)
255+
dst.Grow(int(growSz))
243256
for {
244257
if len(delta) == 0 {
245258
return ErrInvalidDelta
@@ -403,6 +416,10 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader,
403416
// This must be called twice on the delta data buffer, first to get the
404417
// expected source buffer size, and again to get the target buffer size.
405418
func decodeLEB128(input []byte) (uint, []byte) {
419+
if len(input) == 0 {
420+
return 0, input
421+
}
422+
406423
var num, sz uint
407424
var b byte
408425
for {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package packfile
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestDecodeLEB128(t *testing.T) {
10+
t.Parallel()
11+
12+
tests := []struct {
13+
name string
14+
input []byte
15+
want uint
16+
wantRest []byte
17+
}{
18+
{
19+
name: "single byte, small number",
20+
input: []byte{0x01, 0xFF},
21+
want: 1,
22+
wantRest: []byte{0xFF},
23+
},
24+
{
25+
name: "single byte, max value without continuation",
26+
input: []byte{0x7F, 0xFF},
27+
want: 127,
28+
wantRest: []byte{0xFF},
29+
},
30+
{
31+
name: "two bytes",
32+
input: []byte{0x80, 0x01, 0xFF},
33+
want: 128,
34+
wantRest: []byte{0xFF},
35+
},
36+
{
37+
name: "two bytes, larger number",
38+
input: []byte{0xFF, 0x01, 0xFF},
39+
want: 255,
40+
wantRest: []byte{0xFF},
41+
},
42+
{
43+
name: "three bytes",
44+
input: []byte{0x80, 0x80, 0x01, 0xFF},
45+
want: 16384,
46+
wantRest: []byte{0xFF},
47+
},
48+
{
49+
name: "empty remaining bytes",
50+
input: []byte{0x01},
51+
want: 1,
52+
wantRest: []byte{},
53+
},
54+
{
55+
name: "empty input",
56+
input: []byte{},
57+
want: 0,
58+
wantRest: []byte{},
59+
},
60+
}
61+
62+
for _, tc := range tests {
63+
tc := tc
64+
t.Run(tc.name, func(t *testing.T) {
65+
t.Parallel()
66+
67+
gotNum, gotRest := decodeLEB128(tc.input)
68+
assert.Equal(t, tc.want, gotNum, "decoded number mismatch")
69+
assert.Equal(t, tc.wantRest, gotRest, "remaining bytes mismatch")
70+
})
71+
}
72+
}

plumbing/object/signature_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ signed tag`),
193193
}
194194

195195
func FuzzParseSignedBytes(f *testing.F) {
196+
f.Add([]byte(openPGPSignatureFormat[0]))
197+
f.Add([]byte(x509SignatureFormat[0]))
198+
f.Add([]byte(sshSignatureFormat[0]))
196199

197200
f.Fuzz(func(t *testing.T, input []byte) {
198201
parseSignedBytes(input)

plumbing/protocol/packp/srvresp.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ func (r *ServerResponse) decodeACKLine(line []byte) error {
120120
}
121121

122122
sp := bytes.Index(line, []byte(" "))
123+
if sp+41 > len(line) {
124+
return fmt.Errorf("malformed ACK %q", line)
125+
}
123126
h := plumbing.NewHash(string(line[sp+1 : sp+41]))
124127
r.ACKs = append(r.ACKs, h)
125128
return nil

plumbing/protocol/packp/uppackresp_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ func (s *UploadPackResponseSuite) TestEncodeMultiACK(c *C) {
131131
}
132132

133133
func FuzzDecoder(f *testing.F) {
134+
f.Add([]byte("0045ACK 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f81\n"))
135+
f.Add([]byte("003aACK5dc01c595e6c6ec9ccda4f6f69c131c0dd945f82 \n0008NAK\n0"))
134136

135137
f.Fuzz(func(t *testing.T, input []byte) {
136138
req := NewUploadPackRequest()

plumbing/transport/common_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ func (s *SuiteCommon) TestNewEndpointIPv6(c *C) {
235235
}
236236

237237
func FuzzNewEndpoint(f *testing.F) {
238+
f.Add("http://127.0.0.1:8080/foo.git")
239+
f.Add("http://[::1]:8080/foo.git")
240+
f.Add("file:///foo.git")
241+
f.Add("ssh://git@github.com/user/repository.git")
242+
f.Add("git@github.com:user/repository.git")
238243

239244
f.Fuzz(func(t *testing.T, input string) {
240245
NewEndpoint(input)

0 commit comments

Comments
 (0)
0