From f98b9771f4f8b4a47fd4d9f5a101d762a26d62c0 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev <moiseev.georgii@gmail.com> Date: Mon, 17 Jul 2023 11:34:09 +0300 Subject: [PATCH] msgpack: fix decoding intervals with int64 It is possible for interval to have days, hours, minutes and seconds larger than INT_MAX (or less than INT_MIN). Before this patch, msgpack decoding had failed to parse intervals with msgpack int64 and uint64. int64_t should be enough to store any value allowed for datetime intervals. Closes #8887 NO_DOC=small bug fix (cherry picked from commit 01c7ae1128e181a9be341aa82c0abcf3f1afe196) --- .../unreleased/gh-8887-fix-interval-int64.md | 4 ++ src/lib/core/mp_interval.c | 6 +-- test/app-tap/datetime.test.lua | 13 ++++-- test/unit/interval.c | 46 ++++++++++++++++++- 4 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/gh-8887-fix-interval-int64.md diff --git a/changelogs/unreleased/gh-8887-fix-interval-int64.md b/changelogs/unreleased/gh-8887-fix-interval-int64.md new file mode 100644 index 0000000000..20142aea11 --- /dev/null +++ b/changelogs/unreleased/gh-8887-fix-interval-int64.md @@ -0,0 +1,4 @@ +## bugfix/msgpack + +* Fixed decoding datetime intervals with fields larger than possible int32 + values (gh-8887). diff --git a/src/lib/core/mp_interval.c b/src/lib/core/mp_interval.c index 6d633d0ea8..2c0486224c 100644 --- a/src/lib/core/mp_interval.c +++ b/src/lib/core/mp_interval.c @@ -116,7 +116,7 @@ interval_unpack(const char **data, uint32_t len, struct interval *itv) memset(itv, 0, sizeof(*itv)); for (uint32_t i = 0; i < count; ++i) { uint32_t field = mp_load_u8(data); - int32_t value; + int64_t value; enum mp_type type = mp_typeof(**data); if (type == MP_UINT) { if (mp_check_uint(*data, end) > 0) @@ -127,7 +127,7 @@ interval_unpack(const char **data, uint32_t len, struct interval *itv) } else { return NULL; } - if (mp_read_int32(data, &value) != 0) + if (mp_read_int64(data, &value) != 0) return NULL; switch (field) { case FIELD_YEAR: @@ -155,7 +155,7 @@ interval_unpack(const char **data, uint32_t len, struct interval *itv) itv->nsec = value; break; case FIELD_ADJUST: - if (value > (int32_t)DT_SNAP) + if (value > (int64_t)DT_SNAP) return NULL; itv->adjust = (dt_adjust_t)value; break; diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua index 2349fdf252..85a00b2afb 100755 --- a/test/app-tap/datetime.test.lua +++ b/test/app-tap/datetime.test.lua @@ -5,6 +5,7 @@ local test = tap.test('errno') local date = require('datetime') local ffi = require('ffi') local json = require('json') +local msgpack = require('msgpack') local TZ = date.TZ test:plan(39) @@ -1312,7 +1313,7 @@ test:test("Time interval operations - different timezones", function(test) end) test:test("Time intervals creation - range checks", function(test) - test:plan(63) + test:plan(83) local inew = date.interval.new @@ -1377,12 +1378,18 @@ test:test("Time intervals creation - range checks", function(test) local val_max = math.floor(range_max) local attrib_min = {[name] = -val_max} - test:is(tostring(inew(attrib_min)), iv_str_repr(attrib_min), + local iv_min = inew(attrib_min) + test:is(tostring(iv_min), iv_str_repr(attrib_min), ('interval %s is allowed'):format(json.encode(attrib_min))) + test:is(msgpack.decode(msgpack.encode(iv_min)), iv_min, + ('msgpack can process interval %s'):format(json.encode(attrib_min))) local attrib_max = {[name] = val_max} - test:is(tostring(inew(attrib_max)), iv_str_repr(attrib_max), + local iv_max = inew(attrib_max) + test:is(tostring(iv_max), iv_str_repr(attrib_max), ('interval %s is allowed'):format(json.encode(attrib_max))) + test:is(msgpack.decode(msgpack.encode(iv_max)), iv_max, + ('msgpack can process interval %s'):format(json.encode(attrib_max))) local attrib_over_min = {[name] = -val_max - 1} assert_raises( diff --git a/test/unit/interval.c b/test/unit/interval.c index 6a3d2502c3..c3067dcf6f 100644 --- a/test/unit/interval.c +++ b/test/unit/interval.c @@ -1,5 +1,6 @@ #include <stdio.h> #include <assert.h> +#include <limits.h> #include "string.h" #include "datetime.h" @@ -96,11 +97,54 @@ test_interval_encode_decode(void) is(result.adjust, DT_EXCESS, "Adjust value is right"); } +static void +test_interval_encode_decode_values_outside_int32_limits(void) +{ + struct interval itv; + memset(&itv, 0, sizeof(itv)); + struct interval result; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.day = (double)INT32_MIN - 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.day = (double)INT32_MAX + 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.hour = (double)INT32_MIN - 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.hour = (double)INT32_MAX + 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.min = (double)INT32_MIN - 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.min = (double)INT32_MAX + 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.sec = (double)INT32_MIN - 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); + + itv.sec = (double)INT32_MAX + 1; + interval_mp_recode(&itv, &result); + ok(is_interval_equal(&itv, &result), "Intervals are equal."); +} + int main(void) { - plan(21); + plan(30); test_interval_sizeof(); test_interval_encode_decode(); + test_interval_encode_decode_values_outside_int32_limits(); return check_plan(); } -- GitLab