Skip to content

Commit

Permalink
add support for go1.18 fuzzing; fixes (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmank88 authored Dec 18, 2021
1 parent 4114471 commit a101db2
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 44 deletions.
2 changes: 1 addition & 1 deletion decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
19 changes: 12 additions & 7 deletions encode.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ubjson

import (
"fmt"
"io"
"reflect"

Expand Down Expand Up @@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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))

Expand All @@ -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 {
Expand Down
26 changes: 26 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ubjson

import (
"fmt"
"reflect"
"testing"
"unicode/utf8"
)
Expand Down Expand Up @@ -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)
}
})
}
}
8 changes: 4 additions & 4 deletions errors.go
Original file line number Diff line number Diff line change
@@ -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)
}
1 change: 1 addition & 0 deletions fuzz.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz

package ubjson
Expand Down
83 changes: 83 additions & 0 deletions fuzz_1.18_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module github.com/jmank88/ubjson

go 1.12
go 1.18

require github.com/pkg/errors v0.8.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("[$d#U\x190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("[$C#U\x010")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
string("[[][$][C][#][U][1][0]")
38 changes: 7 additions & 31 deletions ubjson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]"},

Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit a101db2

Please sign in to comment.