From cabb84a6b63696b66fa53ca1787ec50aafcf5ac0 Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Fri, 27 May 2022 10:14:54 +0200 Subject: [PATCH 1/3] Fix wrap around issues on large numerics There seemed to be a mistake on LLONG_MAX vs ULLONG_MAX (the latter is the maximum value for an object of type `unsigned long long int` which JSUINT64 should be). I fixed that. I am unsure about how much performance is lost on the check for overflow on digit check... --- lib/ultrajsondec.c | 16 +++++++++------- tests/test_ujson.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/ultrajsondec.c b/lib/ultrajsondec.c index 73e8a5db..904a84fc 100644 --- a/lib/ultrajsondec.c +++ b/lib/ultrajsondec.c @@ -96,7 +96,7 @@ static FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric (struct DecoderState *ds int chr; char *offset = ds->start; - JSUINT64 overflowLimit = LLONG_MAX; + JSUINT64 overflowLimit = ULLONG_MAX; if (*(offset) == 'I') { goto DECODE_INF; @@ -131,15 +131,17 @@ static FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric (struct DecoderState *ds case '8': case '9': { + // detect overflow on digit shift + if ((intValue * 10ULL) % 10 != 0) + { + hasError = 1; + } + //PERF: Don't do 64-bit arithmetic here unless we know we have to prevIntValue = intValue; intValue = intValue * 10ULL + (JSLONG) (chr - 48); - if (intNeg == 1 && prevIntValue > intValue) - { - hasError = 1; - } - else if (intNeg == -1 && intValue > overflowLimit) + if (prevIntValue > intValue) { hasError = 1; } @@ -169,7 +171,7 @@ static FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric (struct DecoderState *ds } else if (intNeg == -1) { - return SetError(ds, -1, overflowLimit == LLONG_MAX ? "Value is too big!" : "Value is too small"); + return SetError(ds, -1, overflowLimit == LLONG_MAX ? "Value is too big!" : "Value is too small!"); } } goto BREAK_INT_LOOP; diff --git a/tests/test_ujson.py b/tests/test_ujson.py index 9c40b97b..e4bebedf 100644 --- a/tests/test_ujson.py +++ b/tests/test_ujson.py @@ -1055,3 +1055,18 @@ def test_decode_string_utf8(): heap = hp.heapu() print(heap) """ + + +@pytest.mark.parametrize( + "test_input", + [ + ("33333333303333333333"), + ("18446744073709551616"), # 64 bit + ("-18446744073709551616"), # 64 bit + ("-80888888888888888888"), + ], +) +def test_decode_big_numeric(test_input): + with pytest.raises(ujson.JSONDecodeError): + ujson.loads(test_input) + From 06b2e637563a9b48e603c0e10820aafbc58e5937 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 May 2022 08:16:58 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_ujson.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_ujson.py b/tests/test_ujson.py index e4bebedf..95b801e3 100644 --- a/tests/test_ujson.py +++ b/tests/test_ujson.py @@ -1061,12 +1061,11 @@ def test_decode_string_utf8(): "test_input", [ ("33333333303333333333"), - ("18446744073709551616"), # 64 bit - ("-18446744073709551616"), # 64 bit + ("18446744073709551616"), # 64 bit + ("-18446744073709551616"), # 64 bit ("-80888888888888888888"), ], ) def test_decode_big_numeric(test_input): with pytest.raises(ujson.JSONDecodeError): ujson.loads(test_input) - From 15f3635e28b4ac44cc90bfbbec708446dd76258c Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Fri, 27 May 2022 10:53:35 +0200 Subject: [PATCH 3/3] remove exclamation marks --- lib/ultrajsondec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ultrajsondec.c b/lib/ultrajsondec.c index 904a84fc..e09f8c1b 100644 --- a/lib/ultrajsondec.c +++ b/lib/ultrajsondec.c @@ -171,7 +171,7 @@ static FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric (struct DecoderState *ds } else if (intNeg == -1) { - return SetError(ds, -1, overflowLimit == LLONG_MAX ? "Value is too big!" : "Value is too small!"); + return SetError(ds, -1, overflowLimit == LLONG_MAX ? "Value is too big" : "Value is too small"); } } goto BREAK_INT_LOOP;