Skip to content
Snippets Groups Projects
Commit 5b47124a authored by Serge Petrenko's avatar Serge Petrenko Committed by Vladimir Davydov
Browse files

box: introduce helpers to check extension type validity

Introduce helpers for each of our custom extension types.
The helpers will be used by mp_check() to validate extension contents
and make sure no malformed data is accepted by tarantool.

Closes #6857

NO_DOC=no user visible changes
parent 5893d61d
No related branches found
No related tags found
No related merge requests found
## bugfix/core
* Improved incoming tuple validation. Now tuples coming over the net can't
contain malformed decimals, uuids, datetime values (gh-6857).
......@@ -514,6 +514,21 @@ error_unpack_unsafe(const char **data)
return err;
}
int
mp_validate_error(const char *data, uint32_t len)
{
const char *end = data + len;
struct error *err = error_unpack_unsafe(&data);
if (err != NULL) {
/* A hack to delete the error. */
error_ref(err);
error_unref(err);
return data != end;
} else {
return 1;
}
}
/**
* Include this file into self with a few template parameters
* to create mp_snprint_error() and mp_fprint_error() functions
......
......@@ -115,6 +115,16 @@ mp_snprint_error(char *buf, int size, const char **data, int depth);
int
mp_fprint_error(FILE *file, const char **data, int depth);
/**
* Check that the buffer contains a valid packed error representation.
* @param data MessagePack encoded error, without MP_EXT header.
* @param len Length of @a data.
* @retval 1 Error. The buffer doesn't contain a parseable error.
* @retval 0 Ok.
*/
int
mp_validate_error(const char *data, uint32_t len);
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */
......@@ -89,9 +89,31 @@ msgpack_snprint_ext(char *buf, int size, const char **data, int depth)
}
}
/** Our handler to validate MP_EXT contents. */
static int
msgpack_check_ext_data(int8_t type, const char *data, uint32_t len)
{
switch (type) {
case MP_DECIMAL:
return mp_validate_decimal(data, len);
case MP_UUID:
return mp_validate_uuid(data, len);
case MP_DATETIME:
return mp_validate_datetime(data, len);
case MP_ERROR:
return mp_validate_error(data, len);
case MP_INTERVAL:
return mp_validate_interval(data, len);
case MP_COMPRESSION:
default:
return mp_check_ext_data_default(type, data, len);
}
}
void
msgpack_init(void)
{
mp_fprint_ext = msgpack_fprint_ext;
mp_snprint_ext = msgpack_snprint_ext;
mp_check_ext_data = msgpack_check_ext_data;
}
......@@ -59,6 +59,17 @@ mp_snprint_datetime(char *buf, int size, const char **data, uint32_t len);
int
mp_fprint_datetime(FILE *file, const char **data, uint32_t len);
/**
* Check that the buffer contains a valid packed datetime.
* @sa mp_validate_decimal
*/
static inline int
mp_validate_datetime(const char *data, uint32_t len)
{
struct datetime date;
return datetime_unpack(&data, len, &date) == NULL;
}
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */
......@@ -90,6 +90,21 @@ mp_snprint_decimal(char *buf, int size, const char **data, uint32_t len);
int
mp_fprint_decimal(FILE *file, const char **data, uint32_t len);
/**
* Check that the given buffer contains a valid decimal.
* @param data The buffer containing a packed decimal representation, without
* MP_EXT header.
* @param len Length of @a data.
* @retval 1 Couldn't decode a decimal.
* @retval 0 Ok.
*/
static inline int
mp_validate_decimal(const char *data, uint32_t len)
{
decimal_t d;
return decimal_unpack(&data, len, &d) == NULL;
}
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */
......
......@@ -186,3 +186,12 @@ mp_fprint_interval(FILE *file, const char **data)
interval_to_string(&itv, buf, TT_STATIC_BUF_LEN);
return fprintf(file, "%s", buf);
}
int
mp_validate_interval(const char *data, uint32_t len)
{
struct interval itv;
const char * const svp = data;
struct interval *rc = interval_unpack(&data, &itv);
return rc == NULL || (data - svp) != len;
}
......@@ -76,6 +76,16 @@ mp_snprint_interval(char *buf, int size, const char **data);
int
mp_fprint_interval(FILE *file, const char **data);
/**
* Check that the given buffer contains a valid interval.
* @param data The buffer containing a packed interval without MP_EXT header.
* @param len Length of @a data.
* @retval 1 Couldn't decode the interval.
* @retval 0 Ok.
*/
int
mp_validate_interval(const char *data, uint32_t len);
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */
......@@ -112,6 +112,20 @@ mp_snprint_uuid(char *buf, int size, const char **data, uint32_t len);
int
mp_fprint_uuid(FILE *file, const char **data, uint32_t len);
/**
* Check that the buffer contains a valid packed uuid.
* @param data The buffer containing a packed uuid, without MP_EXT header.
* @param len Length of @a data.
* @retval 1 Couldn't decode the uuid.
* @retval 0 Ok.
*/
static inline int
mp_validate_uuid(const char *data, uint32_t len)
{
struct tt_uuid uuid;
return uuid_unpack(&data, len, &uuid) == NULL;
}
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */
Subproject commit 234f305a51a3ee6fe24150e8f43c984e1c68c8bf
Subproject commit 0c6680a300e31714f475a7f90c2d95a02d001d80
local t = require('luatest')
local cluster = require('test.luatest_helpers.cluster')
local server = require('test.luatest_helpers.server')
local g = t.group('gh-6857-tuple-ext-validation')
local net_box = require('net.box')
local msgpack = require('msgpack')
local fiber = require('fiber')
local decimal = require('decimal')
local uuid = require('uuid')
local datetime = require('datetime')
g.before_all = function()
g.cluster = cluster:new({})
local box_cfg = {
log_level = 6,
}
g.server = g.cluster:build_and_add_server({
alias='default',
box_cfg = box_cfg,
})
g.cluster:start()
g.server:eval('function test(val) return true end')
end
g.after_all = function()
g.cluster:drop()
end
-- Iproto keys to encode custom call request.
local IPROTO_REQUEST_TYPE = 0x00
local IPROTO_SYNC = 0x01
local IPROTO_TUPLE = 0x21
local IPROTO_FUNCTION_NAME = 0x22
local IPROTO_CALL = 0x0a
local function inject_call(conn, raw_val)
local header = msgpack.encode({
[IPROTO_REQUEST_TYPE] = IPROTO_CALL,
[IPROTO_SYNC] = conn:_next_sync(),
})
local body = msgpack.encode({
[IPROTO_FUNCTION_NAME] = 'test',
-- Reserve a slot for raw_val.
[IPROTO_TUPLE] = {box.NULL},
})
-- Replace the nil placeholder with val.
body = string.sub(body, 1, -2)..raw_val
local size = msgpack.encode(#header + #body)
local request = size..header..body
return conn:_inject(request)
end
-- Check extension types in tuple handling: previously mp_check() simply
-- iterated over tuple field headers without looking into the field contents.
-- This was ok for plain types, like MP_STR, MP_INT and others, because any
-- msgpack encoded string of any of these types represents a valid value.
-- This isn't true for extension types, however. Types like decimal, uuid and
-- others have complex internal structure, which requires additional validation.
g.test_extension_tuple_validation = function()
local correct_data = {
['decimal'] = msgpack.encode(decimal.new(fiber.time())),
['uuid'] = msgpack.encode(uuid.new()),
['error'] =
msgpack.encode(box.error.new(box.error.UNSUPPORTED, 'a', 'b')),
['datetime'] = msgpack.encode(datetime.now()),
['interval'] = msgpack.encode(datetime.now() - datetime.new()),
}
local test_data = {
['decimal'] = string.fromhex('d5010010'), -- bad last nibble 0x0
['uuid'] = string.fromhex('d702c93f2d79bca74235'), -- bad length
['error'] = string.fromhex('c708038100918600ab436c'), -- bad length
['datetime'] = string.fromhex('c7070400000000000000'), -- bad length
['interval'] = string.fromhex('c7050602060fff01'), -- bad key 0xff
}
local c = net_box.connect(server.build_instance_uri('default'))
t.assert_equals(c.state, 'active', 'Connection established')
-- First check that there are no complaints on correctly-encoded ext types.
for name, val in pairs(correct_data) do
local ok = pcall(inject_call, c, val)
t.assert_equals(ok, true, ('No error with %s argument'):format(name))
end
-- Now check that badly encoded ext types are detected.
for name, val in pairs(test_data) do
local ok, err = pcall(inject_call, c, val)
t.assert(not ok, 'Error discovered')
t.assert_equals(err.message, 'Invalid MsgPack - packet body',
('Malformed %s argument discovered'):format(name))
end
c:close()
end
1..23
1..24
1..135
# *** test_uints ***
# uint 0U
......@@ -2181,6 +2181,34 @@ ok 20 - subtests
ok 65 - invalid map32 3
# *** test_mp_check: done ***
ok 21 - subtests
1..24
# *** test_mp_check_ext_data ***
ok 1 - invalid ext8 - bad type
ok 2 - invalid ext8 - bad data
ok 3 - valid ext8
ok 4 - invalid ext16 - bad type
ok 5 - invalid ext16 - bad data
ok 6 - valid ext16
ok 7 - invalid ext32 - bad type
ok 8 - invalid ext32 - bad data
ok 9 - valid ext32
ok 10 - invalid fixext8 - bad type
ok 11 - invalid fixext8 - bad data
ok 12 - valid fixext8
ok 13 - invalid fixext16 - bad type
ok 14 - invalid fixext16 - bad data
ok 15 - valid fixext16
ok 16 - invalid fixext32 - bad type
ok 17 - invalid fixext32 - bad data
ok 18 - valid fixext32
ok 19 - invalid fixext64 - bad type
ok 20 - invalid fixext64 - bad data
ok 21 - valid fixext64
ok 22 - invalid fixext128 - bad type
ok 23 - invalid fixext128 - bad data
ok 24 - valid fixext128
# *** test_mp_check_ext_data: done ***
ok 22 - subtests
1..96
# *** test_numbers ***
ok 1 - mp_read_int32(mp_encode_uint(123)) check success
......@@ -2280,7 +2308,7 @@ ok 21 - subtests
ok 95 - mp_read_double(mp_encode_strl(100)) check fail
ok 96 - mp_read_double(mp_encode_strl(100)) check pos unchanged
# *** test_numbers: done ***
ok 22 - subtests
ok 23 - subtests
1..5
# *** test_overflow ***
ok 1 - mp_check array overflow
......@@ -2289,4 +2317,4 @@ ok 22 - subtests
ok 4 - mp_check bin overflow
ok 5 - mp_check str overflow
# *** test_overflow: done ***
ok 23 - subtests
ok 24 - subtests
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment