Skip to content

Commit 8c13bc5

Browse files
authored
Merge pull request #1993 from matthewdale/godriver3451-more-fuzz
GODRIVER-3451 Add fuzz test for BSON MarshalValue/UnmarshalValue.
2 parents 1663fbf + d3fe145 commit 8c13bc5

File tree

3 files changed

+85
-5
lines changed

3 files changed

+85
-5
lines changed

bson/decode_value_fuzz_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (C) MongoDB, Inc. 2025-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package bson
8+
9+
import (
10+
"math"
11+
"strings"
12+
"testing"
13+
)
14+
15+
func FuzzDecodeValue(f *testing.F) {
16+
// Seed the fuzz corpus with all BSON values from the MarshalValue test
17+
// cases.
18+
for _, tc := range marshalValueTestCases {
19+
f.Add(byte(tc.bsontype), tc.bytes)
20+
}
21+
22+
// Also seed the fuzz corpus with special values that we want to test.
23+
values := []any{
24+
// int32, including max and min values.
25+
int32(0), int32(math.MaxInt32), int32(math.MinInt32),
26+
// int64, including max and min values.
27+
int64(0), int64(math.MaxInt64), int64(math.MinInt64),
28+
// string, including empty and large string.
29+
"", strings.Repeat("z", 10_000),
30+
// map
31+
map[string]any{"nested": []any{1, "two", map[string]any{"three": 3}}},
32+
// array
33+
[]any{1, 2, 3, "four"},
34+
}
35+
36+
for _, v := range values {
37+
typ, b, err := MarshalValue(v)
38+
if err != nil {
39+
f.Fatal(err)
40+
}
41+
f.Add(byte(typ), b)
42+
}
43+
44+
f.Fuzz(func(t *testing.T, bsonType byte, data []byte) {
45+
var v any
46+
if err := UnmarshalValue(Type(bsonType), data, &v); err != nil {
47+
return
48+
}
49+
50+
// There is no value encoder for Go "nil" (nil values handled
51+
// differently by each type encoder), so skip anything that unmarshals
52+
// to "nil". It's not clear if MarshalValue should support "nil", but
53+
// for now we skip it.
54+
if v == nil {
55+
t.Logf("data unmarshaled to nil: %v", data)
56+
return
57+
}
58+
59+
typ, encoded, err := MarshalValue(v)
60+
if err != nil {
61+
t.Fatalf("failed to marshal: %v", err)
62+
}
63+
64+
var v2 any
65+
if err := UnmarshalValue(typ, encoded, &v2); err != nil {
66+
t.Fatalf("failed to unmarshal: %v", err)
67+
}
68+
})
69+
}

bson/fuzz_test.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
// not use this file except in compliance with the License. You may obtain
55
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
66

7+
// fuzz_test.go is used by the "oss-fuzz" integration. Use caution when
8+
// modifying this file because it may break that integration.
9+
//
10+
// See https://github.com/google/oss-fuzz/tree/master/projects/mongo-go-driver
11+
712
package bson
813

914
import (
@@ -29,11 +34,11 @@ func FuzzDecode(f *testing.F) {
2934

3035
encoded, err := Marshal(i)
3136
if err != nil {
32-
t.Fatal("failed to marshal", err)
37+
t.Fatalf("failed to marshal: %v", err)
3338
}
3439

3540
if err := Unmarshal(encoded, i); err != nil {
36-
t.Fatal("failed to unmarshal", err)
41+
t.Fatalf("failed to unmarshal: %v", err)
3742
}
3843
}
3944
})

etc/run-fuzz.sh

+9-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
set -o errexit # Exit the script with error if any of the commands fail
44

5-
FUZZTIME=10m
5+
# Default fuzztime to 10m.
6+
FUZZTIME=${FUZZTIME:-10m}
7+
8+
if [ -z "$PROJECT_DIRECTORY" ]; then
9+
echo "Please set PROJECT_DIRECTORY env variable."
10+
exit 1
11+
fi
612

713
# Change the working directory to the root of the mongo repository directory
814
cd $PROJECT_DIRECTORY
@@ -21,7 +27,7 @@ do
2127
# For each fuzz test in the file, run it for FUZZTIME.
2228
for FUNC in ${FUNCS}
2329
do
24-
echo "Fuzzing \"${FUNC}\" in \"${FILE}\""
30+
echo "Fuzzing \"${FUNC}\" in \"${FILE}\" for ${FUZZTIME}"
2531

2632
# Create a set of directories that are already in the subdirectories testdata/fuzz/$fuzzer corpus. This
2733
# set will be used to differentiate between new and old corpus files.
@@ -35,7 +41,7 @@ do
3541
done
3642
fi
3743

38-
GOMAXPROCS=2 go test ${PARENTDIR} -run=${FUNC} -fuzz=${FUNC} -fuzztime=${FUZZTIME} || true
44+
GOMAXPROCS=2 go test -race ${PARENTDIR} -run=${FUNC} -fuzz=^${FUNC}\$ -fuzztime=${FUZZTIME} || true
3945

4046
# Check if any new corpus files were generated for the fuzzer. If there are new corpus files, move them
4147
# to $PROJECT_DIRECTORY/fuzz/$FUNC/* so they can be tarred up and uploaded to S3.

0 commit comments

Comments
 (0)