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:
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
+}