From a101db26bd4f15dd4915cd86378cb1f9dcf79e51 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Sat, 18 Dec 2021 11:13:06 -0600 Subject: [PATCH] add support for go1.18 fuzzing; fixes (#20) --- decode_test.go | 2 +- encode.go | 19 +++-- encode_test.go | 26 ++++++ errors.go | 8 +- fuzz.go | 1 + fuzz_1.18_test.go | 83 +++++++++++++++++++ go.mod | 2 +- ...8400f8a0a12f2f1952d87f0bae9e950c94a90fabea | 2 + ...a3a5f86c38549135c789b8d97dbbf084d21369685f | 2 + ...8f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca | 2 + ubjson_test.go | 38 ++------- 11 files changed, 141 insertions(+), 44 deletions(-) create mode 100644 fuzz_1.18_test.go create mode 100644 testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea create mode 100644 testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f create mode 100644 testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca diff --git a/decode_test.go b/decode_test.go index ed517eb..7a4ee88 100644 --- a/decode_test.go +++ b/decode_test.go @@ -18,7 +18,7 @@ func (tc *testCase) unmarshal(t *testing.T) { actual := reflect.New(reflect.ValueOf(tc.value).Type()) if err := Unmarshal(tc.binary, actual.Interface()); err != nil { - t.Fatal("failed to unmarshal:", err.Error()) + t.Fatalf("failed to unmarshal: %+v\n", err) } if !reflect.DeepEqual(actual.Elem().Interface(), expected) { t.Errorf("\nexpected: %T %v \nbut got: %T %v", diff --git a/encode.go b/encode.go index 8c8cd18..4c1aaf9 100644 --- a/encode.go +++ b/encode.go @@ -1,7 +1,6 @@ package ubjson import ( - "fmt" "io" "reflect" @@ -171,6 +170,12 @@ func elementMarkerFor(t reflect.Type) Marker { if t == nil { return 0 } + switch t { + case reflect.TypeOf(Char(0)): + return CharMarker + case reflect.TypeOf(HighPrecNumber("")): + return HighPrecNumMarker + } k := t.Kind() if v, ok := reflect.New(t).Interface().(Value); ok { m := v.UBJSONType() @@ -197,9 +202,9 @@ func elementMarkerFor(t reflect.Type) Marker { case reflect.Int64: return Int64Marker case reflect.Float32: - return Int64Marker + return Float32Marker case reflect.Float64: - return Int64Marker + return Float64Marker case reflect.Array, reflect.Slice: return ArrayStartMarker case reflect.Map, reflect.Struct: @@ -256,7 +261,7 @@ func (a *ArrayEncoder) End() error { return err } } else if a.len != a.count { - return fmt.Errorf("unable to end array of length %d after %d elements", a.len, a.count) + return errors.Errorf("unable to end array of length %d after %d elements", a.len, a.count) } return a.Flush() @@ -334,7 +339,7 @@ func (o *ObjectEncoder) End() error { return err } } else if 2*o.len != o.count { - return fmt.Errorf("unable to end map of %d entries after %d", o.len, o.count/2) + return errors.Errorf("unable to end map of %d entries after %d", o.len, o.count/2) } return o.Flush() @@ -459,7 +464,7 @@ func (e *Encoder) Encode(v interface{}) error { case reflect.Map: if k := value.Type().Key().Kind(); k != reflect.String { - return fmt.Errorf("unable to encode map of type %s: key reflect.Kind must be reflect.String but is %s", value.Type(), k) + return errors.Errorf("unable to encode map of type %s: key reflect.Kind must be reflect.String but is %s", value.Type(), k) } return e.encode(ObjectStartMarker, encodeMap(value)) @@ -473,7 +478,7 @@ func (e *Encoder) Encode(v interface{}) error { return e.Encode(value.Elem().Interface()) } - return fmt.Errorf("unable to encode value: %v", v) + return errors.Errorf("unable to encode value: %v", v) } func encodeArray(arrayValue reflect.Value) func(*Encoder) error { diff --git a/encode_test.go b/encode_test.go index fcaebde..8d05b6a 100644 --- a/encode_test.go +++ b/encode_test.go @@ -2,6 +2,7 @@ package ubjson import ( "fmt" + "reflect" "testing" "unicode/utf8" ) @@ -156,3 +157,28 @@ func firstStringDiff(s1, s2 string) stringDiff { diff.rune++ } } + +func Test_elementMarkerFor(t *testing.T) { + for _, tt := range []struct { + v interface{} + m Marker + }{ + {uint8(1), UInt8Marker}, + {int8(2), Int8Marker}, + {int16(3), Int16Marker}, + {int32(4), Int32Marker}, + {int64(5), Int64Marker}, + {float32(1.1), Float32Marker}, + {float64(2.2), Float64Marker}, + {HighPrecNumber(""), HighPrecNumMarker}, + {Char('a'), CharMarker}, + {"", StringMarker}, + } { + tt := tt + t.Run(tt.m.String(), func(t *testing.T) { + if got := elementMarkerFor(reflect.TypeOf(tt.v)); got != tt.m { + t.Errorf("expected %s for %v but got %s", tt.m, tt.v, got) + } + }) + } +} diff --git a/errors.go b/errors.go index 681ced1..381ac31 100644 --- a/errors.go +++ b/errors.go @@ -1,15 +1,15 @@ package ubjson -import "fmt" +import "github.com/pkg/errors" func errTooMany(len int) error { - return fmt.Errorf("too many calls for container with len %d", len) + return errors.Errorf("too many calls for container with len %d", len) } func errWrongTypeWrite(exp, got Marker) error { - return fmt.Errorf("unable to write type '%s' to container type '%s'", exp, got) + return errors.Errorf("unable to write element type '%s' to container type '%s'", got, exp) } func errWrongTypeRead(exp, got Marker) error { - return fmt.Errorf("tried to read type '%s' but found type '%s'", exp, got) + return errors.Errorf("tried to read type '%s' but found type '%s'", exp, got) } diff --git a/fuzz.go b/fuzz.go index 63b3343..a152819 100644 --- a/fuzz.go +++ b/fuzz.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz package ubjson diff --git a/fuzz_1.18_test.go b/fuzz_1.18_test.go new file mode 100644 index 0000000..62a03c3 --- /dev/null +++ b/fuzz_1.18_test.go @@ -0,0 +1,83 @@ +//go:build go1.18 +// +build go1.18 + +package ubjson + +import ( + "embed" + "os" + "testing" +) + +//go:embed testdata/**/* +var testdata embed.FS + +func FuzzBinary(f *testing.F) { + for _, c := range cases { + f.Add(c.binary) + } + const corpus = "testdata/bin/corpus" + if dir, err := testdata.ReadDir(corpus); err != nil { + f.Fatal("failed to read corpus dir:", err) + } else { + for _, c := range dir { + if c.IsDir() { + continue + } + if b, err := os.ReadFile(corpus + "/" + c.Name()); err != nil { + f.Error("failed to open corpus file:", err) + } else { + f.Add(b) + } + } + } + f.Fuzz(func(t *testing.T, data []byte) { + var i interface{} + if Unmarshal(data, &i) != nil { + t.Skip() + } + if _, err := Marshal(i); err != nil { + t.Errorf("failed to re-marshal: %+v\n", err) + t.Logf("original: 0x%x\n", data) + t.Logf("value: %#v\n", i) + bl, err := MarshalBlock(i) + if err != nil { + t.Error("block: failed to marshal:", err) + } else { + t.Log("block:", bl) + } + } + }) +} + +func FuzzBlock(f *testing.F) { + for _, c := range cases { + f.Add(c.block) + } + const corpus = "testdata/block/corpus" + if dir, err := testdata.ReadDir(corpus); err != nil { + f.Fatal("failed to read corpus dir:", err) + } else { + for _, c := range dir { + if c.IsDir() { + continue + } + if b, err := os.ReadFile(corpus + "/" + c.Name()); err != nil { + f.Error("failed to open corpus file:", err) + } else { + f.Add(string(b)) + } + } + } + f.Fuzz(func(t *testing.T, data string) { + var i interface{} + if UnmarshalBlock([]byte(data), &i) != nil { + t.Skip() + } + if _, err := MarshalBlock(i); err != nil { + t.Errorf("failed to re-marshal: %+v\n", err) + t.Log("original:", data) + t.Logf("value: %#v\n", i) + } + }) +} diff --git a/go.mod b/go.mod index b8288f8..97e8910 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/jmank88/ubjson -go 1.12 +go 1.18 require github.com/pkg/errors v0.8.1 diff --git a/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea b/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea new file mode 100644 index 0000000..dd2351e --- /dev/null +++ b/testdata/fuzz/FuzzBinary/52a8589fb3fbeb9e223a988400f8a0a12f2f1952d87f0bae9e950c94a90fabea @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("[$d#U\x190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f b/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f new file mode 100644 index 0000000..3fffd2b --- /dev/null +++ b/testdata/fuzz/FuzzBinary/b5ea624f1f6cd506a15f6ca3a5f86c38549135c789b8d97dbbf084d21369685f @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("[$C#U\x010") diff --git a/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca b/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca new file mode 100644 index 0000000..3e0f094 --- /dev/null +++ b/testdata/fuzz/FuzzBlock/6e80b181765c2b8b7791e98f75c5c8cbd830647bdbba557f2cfa63f6d7b197ca @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[][$][C][#][U][1][0]") diff --git a/ubjson_test.go b/ubjson_test.go index 143c694..aa167fa 100644 --- a/ubjson_test.go +++ b/ubjson_test.go @@ -16,36 +16,6 @@ func init() { } } -// A Values struct holds one of each primitive type with a corresponding -// Encode/Decode UBJSON value method. -type Values struct { - Int int - UInt8 uint8 - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - Float32 float32 - Float64 float64 - Bool bool - String string - Char Char - HighPrecNumber HighPrecNumber - - IntPtr *int - UInt8Ptr *uint8 - Int8Ptr *int8 - Int16Ptr *int16 - Int32Ptr *int32 - Int64Ptr *int64 - Float32Ptr *float32 - Float64Ptr *float64 - BoolPtr *bool - StringPtr *string - CharPtr *Char - HighPrecNumberPtr *HighPrecNumber -} - type testCase struct { value interface{} binary []byte @@ -82,7 +52,7 @@ var cases = map[string]testCase{ "C=a": {Char('a'), []byte{'C', 0x61}, "[C][a]"}, "string=string": {"string", append([]byte{'S', 0x55, 0x06}, "string"...), "[S][U][6][string]"}, - "string=empty": {"", []byte{'S', 0x55, 0x00}, "[S][U][0]"}, + "string=empty": {"", []byte{'S', 0x55, 0x00}, "[S][U][0]"}, "Array-empty": {[0]int{}, []byte{0x5b, 0x23, 0x55, 0x0}, "[[][#][U][0]"}, @@ -132,6 +102,12 @@ var cases = map[string]testCase{ "Object=complex-struct": {complexStruct, complexStructBinary, complexStructBlock}, "Object=complex-map": {complexMap, complexMapBinary, complexMapBlock}, + + // fuzz trophies + "Array-Char": {[]Char{'a'}, []byte{'[', '$', 'C', '#', 'U', 0x01, 'a'}, + "[[][$][C][#][U][1]\n\t[a]"}, + "Array-HighPrecNumber": {[]HighPrecNumber{"3.402823e38"}, append([]byte{'[', '$', 'H', '#', 'U', 0x01, 0x55, 0x0B}, "3.402823e38"...), + "[[][$][H][#][U][1]\n\t[U][11][3.402823e38]"}, } type complexType struct {