gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit b8dc210cd231638e2cdf5b330e4050ceab664d0f
parent 7166fd659b3e557c8be1b04c95527add7a0f5f67
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Fri, 25 May 2018 00:00:24 +0800

Merge pull request #33 from francoispqt/update/fuzz-crashers-fix

- fix cases where malformed numbers can cause panic
- make uint treating negative numbers in line with `encoding/json` returning an InvalidUnmarshalError
Diffstat:
Mdecode_number.go | 16++++++++--------
Mdecode_number_float.go | 59+++++++++++++++++++++++++++++++++++++++++------------------
Mdecode_number_float_test.go | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdecode_number_int.go | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mdecode_number_int_test.go | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdecode_number_uint.go | 42+++++++++++++++++-------------------------
Mdecode_number_uint_test.go | 36++++++++++++++++++++++++------------
Mdecode_object_test.go | 42++++++++++++++++++++++++++++++++++++++++++
Mdecode_test.go | 16++++++++--------
Mdecode_unsafe_test.go | 8++++----
Aexamples/fuzz/Makefile | 12++++++++++++
Mexamples/fuzz/main.go | 16+++++++++++++---
12 files changed, 533 insertions(+), 109 deletions(-)

diff --git a/decode_number.go b/decode_number.go @@ -79,7 +79,7 @@ func (dec *Decoder) skipNumber() (int, error) { return end, nil } -func (dec *Decoder) getExponent() int64 { +func (dec *Decoder) getExponent() (int64, error) { start := dec.cursor end := dec.cursor for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { @@ -88,7 +88,8 @@ func (dec *Decoder) getExponent() int64 { end = dec.cursor + 1 case '-': dec.cursor++ - return -dec.getExponent() + exp, err := dec.getExponent() + return -exp, err case '+': dec.cursor++ return dec.getExponent() @@ -96,15 +97,14 @@ func (dec *Decoder) getExponent() int64 { // if nothing return 0 // could raise error if start == end { - dec.raiseInvalidJSONErr(dec.cursor) - return 0 + return 0, dec.raiseInvalidJSONErr(dec.cursor) } - return dec.atoi64(start, end-1) + return dec.atoi64(start, end-1), nil } } if start == end { - dec.raiseInvalidJSONErr(dec.cursor) - return 0 + + return 0, dec.raiseInvalidJSONErr(dec.cursor) } - return dec.atoi64(start, end-1) + return dec.atoi64(start, end-1), nil } diff --git a/decode_number_float.go b/decode_number_float.go @@ -83,7 +83,7 @@ func (dec *Decoder) getFloat() (float64, error) { end = i beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) continue - } else if c == 'e' || c == 'E' { + } else if (c == 'e' || c == 'E') && j < i-1 { afterDecimal := dec.atoi64(start, end) dec.cursor = i + 1 expI := end - start + 2 @@ -92,19 +92,26 @@ func (dec *Decoder) getFloat() (float64, error) { } pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { - return float64(floatVal) * (1 / float64(pow10uint64[exp*-1+1])), nil + return float64(floatVal) * (1 / float64(pow10uint64[pExp])), nil } - return float64(floatVal) * float64(pow10uint64[exp+1]), nil + return float64(floatVal) * float64(pow10uint64[pExp]), nil } dec.cursor = i break } + if end >= dec.length || end <= start { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(start, end) @@ -119,15 +126,19 @@ func (dec *Decoder) getFloat() (float64, error) { // we get part before decimal as integer beforeDecimal := uint64(dec.atoi64(start, end)) // get exponent - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { - return float64(beforeDecimal) * (1 / float64(pow10uint64[exp*-1+1])), nil + return float64(beforeDecimal) * (1 / float64(pow10uint64[pExp])), nil } - return float64(beforeDecimal) * float64(pow10uint64[exp+1]), nil + return float64(beforeDecimal) * float64(pow10uint64[pExp]), nil case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal dec.cursor = j return float64(dec.atoi64(start, end)), nil @@ -221,7 +232,7 @@ func (dec *Decoder) getFloat32() (float32, error) { end = i beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) continue - } else if c == 'e' || c == 'E' { + } else if (c == 'e' || c == 'E') && j < i-1 { afterDecimal := dec.atoi32(start, end) dec.cursor = i + 1 expI := end - start + 2 @@ -230,19 +241,26 @@ func (dec *Decoder) getFloat32() (float32, error) { } pow := pow10uint64[expI] floatVal := float32(beforeDecimal+afterDecimal) / float32(pow) - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { - return float32(floatVal) * (1 / float32(pow10uint64[exp*-1+1])), nil + return float32(floatVal) * (1 / float32(pow10uint64[pExp])), nil } - return float32(floatVal) * float32(pow10uint64[exp+1]), nil + return float32(floatVal) * float32(pow10uint64[pExp]), nil } dec.cursor = i break } + if end >= dec.length || end <= start { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi32(start, end) @@ -257,15 +275,20 @@ func (dec *Decoder) getFloat32() (float32, error) { // we get part before decimal as integer beforeDecimal := uint32(dec.atoi32(start, end)) // get exponent - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 + // log.Print(exp, " after") + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } // if exponent is negative if exp < 0 { - return float32(beforeDecimal) * (1 / float32(pow10uint64[exp*-1+1])), nil + return float32(beforeDecimal) * (1 / float32(pow10uint64[pExp])), nil } - return float32(beforeDecimal) * float32(pow10uint64[exp+1]), nil + return float32(beforeDecimal) * float32(pow10uint64[pExp]), nil case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal dec.cursor = j return float32(dec.atoi32(start, end)), nil diff --git a/decode_number_float_test.go b/decode_number_float_test.go @@ -15,6 +15,7 @@ func TestDecoderFloat64(t *testing.T) { name string json string expectedResult float64 + skipResult bool err bool errType interface{} }{ @@ -67,6 +68,48 @@ func TestDecoderFloat64(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-err1", + json: "0.", + expectedResult: 0, + err: true, + }, + { + name: "basic-err2", + json: "-1.", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "exp-err", + json: "0e-20", + expectedResult: 0, + err: true, + }, + { + name: "exp-err3", + json: "-9e-60", + expectedResult: 0, + err: true, + }, + { + name: "exp-err4", + json: "0.e-2", + skipResult: true, + err: true, + }, + { + name: "exp-err5", + json: "-5.E-2", + skipResult: true, + err: true, + }, + { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, @@ -160,6 +203,12 @@ func TestDecoderFloat64(t *testing.T) { }, { name: "basic-exp-too-big", + json: "0e9223372036000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", json: "1.00232492420002423545849009", expectedResult: 0, err: true, @@ -178,6 +227,18 @@ func TestDecoderFloat64(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "exponent-err", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err", + json: "0e", + expectedResult: 0, + err: true, + }, + { name: "error", json: "-83zez4", expectedResult: 0, @@ -210,7 +271,9 @@ func TestDecoderFloat64(t *testing.T) { } else { assert.Nil(t, err, "Err must be nil") } - assert.Equal(t, testCase.expectedResult*1000000, math.Round(v*1000000), fmt.Sprintf("v must be equal to %f", testCase.expectedResult)) + if !testCase.skipResult { + assert.Equal(t, testCase.expectedResult*1000000, math.Round(v*1000000), fmt.Sprintf("v must be equal to %f", testCase.expectedResult)) + } }) } t.Run("pool-error", func(t *testing.T) { @@ -256,6 +319,7 @@ func TestDecoderFloat32(t *testing.T) { name string json string expectedResult float32 + skipResult bool err bool errType interface{} }{ @@ -280,6 +344,47 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, }, { + name: "basic-err1", + json: "0.", + expectedResult: 0, + err: true, + }, + { + name: "basic-err2", + json: "-1.", + expectedResult: 0, + err: true, + }, + { + name: "exp-err", + json: "0e-20", + expectedResult: 0, + err: true, + }, + { + name: "exp-err3", + json: "-9e-60", + expectedResult: 0, + err: true, + }, + { + name: "exp-err4", + json: "0.e-2", + skipResult: true, + err: true, + }, + { + name: "exp-err5", + json: "-5.E-2", + skipResult: true, + err: true, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + }, + { name: "basic-null-err", json: "trua", expectedResult: 0, @@ -301,6 +406,12 @@ func TestDecoderFloat32(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { name: "basic-negative-err", json: "-q", expectedResult: 0, @@ -432,6 +543,12 @@ func TestDecoderFloat32(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "exponent-err", + json: "0e", + expectedResult: 0, + err: true, + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -457,7 +574,13 @@ func TestDecoderFloat32(t *testing.T) { } else { assert.Nil(t, err, "Err must be nil") } - assert.Equal(t, float64(testCase.expectedResult*1000000), math.Round(float64(v*1000000)), fmt.Sprintf("v must be equal to %f", testCase.expectedResult)) + if !testCase.skipResult { + assert.Equal( + t, + float64(testCase.expectedResult*1000000), math.Round(float64(v*1000000)), + fmt.Sprintf("v must be equal to %f", testCase.expectedResult), + ) + } }) } t.Run("pool-error", func(t *testing.T) { diff --git a/decode_number_int.go b/decode_number_int.go @@ -138,15 +138,18 @@ func (dec *Decoder) getInt16() (int16, error) { // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point - startDecimal := j + 1 - endDecimal := j + 1 j++ + startDecimal := j + endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': + if startDecimal > endDecimal { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first @@ -167,11 +170,15 @@ func (dec *Decoder) getInt16() (int16, error) { pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } - val := floatVal * float64(pow10uint64[exp+1]) + val := floatVal * float64(pow10uint64[pExp]) return int16(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j @@ -215,24 +222,26 @@ func (dec *Decoder) getInt16WithExp(init int16) (int16, error) { uintv := uint16(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint16(len(pow10uint64)) { + exp = exp + 1 + if exp >= uint16(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { - return init * (1 / int16(pow10uint64[exp+1])), nil + return init * (1 / int16(pow10uint64[exp])), nil } - return init * int16(pow10uint64[exp+1]), nil + return init * int16(pow10uint64[exp]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } } - if exp+1 >= uint16(len(pow10uint64)) { + exp = exp + 1 + if exp >= uint16(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } if sign == -1 { - return init * (1 / int16(pow10uint64[exp+1])), nil + return init * (1 / int16(pow10uint64[exp])), nil } - return init * int16(pow10uint64[exp+1]), nil + return init * int16(pow10uint64[exp]), nil default: return 0, dec.raiseInvalidJSONErr(dec.cursor) } @@ -317,15 +326,18 @@ func (dec *Decoder) getInt8() (int8, error) { // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point - startDecimal := j + 1 - endDecimal := j + 1 j++ + startDecimal := j + endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': + if startDecimal > endDecimal { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first @@ -346,11 +358,15 @@ func (dec *Decoder) getInt8() (int8, error) { pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } - val := floatVal * float64(pow10uint64[exp+1]) + val := floatVal * float64(pow10uint64[pExp]) return int8(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j @@ -495,15 +511,19 @@ func (dec *Decoder) getInt32() (int32, error) { // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point - startDecimal := j + 1 - endDecimal := j + 1 j++ + startDecimal := j + endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': + // if eg 1.E + if startDecimal > endDecimal { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first @@ -524,11 +544,15 @@ func (dec *Decoder) getInt32() (int32, error) { pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } - val := floatVal * float64(pow10uint64[exp+1]) + val := floatVal * float64(pow10uint64[pExp]) return int32(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j @@ -677,15 +701,19 @@ func (dec *Decoder) getInt64() (int64, error) { // look for exponent (e,E) as exponent can change the // way number should be parsed to int. // if no exponent found, just unmarshal the number before decimal point - startDecimal := j + 1 - endDecimal := j + 1 j++ + startDecimal := j + endDecimal := j - 1 for ; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': endDecimal = j continue case 'e', 'E': + // if eg 1.E + if startDecimal > endDecimal { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } dec.cursor = j + 1 // can try unmarshalling to int as Exponent might change decimal number to non decimal // let's get the float value first @@ -706,11 +734,15 @@ func (dec *Decoder) getInt64() (int64, error) { pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent - exp := dec.getExponent() - if +exp+1 >= int64(len(pow10uint64)) { + exp, err := dec.getExponent() + if err != nil { + return 0, err + } + pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs + if pExp >= int64(len(pow10uint64)) || pExp < 0 { return 0, dec.raiseInvalidJSONErr(dec.cursor) } - val := floatVal * float64(pow10uint64[exp+1]) + val := floatVal * float64(pow10uint64[pExp]) return int64(val), nil case ' ', '\t', '\n', ',', ']', '}': dec.cursor = j diff --git a/decode_number_int_test.go b/decode_number_int_test.go @@ -209,20 +209,41 @@ func TestDecoderInt(t *testing.T) { errType: InvalidJSONError(""), }, { - name: "error3", + name: "error4", json: "0.E----", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { - name: "error3", + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "error5", json: "0E40", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -237,7 +258,7 @@ func TestDecoderInt(t *testing.T) { err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") - if testCase.errType != nil { + if testCase.errType != nil && err != nil { assert.IsType( t, testCase.errType, @@ -511,6 +532,40 @@ func TestDecoderInt64(t *testing.T) { err: true, }, { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -760,6 +815,12 @@ func TestDecoderInt32(t *testing.T) { expectedResult: -800000, }, { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, @@ -803,6 +864,34 @@ func TestDecoderInt32(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-float", json: "8.32 ", expectedResult: 8, @@ -1057,6 +1146,12 @@ func TestDecoderInt16(t *testing.T) { expectedResult: -100, }, { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, @@ -1134,6 +1229,41 @@ func TestDecoderInt16(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "0.e", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error8", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -1380,6 +1510,12 @@ func TestDecoderInt8(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { name: "exponent-err-too-big", json: "0.1e10000000000000000000", expectedResult: 0, @@ -1440,6 +1576,48 @@ func TestDecoderInt8(t *testing.T) { err: true, }, { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "0.e", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error8", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error8", + json: "-5.01e", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, diff --git a/decode_number_uint.go b/decode_number_uint.go @@ -20,20 +20,18 @@ func (dec *Decoder) decodeUint8(v *uint8) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint8(c) + val, err := dec.getUint8() if err != nil { return err } *v = val return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getUint8(dec.data[dec.cursor]) + case '-': // if negative, we just set it to 0 and set error + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() if err != nil { return err } - // unsigned int so we don't bother with the sign - *v = val return nil case 'n': dec.cursor++ @@ -54,7 +52,7 @@ func (dec *Decoder) decodeUint8(v *uint8) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getUint8(b byte) (uint8, error) { +func (dec *Decoder) getUint8() (uint8, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -91,20 +89,18 @@ func (dec *Decoder) decodeUint16(v *uint16) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint16(c) + val, err := dec.getUint16() if err != nil { return err } *v = val return nil case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getUint16(dec.data[dec.cursor]) + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() if err != nil { return err } - // unsigned int so we don't bother with the sign - *v = val return nil case 'n': dec.cursor++ @@ -125,7 +121,7 @@ func (dec *Decoder) decodeUint16(v *uint16) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getUint16(b byte) (uint16, error) { +func (dec *Decoder) getUint16() (uint16, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -162,20 +158,18 @@ func (dec *Decoder) decodeUint32(v *uint32) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint32(c) + val, err := dec.getUint32() if err != nil { return err } *v = val return nil case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getUint32(dec.data[dec.cursor]) + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() if err != nil { return err } - // unsigned int so we don't bother with the sign - *v = val return nil case 'n': dec.cursor++ @@ -196,7 +190,7 @@ func (dec *Decoder) decodeUint32(v *uint32) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getUint32(b byte) (uint32, error) { +func (dec *Decoder) getUint32() (uint32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -232,20 +226,18 @@ func (dec *Decoder) decodeUint64(v *uint64) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint64(c) + val, err := dec.getUint64() if err != nil { return err } *v = val return nil case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getUint64(dec.data[dec.cursor]) + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() if err != nil { return err } - // unsigned int so we don't bother with the sign - *v = val return nil case 'n': dec.cursor++ @@ -266,7 +258,7 @@ func (dec *Decoder) decodeUint64(v *uint64) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getUint64(b byte) (uint64, error) { +func (dec *Decoder) getUint64() (uint64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers diff --git a/decode_number_uint_test.go b/decode_number_uint_test.go @@ -30,7 +30,8 @@ func TestDecoderUint64(t *testing.T) { { name: "basic-negative", json: "-2", - expectedResult: 2, + expectedResult: 0, + err: true, }, { name: "basic-null", @@ -77,7 +78,8 @@ func TestDecoderUint64(t *testing.T) { { name: "basic-negative2", json: "-2349557", - expectedResult: 2349557, + expectedResult: 0, + err: true, }, { name: "basic-float", @@ -87,7 +89,8 @@ func TestDecoderUint64(t *testing.T) { { name: "basic-float2", json: "-7.8876", - expectedResult: 7, + expectedResult: 0, + err: true, }, { name: "error1", @@ -182,7 +185,8 @@ func TestDecoderUint32(t *testing.T) { { name: "basic-negative", json: "-2", - expectedResult: 2, + expectedResult: 0, + err: true, }, { name: "basic-null", @@ -206,7 +210,8 @@ func TestDecoderUint32(t *testing.T) { { name: "basic-negative2", json: "-2349557", - expectedResult: 2349557, + expectedResult: 0, + err: true, }, { name: "basic-big", @@ -239,7 +244,8 @@ func TestDecoderUint32(t *testing.T) { { name: "basic-float2", json: "-7.8876", - expectedResult: 7, + expectedResult: 0, + err: true, }, { name: "error", @@ -342,7 +348,8 @@ func TestDecoderUint16(t *testing.T) { { name: "basic-negative", json: "-2", - expectedResult: 2, + expectedResult: 0, + err: true, }, { name: "basic-null", @@ -380,7 +387,8 @@ func TestDecoderUint16(t *testing.T) { { name: "basic-negative2", json: "-24467", - expectedResult: 24467, + expectedResult: 0, + err: true, }, { name: "basic-big", @@ -419,7 +427,8 @@ func TestDecoderUint16(t *testing.T) { { name: "basic-float2", json: "-7.8876", - expectedResult: 7, + expectedResult: 0, + err: true, }, { name: "error", @@ -530,7 +539,8 @@ func TestDecoderUint8(t *testing.T) { { name: "basic-negative", json: "-2", - expectedResult: 2, + expectedResult: 0, + err: true, }, { name: "basic-null", @@ -554,7 +564,8 @@ func TestDecoderUint8(t *testing.T) { { name: "basic-negative2", json: "-234", - expectedResult: 234, + expectedResult: 0, + err: true, }, { name: "basic-big", @@ -595,7 +606,8 @@ func TestDecoderUint8(t *testing.T) { { name: "basic-float2", json: "-7.8876", - expectedResult: 7, + expectedResult: 0, + err: true, }, { name: "error", diff --git a/decode_object_test.go b/decode_object_test.go @@ -410,6 +410,48 @@ func TestDecodeObjectBasic(t *testing.T) { }, err: false, }, + { + name: "basic-skip-data", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "skipObject": { + "escapedString": "string with escaped \\n new line" + }, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": -255, + "skipArray": [[],[],{}], + "testUint16": 3455, + "skipBool": true, + "skipNull": null, + "testUint32": 343443, + "testUint64": 545665757, + "skipString": "skipping string with escaped \\n new line", + "skipInt": 3, + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 4535, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 0, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: true, + }, } for _, testCase := range testCases { diff --git a/decode_test.go b/decode_test.go @@ -99,8 +99,8 @@ func TestUnmarshalAllTypes(t *testing.T) { name: "test decode uint64 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint64(1), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint64(0), *vt, "v must be equal to 1") }, }, { @@ -129,8 +129,8 @@ func TestUnmarshalAllTypes(t *testing.T) { name: "test decode uint32 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint32(1), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint32(0), *vt, "v must be equal to 1") }, }, { @@ -337,8 +337,8 @@ func TestDecodeAllTypes(t *testing.T) { name: "test decode uint64 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint64(1), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint64(0), *vt, "v must be equal to 1") }, }, { @@ -367,8 +367,8 @@ func TestDecodeAllTypes(t *testing.T) { name: "test decode uint32 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint32(1), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint32(0), *vt, "v must be equal to 1") }, }, { diff --git a/decode_unsafe_test.go b/decode_unsafe_test.go @@ -101,8 +101,8 @@ func TestUnmarshalUnsafeAllTypes(t *testing.T) { name: "test decode uint64 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint64) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint64(1), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint64(0), *vt, "v must be equal to 1") }, }, { @@ -131,8 +131,8 @@ func TestUnmarshalUnsafeAllTypes(t *testing.T) { name: "test decode uint32 negative", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*uint32) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint32(1), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint32(0), *vt, "v must be equal to 1") }, }, { diff --git a/examples/fuzz/Makefile b/examples/fuzz/Makefile @@ -0,0 +1,11 @@ +.PHONY: gofuzzbuild +gofuzzbuild: + go-fuzz-build github.com/francoispqt/gojay/examples/fuzz + +.PHONY: gofuzz +gofuzz: + go-fuzz -bin=fuzz-fuzz.zip -workdir=. + +.PHONY: gofuzzclean +gofuzzclean: + rm -rf corpus crashers suppressions fuzz-fuzz.zip +\ No newline at end of file diff --git a/examples/fuzz/main.go b/examples/fuzz/main.go @@ -5,9 +5,12 @@ import ( ) type user struct { - id int - name string - email string + id int + created uint64 + age float64 + name string + email string + friend *user } // implement gojay.UnmarshalerJSONObject @@ -15,10 +18,17 @@ func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "id": return dec.Int(&u.id) + case "created": + return dec.Uint64(&u.created) + case "age": + return dec.Float(&u.age) case "name": return dec.String(&u.name) case "email": return dec.String(&u.email) + case "friend": + uu := &user{} + return dec.Object(uu) } return nil }