gojay

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

commit efc4a69235597da330054429f6486da6eb48fb68
parent 0298226a77a70f5ad3ac2b0f45a6a9dbe2837597
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Mon, 21 May 2018 23:55:12 +0800

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

Fix cases where malformed integers could cause a panic
Diffstat:
Mdecode_number.go | 10+++++++---
Mdecode_number_float.go | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mdecode_number_float_test.go | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_int.go | 234++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mdecode_number_int_test.go | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_object_test.go | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_stream.go | 9++++++++-
Mdecode_stream_pool.go | 1+
Mdecode_string_unicode.go | 4++--
Aexamples/fuzz/main.go | 36++++++++++++++++++++++++++++++++++++
10 files changed, 673 insertions(+), 81 deletions(-)

diff --git a/decode_number.go b/decode_number.go @@ -85,7 +85,7 @@ func (dec *Decoder) getExponent() int64 { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { // is positive case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = dec.cursor + end = dec.cursor + 1 case '-': dec.cursor++ return -dec.getExponent() @@ -99,8 +99,12 @@ func (dec *Decoder) getExponent() int64 { dec.raiseInvalidJSONErr(dec.cursor) return 0 } - return dec.atoi64(start, end) + return dec.atoi64(start, end-1) } } - return dec.atoi64(start, end) + if start == end { + dec.raiseInvalidJSONErr(dec.cursor) + return 0 + } + return dec.atoi64(start, end-1) } diff --git a/decode_number_float.go b/decode_number_float.go @@ -15,7 +15,7 @@ func (dec *Decoder) decodeFloat64(v *float64) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat(c) + val, err := dec.getFloat() if err != nil { return err } @@ -23,7 +23,7 @@ func (dec *Decoder) decodeFloat64(v *float64) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getFloat(c) + val, err := dec.getFloatNegative() if err != nil { return err } @@ -48,7 +48,20 @@ func (dec *Decoder) decodeFloat64(v *float64) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getFloat(b byte) (float64, error) { +func (dec *Decoder) getFloatNegative() (float64, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getFloat() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getFloat() (float64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -73,9 +86,16 @@ func (dec *Decoder) getFloat(b byte) (float64, error) { } else if c == 'e' || c == 'E' { afterDecimal := dec.atoi64(start, end) dec.cursor = i + 1 - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float64(floatVal) * (1 / float64(pow10uint64[exp*-1+1])), nil @@ -88,14 +108,21 @@ func (dec *Decoder) getFloat(b byte) (float64, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(start, end) - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] return float64(beforeDecimal+afterDecimal) / float64(pow), nil case 'e', 'E': - dec.cursor = dec.cursor + 2 + dec.cursor = j + 1 // we get part before decimal as integer beforeDecimal := uint64(dec.atoi64(start, end)) // get exponent exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float64(beforeDecimal) * (1 / float64(pow10uint64[exp*-1+1])), nil @@ -126,7 +153,7 @@ func (dec *Decoder) decodeFloat32(v *float32) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat32(c) + val, err := dec.getFloat32() if err != nil { return err } @@ -134,7 +161,7 @@ func (dec *Decoder) decodeFloat32(v *float32) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getFloat32(c) + val, err := dec.getFloat32Negative() if err != nil { return err } @@ -159,7 +186,20 @@ func (dec *Decoder) decodeFloat32(v *float32) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getFloat32(b byte) (float32, error) { +func (dec *Decoder) getFloat32Negative() (float32, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getFloat32() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getFloat32() (float32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -184,9 +224,16 @@ func (dec *Decoder) getFloat32(b byte) (float32, error) { } else if c == 'e' || c == 'E' { afterDecimal := dec.atoi32(start, end) dec.cursor = i + 1 - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] floatVal := float32(beforeDecimal+afterDecimal) / float32(pow) exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float32(floatVal) * (1 / float32(pow10uint64[exp*-1+1])), nil @@ -199,14 +246,21 @@ func (dec *Decoder) getFloat32(b byte) (float32, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi32(start, end) - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] return float32(beforeDecimal+afterDecimal) / float32(pow), nil case 'e', 'E': - dec.cursor = dec.cursor + 2 + dec.cursor = j + 1 // we get part before decimal as integer beforeDecimal := uint32(dec.atoi32(start, end)) // get exponent exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float32(beforeDecimal) * (1 / float32(pow10uint64[exp*-1+1])), nil diff --git a/decode_number_float_test.go b/decode_number_float_test.go @@ -46,6 +46,20 @@ func TestDecoderFloat64(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-null-err", json: "trua", expectedResult: 0, @@ -133,6 +147,30 @@ func TestDecoderFloat64(t *testing.T) { expectedResult: -788.76, }, { + name: "basic-exp-too-big", + json: "1e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.002e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009e10000000000 ", + expectedResult: 0, + err: true, + }, + { name: "error", json: "83zez4", expectedResult: 0, @@ -256,6 +294,20 @@ func TestDecoderFloat32(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, @@ -306,6 +358,36 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: -0.000082, }, { + name: "basic-exp-too-big", + json: "1e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.0023249242000242e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.002e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009e10000000000 ", + expectedResult: 0, + err: true, + }, + { name: "basic-float", json: "2.4595", expectedResult: 2.4595, diff --git a/decode_number_int.go b/decode_number_int.go @@ -21,7 +21,7 @@ func (dec *Decoder) decodeInt(v *int) error { continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64(c) + val, err := dec.getInt64() if err != nil { return err } @@ -29,7 +29,7 @@ func (dec *Decoder) decodeInt(v *int) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getInt64(dec.data[dec.cursor]) + val, err := dec.getInt64Negative() if err != nil { return err } @@ -58,7 +58,7 @@ func (dec *Decoder) decodeInt(v *int) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt16 reads the next JSON-encoded value from its input and stores it in the int16 pointed to by v. @@ -77,7 +77,7 @@ func (dec *Decoder) decodeInt16(v *int16) error { continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt16(c) + val, err := dec.getInt16() if err != nil { return err } @@ -85,7 +85,7 @@ func (dec *Decoder) decodeInt16(v *int16) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getInt16(dec.data[dec.cursor]) + val, err := dec.getInt16Negative() if err != nil { return err } @@ -111,7 +111,20 @@ func (dec *Decoder) decodeInt16(v *int16) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getInt16(b byte) (int16, error) { +func (dec *Decoder) getInt16Negative() (int16, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getInt16() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getInt16() (int16, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -147,10 +160,17 @@ func (dec *Decoder) getInt16(b byte) (int16, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi16(startDecimal, endDecimal) - pow := pow10uint64[endDecimal-startDecimal+2] + expI := endDecimal - startDecimal + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + 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)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } val := floatVal * float64(pow10uint64[exp+1]) return int16(val), nil case ' ', '\t', '\n', ',', ']', '}': @@ -158,13 +178,14 @@ func (dec *Decoder) getInt16(b byte) (int16, error) { return dec.atoi16(start, end), nil default: dec.cursor = j - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi16(start, end), nil case 'e', 'E': // get init n - return dec.getInt16WithExp(dec.atoi16(start, end), j+1) + dec.cursor = j + 1 + return dec.getInt16WithExp(dec.atoi16(start, end)) case ' ', '\n', '\t', '\r', ',', '}', ']': dec.cursor = j return dec.atoi16(start, end), nil @@ -175,33 +196,39 @@ func (dec *Decoder) getInt16(b byte) (int16, error) { return dec.atoi16(start, end), nil } -func (dec *Decoder) getInt16WithExp(init int16, cursor int) (int16, error) { +func (dec *Decoder) getInt16WithExp(init int16) (int16, error) { var exp uint16 var sign = int16(1) - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint16(digits[dec.data[cursor]]) + uintv := uint16(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv - cursor++ - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + dec.cursor++ + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint16(digits[dec.data[cursor]]) + uintv := uint16(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': + if exp+1 >= uint16(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int16(pow10uint64[exp+1])), nil } return init * int16(pow10uint64[exp+1]), nil default: - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } + if exp+1 >= uint16(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int16(pow10uint64[exp+1])), nil } @@ -229,7 +256,7 @@ func (dec *Decoder) decodeInt8(v *int8) error { continue // we don't look for 0 as leading zeros are invalid per RFC case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt8(c) + val, err := dec.getInt8() if err != nil { return err } @@ -237,7 +264,7 @@ func (dec *Decoder) decodeInt8(v *int8) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getInt8(dec.data[dec.cursor]) + val, err := dec.getInt8Negative() if err != nil { return err } @@ -263,7 +290,20 @@ func (dec *Decoder) decodeInt8(v *int8) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getInt8(b byte) (int8, error) { +func (dec *Decoder) getInt8Negative() (int8, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getInt8() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getInt8() (int8, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -299,10 +339,17 @@ func (dec *Decoder) getInt8(b byte) (int8, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi8(startDecimal, endDecimal) - pow := pow10uint64[endDecimal-startDecimal+2] + expI := endDecimal - startDecimal + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + 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)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } val := floatVal * float64(pow10uint64[exp+1]) return int8(val), nil case ' ', '\t', '\n', ',', ']', '}': @@ -310,13 +357,14 @@ func (dec *Decoder) getInt8(b byte) (int8, error) { return dec.atoi8(start, end), nil default: dec.cursor = j - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi8(start, end), nil case 'e', 'E': // get init n - return dec.getInt8WithExp(dec.atoi8(start, end), j+1) + dec.cursor = j + 1 + return dec.getInt8WithExp(dec.atoi8(start, end)) case ' ', '\n', '\t', '\r', ',', '}', ']': dec.cursor = j return dec.atoi8(start, end), nil @@ -327,33 +375,39 @@ func (dec *Decoder) getInt8(b byte) (int8, error) { return dec.atoi8(start, end), nil } -func (dec *Decoder) getInt8WithExp(init int8, cursor int) (int8, error) { +func (dec *Decoder) getInt8WithExp(init int8) (int8, error) { var exp uint8 var sign = int8(1) - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint8(digits[dec.data[cursor]]) + uintv := uint8(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv - cursor++ - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + dec.cursor++ + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint8(digits[dec.data[cursor]]) + uintv := uint8(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': + if exp+1 >= uint8(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int8(pow10uint64[exp+1])), nil } return init * int8(pow10uint64[exp+1]), nil default: - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } + if exp+1 >= uint8(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int8(pow10uint64[exp+1])), nil } @@ -381,7 +435,7 @@ func (dec *Decoder) decodeInt32(v *int32) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt32(c) + val, err := dec.getInt32() if err != nil { return err } @@ -389,7 +443,7 @@ func (dec *Decoder) decodeInt32(v *int32) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getInt32(dec.data[dec.cursor]) + val, err := dec.getInt32Negative() if err != nil { return err } @@ -414,7 +468,20 @@ func (dec *Decoder) decodeInt32(v *int32) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getInt32(b byte) (int32, error) { +func (dec *Decoder) getInt32Negative() (int32, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getInt32() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getInt32() (int32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -450,10 +517,17 @@ func (dec *Decoder) getInt32(b byte) (int32, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(startDecimal, endDecimal) - pow := pow10uint64[endDecimal-startDecimal+2] + expI := endDecimal - startDecimal + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + 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)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } val := floatVal * float64(pow10uint64[exp+1]) return int32(val), nil case ' ', '\t', '\n', ',', ']', '}': @@ -461,13 +535,14 @@ func (dec *Decoder) getInt32(b byte) (int32, error) { return dec.atoi32(start, end), nil default: dec.cursor = j - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi32(start, end), nil case 'e', 'E': // get init n - return dec.getInt32WithExp(dec.atoi32(start, end), j+1) + dec.cursor = j + 1 + return dec.getInt32WithExp(dec.atoi32(start, end)) case ' ', '\n', '\t', '\r', ',', '}', ']': dec.cursor = j return dec.atoi32(start, end), nil @@ -478,33 +553,39 @@ func (dec *Decoder) getInt32(b byte) (int32, error) { return dec.atoi32(start, end), nil } -func (dec *Decoder) getInt32WithExp(init int32, cursor int) (int32, error) { +func (dec *Decoder) getInt32WithExp(init int32) (int32, error) { var exp uint32 var sign = int32(1) - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint32(digits[dec.data[cursor]]) + uintv := uint32(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv - cursor++ - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + dec.cursor++ + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint32(digits[dec.data[cursor]]) + uintv := uint32(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': + if exp+1 >= uint32(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int32(pow10uint64[exp+1])), nil } return init * int32(pow10uint64[exp+1]), nil default: - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } + if exp+1 >= uint32(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int32(pow10uint64[exp+1])), nil } @@ -533,7 +614,7 @@ func (dec *Decoder) decodeInt64(v *int64) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64(c) + val, err := dec.getInt64() if err != nil { return err } @@ -541,7 +622,7 @@ func (dec *Decoder) decodeInt64(v *int64) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getInt64(dec.data[dec.cursor]) + val, err := dec.getInt64Negative() if err != nil { return err } @@ -566,7 +647,20 @@ func (dec *Decoder) decodeInt64(v *int64) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getInt64(b byte) (int64, error) { +func (dec *Decoder) getInt64Negative() (int64, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getInt64() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getInt64() (int64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -605,10 +699,17 @@ func (dec *Decoder) getInt64(b byte) (int64, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(startDecimal, endDecimal) - pow := pow10uint64[endDecimal-startDecimal+2] + expI := endDecimal - startDecimal + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + 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)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } val := floatVal * float64(pow10uint64[exp+1]) return int64(val), nil case ' ', '\t', '\n', ',', ']', '}': @@ -616,13 +717,14 @@ func (dec *Decoder) getInt64(b byte) (int64, error) { return dec.atoi64(start, end), nil default: dec.cursor = j - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } return dec.atoi64(start, end), nil case 'e', 'E': // get init n - return dec.getInt64WithExp(dec.atoi64(start, end), j+1) + dec.cursor = j + 1 + return dec.getInt64WithExp(dec.atoi64(start, end)) } // invalid json we expect numbers, dot (single one), comma, or spaces return 0, dec.raiseInvalidJSONErr(dec.cursor) @@ -630,33 +732,39 @@ func (dec *Decoder) getInt64(b byte) (int64, error) { return dec.atoi64(start, end), nil } -func (dec *Decoder) getInt64WithExp(init int64, cursor int) (int64, error) { +func (dec *Decoder) getInt64WithExp(init int64) (int64, error) { var exp uint64 var sign = int64(1) - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '+': continue case '-': sign = -1 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint64(digits[dec.data[cursor]]) + uintv := uint64(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv - cursor++ - for ; cursor < dec.length || dec.read(); cursor++ { - switch dec.data[cursor] { + dec.cursor++ + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint64(digits[dec.data[cursor]]) + uintv := uint64(digits[dec.data[dec.cursor]]) exp = (exp << 3) + (exp << 1) + uintv case ' ', '\t', '\n', '}', ',', ']': + if exp+1 >= uint64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int64(pow10uint64[exp+1])), nil } return init * int64(pow10uint64[exp+1]), nil default: - return 0, InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } + if exp+1 >= uint64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } if sign == -1 { return init * (1 / int64(pow10uint64[exp+1])), nil } diff --git a/decode_number_int_test.go b/decode_number_int_test.go @@ -38,6 +38,20 @@ func TestDecoderInt(t *testing.T) { expectedResult: 0, }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-null-err", json: "nxll", expectedResult: 0, @@ -83,6 +97,12 @@ func TestDecoderInt(t *testing.T) { expectedResult: -2349557, }, { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { name: "basic-float", json: "2.4595", expectedResult: 2, @@ -189,6 +209,20 @@ func TestDecoderInt(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "error3", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -291,6 +325,20 @@ func TestDecoderInt64(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-skip-data-err", json: "trua", expectedResult: 0, @@ -383,6 +431,13 @@ func TestDecoderInt64(t *testing.T) { expectedResult: 0, }, { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, @@ -413,6 +468,30 @@ func TestDecoderInt64(t *testing.T) { expectedResult: -800000, }, { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e1000000000000000000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { name: "basic-exponent-negative-positive-exp4", json: "8ea+00a5", expectedResult: 0, @@ -534,6 +613,20 @@ func TestDecoderInt32(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-skip-data-err", json: "trua", expectedResult: 0, @@ -590,6 +683,18 @@ func TestDecoderInt32(t *testing.T) { expectedResult: 120, }, { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { name: "basic-exponent-positive-positive-exp1", json: "3.5e+005 ", expectedResult: 350000, @@ -655,12 +760,49 @@ func TestDecoderInt32(t *testing.T) { expectedResult: -800000, }, { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e100000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e100000000000 ", + expectedResult: 0, + err: true, + }, + { name: "basic-exponent-err", json: "3e", expectedResult: 0, err: true, }, { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-float", json: "8.32 ", expectedResult: 8, @@ -789,6 +931,20 @@ func TestDecoderInt16(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-skip-data-err", json: "trua", expectedResult: 0, @@ -901,6 +1057,30 @@ func TestDecoderInt16(t *testing.T) { expectedResult: -100, }, { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { name: "basic-exponent-negative-positive-exp2", json: "-5e+03", expectedResult: -5000, @@ -947,6 +1127,13 @@ func TestDecoderInt16(t *testing.T) { err: true, }, { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -1043,6 +1230,20 @@ func TestDecoderInt8(t *testing.T) { expectedResult: 0, }, { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-null-err", json: "nxll", expectedResult: 0, @@ -1172,11 +1373,42 @@ func TestDecoderInt8(t *testing.T) { expectedResult: -30, }, { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { name: "basic-exponent-negative-positive-exp4", json: "-8e+001", expectedResult: -80, }, { + name: "exponent-err-too-big2", + json: "0e100 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big2", + json: "0.1e100 ", + expectedResult: 0, + err: true, + }, + { name: "basic-exponent-err", json: "3e", expectedResult: 0, diff --git a/decode_object_test.go b/decode_object_test.go @@ -53,6 +53,74 @@ func TestDecodeObjectBasic(t *testing.T) { err: false, }, { + name: "basic-with-exponent", + json: `{ + "testStr": "hello world!", + "testInt": 3e3, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 3000, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + { + name: "basic-with-exponent3", + json: `{ + "testStr": "hello world!", + "testInt": 3e-3, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 12e-3, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 0, + testBool: true, + testFloat32: 2.345, + testFloat64: 0.012, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + { name: "basic-err-invalid-type", json: `1`, expectedResult: testObject{}, diff --git a/decode_stream.go b/decode_stream.go @@ -1,6 +1,7 @@ package gojay import ( + "sync" "time" ) @@ -19,6 +20,7 @@ type stream struct{} // // It implements conext.Context and provide a channel to notify interruption. type StreamDecoder struct { + mux sync.RWMutex *Decoder done chan struct{} deadline *time.Time @@ -64,7 +66,10 @@ func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error { } } close(dec.done) - return InvalidJSONError("Invalid JSON while parsing line delimited JSON") + dec.mux.Lock() + err := dec.raiseInvalidJSONErr(dec.cursor) + dec.mux.Unlock() + return err } // context.Context implementation @@ -96,6 +101,8 @@ func (dec *StreamDecoder) SetDeadline(t time.Time) { func (dec *StreamDecoder) Err() error { select { case <-dec.done: + dec.mux.RLock() + defer dec.mux.RUnlock() return dec.err default: return nil diff --git a/decode_stream_pool.go b/decode_stream_pool.go @@ -17,6 +17,7 @@ func (s stream) NewDecoder(r io.Reader) *StreamDecoder { streamDec := &StreamDecoder{ Decoder: dec, done: make(chan struct{}, 1), + mux: sync.RWMutex{}, } return streamDec } diff --git a/decode_string_unicode.go b/decode_string_unicode.go @@ -56,7 +56,7 @@ func (dec *Decoder) parseUnicode() ([]byte, error) { // check if code can be a surrogate utf16 if utf16.IsSurrogate(r) { if dec.cursor >= dec.length && !dec.read() { - return nil, InvalidJSONError("Invalid JSON") + return nil, dec.raiseInvalidJSONErr(dec.cursor) } c := dec.data[dec.cursor] if c != '\\' { @@ -65,7 +65,7 @@ func (dec *Decoder) parseUnicode() ([]byte, error) { } dec.cursor++ if dec.cursor >= dec.length && !dec.read() { - return nil, InvalidJSONError("Invalid JSON") + return nil, dec.raiseInvalidJSONErr(dec.cursor) } c = dec.data[dec.cursor] if c != 'u' { diff --git a/examples/fuzz/main.go b/examples/fuzz/main.go @@ -0,0 +1,36 @@ +package fuzz + +import ( + "github.com/francoispqt/gojay" +) + +type user struct { + id int + name string + email string +} + +// implement gojay.UnmarshalerJSONObject +func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { + switch key { + case "id": + return dec.Int(&u.id) + case "name": + return dec.String(&u.name) + case "email": + return dec.String(&u.email) + } + return nil +} +func (u *user) NKeys() int { + return 3 +} + +func Fuzz(input []byte) int { + u := &user{} + err := gojay.UnmarshalJSONObject(input, u) + if err != nil { + return 0 + } + return 1 +}