From 2c8ad0c7393a5025c646638f13130a7eeb3d9637 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov@tarantool.org> Date: Mon, 28 Aug 2023 13:07:52 +0300 Subject: [PATCH] lua/msgpack: add details to msgpack.decode error With this patch, mp_check sets diag with detailed information about the MsgPack decoding error. The diag includes the error reason, which is appended to the error message, and the data offset, which is stored in an error payload field. Possible error reasons are: truncated input, junk after input, illegal code, invalid extension. In case of truncated input, the error also includes the trunc_count payload field, which is set to the number of missing MsgPack values. Failing to decode a MsgPack extension adds two payload fields, ext_type and ext_len, and also an error cause, which is set by the extension decoder. For all extensions except the error extension, the error cause is just "cannot unpack FOO". For the error extension, it includes a detailed cause pointing to the error in the MsgPack data stored in the extension. Currently, the mp_check error is reported only by the Lua msgapck decoder while other mp_check users (e.g. xrow decoder) override it. We may improve on this in future. Examples: tarantool> require('msgpack').decode('\x94\xc0') --- - error: Invalid MsgPack - truncated input ... tarantool> box.error.last():unpack() --- - offset: 2 code: 20 base_type: ClientError type: ClientError trunc_count: 3 message: Invalid MsgPack - truncated input trace: - file: ./src/box/msgpack.c line: 151 ... tarantool> require('msgpack').decode(string.char( > 130, 39, 170, 114, 101, 116, 117, 114, 110, 32, 46, > 46, 46, 33, 145, 199, 74, 3, 129, 0, 145, 134, > 0, 171, 67, 108, 105, 101, 110, 116, 69, 114, 114, > 111, 114, 1, 170, 99, 111, 110, 102, 105, 103, 46, > 108, 117, 97, 2, 211, 0, 0, 0, 0, 0, 0, > 0, 201, 3, 173, 85, 110, 107, 110, 111, 119, 110, > 32, 101, 114, 114, 111, 114, 4, 211, 0, 0, 0, > 0, 0, 0, 0, 0, 5, 211, 0, 0, 0, 0, > 0, 0, 0, 0)) --- - error: Invalid MsgPack - invalid extension ... tarantool> box.error.last():unpack() --- - code: 20 base_type: ClientError prev: Invalid MsgPack - cannot unpack error message: Invalid MsgPack - invalid extension ext_len: 74 ext_type: 3 trace: - file: ./src/box/msgpack.c line: 161 type: ClientError offset: 18 ... tarantool> box.error.last().prev:unpack() --- - code: 20 base_type: ClientError type: ClientError prev: Invalid MsgPack - MP_ERROR_LINE value must be MP_UINT message: Invalid MsgPack - cannot unpack error trace: - file: ./src/box/msgpack.c line: 126 ... tarantool> box.error.last().prev.prev:unpack() --- - offset: 30 code: 20 base_type: ClientError type: ClientError message: Invalid MsgPack - MP_ERROR_LINE value must be MP_UINT trace: - file: ./src/box/mp_error.cc line: 350 ... Closes #7968 NO_DOC=error reporting improvement --- .../unreleased/gh-7968-mp-check-diag.md | 7 + src/box/mp_error.cc | 138 ++++----- src/box/msgpack.c | 74 ++++- src/lua/msgpack.c | 12 +- test/app-luatest/msgpack_test.lua | 267 +++++++++++++++++- 5 files changed, 418 insertions(+), 80 deletions(-) create mode 100644 changelogs/unreleased/gh-7968-mp-check-diag.md diff --git a/changelogs/unreleased/gh-7968-mp-check-diag.md b/changelogs/unreleased/gh-7968-mp-check-diag.md new file mode 100644 index 0000000000..1e2345b8f4 --- /dev/null +++ b/changelogs/unreleased/gh-7968-mp-check-diag.md @@ -0,0 +1,7 @@ +## feature/lua/msgpack + +* Improved error reporting for `msgpack.decode`. Now, an error raised by + `mgpack.decode` has a detailed error message and the offset in the input + data. If `msgpack.decode` failed to unpack a MsgPack extension, it also + includes the error cause pointing to the error in the extension data + (gh-7986). diff --git a/src/box/mp_error.cc b/src/box/mp_error.cc index a6219433e9..511c1ecd1d 100644 --- a/src/box/mp_error.cc +++ b/src/box/mp_error.cc @@ -42,6 +42,7 @@ #include "mp_extension_types.h" #include "fiber.h" #include "ssl_error.h" +#include "trivia/util.h" /** * MP_ERROR format: @@ -207,13 +208,21 @@ static struct error * error_build(struct mp_error *mp_error) { struct error *err = NULL; - if (mp_error->type == NULL || mp_error->message == NULL || - mp_error->file == NULL) { + if (mp_error->type == NULL) { diag_set(ClientError, ER_INVALID_MSGPACK, - "Missing mandatory error fields"); + "MP_ERROR_TYPE is missing"); + return NULL; + } + if (mp_error->message == NULL) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "MP_ERROR_MESSAGE is missing"); + return NULL; + } + if (mp_error->file == NULL) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "MP_ERROR_FILE is missing"); return NULL; } - if (strcmp(mp_error->type, "ClientError") == 0) { err = new ClientError(); } else if (strcmp(mp_error->type, "CustomError") == 0) { @@ -261,44 +270,43 @@ error_build(struct mp_error *mp_error) return err; } -static inline const char * -region_strdup(struct region *region, const char *str, uint32_t len) -{ - char *res = (char *)region_alloc(region, len + 1); - if (res == NULL) { - diag_set(OutOfMemory, len + 1, "region_alloc", "res"); - return NULL; - } - memcpy(res, str, len); - res[len] = 0; - return res; -} - static inline const char * mp_decode_and_copy_str(const char **data, struct region *region) { - if (mp_typeof(**data) != MP_STR) { - diag_set(ClientError, ER_INVALID_MSGPACK, - "Invalid MP_ERROR MsgPack format"); - return NULL; - } uint32_t str_len; const char *str = mp_decode_str(data, &str_len); - return region_strdup(region, str, str_len);; + char *copy = (char *)xregion_alloc(region, str_len + 1); + memcpy(copy, str, str_len); + copy[str_len] = '\0'; + return copy; } +/** + * Helper macro for checking that MsgPack data has a particular type. + * Sets diag and returns -1 on error. + */ +#define CHECK_MP_TYPE(data, type, what) ({ \ + int rc = 0; \ + if (mp_typeof(**(data)) != (type)) { \ + diag_set(ClientError, ER_INVALID_MSGPACK, \ + what " must be " #type); \ + rc = -1; \ + } \ + rc; \ +}) + static int mp_decode_error_fields(const char **data, struct mp_error *mp_err, struct region *region) { - if (mp_typeof(**data) != MP_MAP) + if (CHECK_MP_TYPE(data, MP_MAP, "MP_ERROR_FIELDS value") != 0) return -1; uint32_t map_sz = mp_decode_map(data); for (uint32_t i = 0; i < map_sz; ++i) { uint32_t svp = region_used(region); - const char *key = mp_decode_and_copy_str(data, region); - if (key == NULL) + if (CHECK_MP_TYPE(data, MP_STR, "error payload field name")) return -1; + const char *key = mp_decode_and_copy_str(data, region); const char *value = *data; mp_next(data); uint32_t value_len = *data - value; @@ -318,44 +326,48 @@ mp_decode_error_one(const char **data) struct error *err = NULL; uint32_t map_size; - if (mp_typeof(**data) != MP_MAP) - goto error; - + if (CHECK_MP_TYPE(data, MP_MAP, "MP_ERROR_STACK array entry") != 0) + goto finish; map_size = mp_decode_map(data); for (uint32_t i = 0; i < map_size; ++i) { - if (mp_typeof(**data) != MP_UINT) - goto error; - + if (CHECK_MP_TYPE(data, MP_UINT, "error field key") != 0) + goto finish; uint64_t key = mp_decode_uint(data); switch(key) { case MP_ERROR_TYPE: - mp_err.type = mp_decode_and_copy_str(data, region); - if (mp_err.type == NULL) + if (CHECK_MP_TYPE(data, MP_STR, + "MP_ERROR_TYPE value") != 0) goto finish; + mp_err.type = mp_decode_and_copy_str(data, region); break; case MP_ERROR_FILE: - mp_err.file = mp_decode_and_copy_str(data, region); - if (mp_err.file == NULL) + if (CHECK_MP_TYPE(data, MP_STR, + "MP_ERROR_FILE value") != 0) goto finish; + mp_err.file = mp_decode_and_copy_str(data, region); break; case MP_ERROR_LINE: - if (mp_typeof(**data) != MP_UINT) - goto error; + if (CHECK_MP_TYPE(data, MP_UINT, + "MP_ERROR_LINE value") != 0) + goto finish; mp_err.line = mp_decode_uint(data); break; case MP_ERROR_MESSAGE: - mp_err.message = mp_decode_and_copy_str(data, region); - if (mp_err.message == NULL) + if (CHECK_MP_TYPE(data, MP_STR, + "MP_ERROR_MESSAGE value") != 0) goto finish; + mp_err.message = mp_decode_and_copy_str(data, region); break; case MP_ERROR_ERRNO: - if (mp_typeof(**data) != MP_UINT) - goto error; + if (CHECK_MP_TYPE(data, MP_UINT, + "MP_ERROR_ERRNO value") != 0) + goto finish; mp_err.saved_errno = mp_decode_uint(data); break; case MP_ERROR_CODE: - if (mp_typeof(**data) != MP_UINT) - goto error; + if (CHECK_MP_TYPE(data, MP_UINT, + "MP_ERROR_CODE value") != 0) + goto finish; mp_err.code = mp_decode_uint(data); break; case MP_ERROR_FIELDS: @@ -372,11 +384,6 @@ mp_decode_error_one(const char **data) region_truncate(region, region_svp); mp_error_destroy(&mp_err); return err; - -error: - diag_set(ClientError, ER_INVALID_MSGPACK, - "Invalid MP_ERROR MsgPack format"); - goto finish; } /** @@ -460,28 +467,22 @@ error_to_mpstream(const struct error *error, struct mpstream *stream) struct error * error_unpack_unsafe(const char **data) { + const char *begin = *data; struct error *err = NULL; + uint32_t map_size; - if (mp_typeof(**data) != MP_MAP) { - diag_set(ClientError, ER_INVALID_MSGPACK, - "Invalid MP_ERROR MsgPack format"); - return NULL; - } - uint32_t map_size = mp_decode_map(data); + if (CHECK_MP_TYPE(data, MP_MAP, "error data") != 0) + goto error; + map_size = mp_decode_map(data); for (uint32_t i = 0; i < map_size; ++i) { - if (mp_typeof(**data) != MP_UINT) { - diag_set(ClientError, ER_INVALID_MSGPACK, - "Invalid MP_ERROR MsgPack format"); + if (CHECK_MP_TYPE(data, MP_UINT, "error data key") != 0) goto error; - } uint64_t key = mp_decode_uint(data); switch(key) { case MP_ERROR_STACK: { - if (mp_typeof(**data) != MP_ARRAY) { - diag_set(ClientError, ER_INVALID_MSGPACK, - "Invalid MP_ERROR MsgPack format"); + if (CHECK_MP_TYPE(data, MP_ARRAY, + "MP_ERROR_STACK value") != 0) goto error; - } uint32_t stack_sz = mp_decode_array(data); struct error *effect = NULL; for (uint32_t i = 0; i < stack_sz; i++) { @@ -493,7 +494,6 @@ error_unpack_unsafe(const char **data) effect = cur; continue; } - error_set_prev(effect, cur); effect = cur; } @@ -503,6 +503,11 @@ error_unpack_unsafe(const char **data) mp_next(data); } } + if (err == NULL) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "MP_ERROR_STACK is missing"); + goto error; + } return err; error: @@ -511,6 +516,9 @@ error_unpack_unsafe(const char **data) error_ref(err); error_unref(err); } + err = diag_last_error(diag_get()); + assert(err != NULL); + error_set_uint(err, "offset", *data - begin); return NULL; } @@ -519,7 +527,7 @@ error_unpack(const char **data, uint32_t len) { const char *end = *data + len; const char *check = *data; - if (mp_check(&check, end) != 0 || check != end) + if (mp_check_exact(&check, end) != 0) return NULL; return error_unpack_unsafe(data); } diff --git a/src/box/msgpack.c b/src/box/msgpack.c index d15a6ab45a..59ba761948 100644 --- a/src/box/msgpack.c +++ b/src/box/msgpack.c @@ -39,6 +39,11 @@ #include "mp_interval.h" #include "mp_compression.h" +#include "diag.h" +#include "error.h" +#include "errcode.h" +#include "trivia/util.h" + static int msgpack_fprint_ext(FILE *file, const char **data, int depth) { @@ -95,25 +100,84 @@ msgpack_check_ext_data(int8_t type, const char *data, uint32_t len) { switch (type) { case MP_DECIMAL: - return mp_validate_decimal(data, len); + if (mp_validate_decimal(data, len) != 0) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "cannot unpack decimal"); + return -1; + } + return 0; case MP_UUID: - return mp_validate_uuid(data, len); + if (mp_validate_uuid(data, len) != 0) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "cannot unpack uuid"); + return -1; + } + return 0; case MP_DATETIME: - return mp_validate_datetime(data, len); + if (mp_validate_datetime(data, len) != 0) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "cannot unpack datetime"); + return -1; + } + return 0; case MP_ERROR: - return mp_validate_error(data, len); + if (mp_validate_error(data, len) != 0) { + /* The error is set by error_unpack(). */ + diag_add(ClientError, ER_INVALID_MSGPACK, + "cannot unpack error"); + return -1; + } + return 0; case MP_INTERVAL: - return mp_validate_interval(data, len); + if (mp_validate_interval(data, len) != 0) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "cannot unpack interval"); + return -1; + } + return 0; case MP_COMPRESSION: default: return mp_check_ext_data_default(type, data, len); } } +/** Our handler invoked on mp_check() error. */ +static void +msgpack_check_on_error(const struct mp_check_error *mperr) +{ + struct error *err; + switch (mperr->type) { + case MP_CHECK_ERROR_TRUNC: + err = diag_set(ClientError, ER_INVALID_MSGPACK, + "truncated input"); + error_set_int(err, "trunc_count", mperr->trunc_count); + break; + case MP_CHECK_ERROR_ILL: + err = diag_set(ClientError, ER_INVALID_MSGPACK, + "illegal code"); + break; + case MP_CHECK_ERROR_EXT: + /* The error is set by msgpack_check_ext_data(). */ + err = diag_add(ClientError, ER_INVALID_MSGPACK, + "invalid extension"); + error_set_int(err, "ext_type", mperr->ext_type); + error_set_uint(err, "ext_len", mperr->ext_len); + break; + case MP_CHECK_ERROR_JUNK: + err = diag_set(ClientError, ER_INVALID_MSGPACK, + "junk after input"); + break; + default: + unreachable(); + } + error_set_uint(err, "offset", mperr->pos - mperr->data); +} + void msgpack_init(void) { mp_fprint_ext = msgpack_fprint_ext; mp_snprint_ext = msgpack_snprint_ext; mp_check_ext_data = msgpack_check_ext_data; + mp_check_on_error = msgpack_check_on_error; } diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c index 5868c6a3a9..1191a3b85b 100644 --- a/src/lua/msgpack.c +++ b/src/lua/msgpack.c @@ -581,7 +581,7 @@ lua_msgpack_decode_cdata(lua_State *L, bool check) } const char *p = data; if (mp_check(&p, data + data_len) != 0) - return luaL_error(L, "msgpack.decode: invalid MsgPack"); + return luaT_error(L); } struct luaL_serializer *cfg = luaL_checkserializer(L); luamp_decode(L, cfg, &data); @@ -604,7 +604,7 @@ lua_msgpack_decode_string(lua_State *L, bool check) if (check) { const char *p = data + offset; if (mp_check(&p, data + data_len) != 0) - return luaL_error(L, "msgpack.decode: invalid MsgPack"); + return luaT_error(L); } struct luaL_serializer *cfg = luaL_checkserializer(L); const char *p = data + offset; @@ -772,7 +772,7 @@ luamp_push_with_translation(struct lua_State *L, const char *data, size_t data_len = data_end - data; struct luamp_object *obj = luamp_new_object(L, data_len); memcpy((char *)obj->data, data, data_len); - assert(mp_check(&data, data_end) == 0 && data == data_end); + assert(mp_check_exact(&data, data_end) == 0); obj->translation = translation; } @@ -828,10 +828,8 @@ lua_msgpack_object_from_raw(struct lua_State *L) } const char *p = data; const char *data_end = data + data_len; - if (mp_check(&p, data_end) != 0 || p != data_end) { - return luaL_error(L, "msgpack.object_from_raw: " - "invalid MsgPack"); - } + if (mp_check_exact(&p, data_end) != 0) + return luaT_error(L); struct luamp_object *obj = luamp_new_object(L, data_len); memcpy((char *)obj->data, data, data_len); obj->cfg = luaL_checkserializer(L); diff --git a/test/app-luatest/msgpack_test.lua b/test/app-luatest/msgpack_test.lua index 73d9e79e84..3ac6e44d23 100644 --- a/test/app-luatest/msgpack_test.lua +++ b/test/app-luatest/msgpack_test.lua @@ -2,6 +2,7 @@ local buffer = require('buffer') local console = require('console') local msgpack = require('msgpack') local ffi = require('ffi') +local fun = require('fun') local t = require('luatest') local g = t.group() @@ -124,7 +125,7 @@ g.test_encode_decode_buffer = function() end g.test_invalid_msgpack = function() - local err = "msgpack.decode: invalid MsgPack" + local err = "Invalid MsgPack - truncated input" -- Invalid msgpack. local first_buffer = {1, 2, 3} @@ -138,6 +139,7 @@ g.test_invalid_msgpack = function() buf.rpos, buf:size() - 1) -- 0xc1 cannot be used in a valid MsgPack. + err = "Invalid MsgPack - illegal code" t.assert_error_msg_content_equals(err, msgpack.decode, '\xc1') t.assert_error_msg_content_equals(err, msgpack.decode, '\x91\xc1') t.assert_error_msg_content_equals(err, msgpack.decode, '\x81\xff\xc1') @@ -245,10 +247,10 @@ g.test_object_from_raw = function() -- invalid msgpack local s = msgpack.encode(o) t.assert_error_msg_content_equals( - "msgpack.object_from_raw: invalid MsgPack", + "Invalid MsgPack - truncated input", msgpack.object_from_raw, s:sub(1, -2)) t.assert_error_msg_content_equals( - "msgpack.object_from_raw: invalid MsgPack", + "Invalid MsgPack - junk after input", msgpack.object_from_raw, s .. s) end @@ -546,3 +548,262 @@ g.test_object_autocomplete = function() local r = tabcomplete('mp:') t.assert_equals(r, {'mp:', 'mp:get(', 'mp:decode(', 'mp:iterator('}) end + +local g_error_details_params = { + decode = { + func = msgpack.decode, + exact = false, + }, + decode_cdata = { + func = function(data) + return msgpack.decode(ffi.cast('char *', data), #data) + end, + exact = false, + }, + object_from_raw = { + func = msgpack.object_from_raw, + exact = true, + }, +} + +local g_error_details = t.group('msgpack.error-details', t.helpers.matrix({ + test = fun.totable(fun.map(function(k) return k end, + g_error_details_params)), +})) + +g_error_details.test_error_details = function(cg) + local params = g_error_details_params[cg.params.test] + local function check_error(data, expected) + local errmsg_prefix = 'Invalid MsgPack - ' + local ok, err = pcall(params.func, data) + t.assert_not(ok) + local actual, prev + while err ~= nil do + local v = err:unpack() + v.prev = nil + v.trace = nil + v.base_type = nil + t.assert_equals(v.type, 'ClientError') + v.type = nil + t.assert_equals(v.code, box.error.INVALID_MSGPACK) + v.code = nil + if v.message:startswith(errmsg_prefix) then + v.message = v.message:sub(#errmsg_prefix + 1) + end + if prev == nil then + actual = v + else + prev.prev = v + end + prev = v + err = err.prev + end + t.assert_equals(actual, expected) + end + check_error('\xc1', { + message = 'illegal code', offset = 0, + }) + check_error('', { + message = 'truncated input', offset = 0, trunc_count = 1, + }) + check_error('\x94\xc0', { + message = 'truncated input', offset = 2, trunc_count = 3, + }) + check_error('\x94\xda\x00', { + message = 'truncated input', offset = 1, trunc_count = 4, + }) + if params.exact then + check_error('\xc0\xc0', { + message = 'junk after input', offset = 1, + }) + check_error('\x92\x91\xc0\xc0\xc1', { + message = 'junk after input', offset = 4, + }) + end + check_error('\xc7\x04\x01\xc0\xc0\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 1, + prev = {message = 'cannot unpack decimal'}, + }) + check_error('\xc7\x04\x02\xc0\xc0\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 2, + prev = {message = 'cannot unpack uuid'}, + }) + check_error('\xc7\x04\x04\xc0\xc0\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 4, + prev = {message = 'cannot unpack datetime'}, + }) + check_error('\xc7\x04\x06\xc0\xc0\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 6, + prev = {message = 'cannot unpack interval'}, + }) + check_error('\xc7\x02\x03\x91\xc1', { + message = 'invalid extension', offset = 3, ext_len = 2, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'illegal code', offset = 1}, + }, + }) + check_error('\xc7\x02\x03\x92\xc0', { + message = 'invalid extension', offset = 3, ext_len = 2, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'truncated input', offset = 2, trunc_count = 1}, + }, + }) + check_error('\xc7\x04\x03\x92\xc0\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'junk after input', offset = 3}, + }, + }) + check_error('\xc7\x01\x03\xc0', { + message = 'invalid extension', offset = 3, ext_len = 1, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'error data must be MP_MAP', offset = 0}, + }, + }) + check_error('\xc7\x03\x03\x81\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 3, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'error data key must be MP_UINT', offset = 1}, + }, + }) + check_error('\xc7\x01\x03\x80', { + message = 'invalid extension', offset = 3, ext_len = 1, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'MP_ERROR_STACK is missing', offset = 1}, + }, + }) + check_error('\xc7\x04\x03\x81\x00\x91\x80', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'MP_ERROR_TYPE is missing', offset = 4}, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x00\xa0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'MP_ERROR_MESSAGE is missing', offset = 6}, + }, + }) + check_error('\xc7\x08\x03\x81\x00\x91\x82\x00\xa0\x03\xa0', { + message = 'invalid extension', offset = 3, ext_len = 8, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'MP_ERROR_FILE is missing', offset = 8}, + }, + }) + check_error('\xc7\x03\x03\x81\x00\xc0', { + message = 'invalid extension', offset = 3, ext_len = 3, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_STACK value must be MP_ARRAY', + offset = 2, + }, + }, + }) + check_error('\xc7\x04\x03\x81\x00\x91\xc0', { + message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_STACK array entry must be MP_MAP', + offset = 3, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = {message = 'error field key must be MP_UINT', offset = 4}, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x00\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_TYPE value must be MP_STR', + offset = 5, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x01\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_FILE value must be MP_STR', + offset = 5, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x02\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_LINE value must be MP_UINT', + offset = 5, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x03\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_MESSAGE value must be MP_STR', + offset = 5, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x04\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_ERRNO value must be MP_UINT', + offset = 5, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x05\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_CODE value must be MP_UINT', + offset = 5, + }, + }, + }) + check_error('\xc7\x06\x03\x81\x00\x91\x81\x06\xc0', { + message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'MP_ERROR_FIELDS value must be MP_MAP', + offset = 5, + }, + }, + }) + check_error('\xc7\x08\x03\x81\x00\x91\x81\x06\x81\xc0\xc0', { + message = 'invalid extension', offset = 3, ext_len = 8, ext_type = 3, + prev = { + message = 'cannot unpack error', + prev = { + message = 'error payload field name must be MP_STR', + offset = 6, + }, + }, + }) +end -- GitLab