From bc0daf99b35bff9f6a4afa85b1e7faabcbc68144 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov@tarantool.org> Date: Thu, 6 Jun 2024 10:56:54 +0300 Subject: [PATCH] tuple: fix crash on hashing tuple with double fields `tuple_hash_field()` doesn't advance the MsgPack cursor after hashing a tuple field with the type `double`, which can result in crashes both in memtx (while inserting a tuple into a hash index) and in vinyl (while writing a bloom filter on dump or compaction). The bug was introduced by commit 51af059c10ff ("box: compare and hash msgpack value of double key field as double"). Closes #10090 NO_DOC=bug fix --- .../gh-10090-tuple-hash-double-fix.md | 6 +++ src/box/tuple_hash.cc | 2 +- .../gh_10090_tuple_hash_double_test.lua | 37 ++++++++++++++++++ .../gh_10090_tuple_hash_double_test.lua | 38 +++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/gh-10090-tuple-hash-double-fix.md create mode 100644 test/box-luatest/gh_10090_tuple_hash_double_test.lua create mode 100644 test/vinyl-luatest/gh_10090_tuple_hash_double_test.lua diff --git a/changelogs/unreleased/gh-10090-tuple-hash-double-fix.md b/changelogs/unreleased/gh-10090-tuple-hash-double-fix.md new file mode 100644 index 0000000000..7d4023565a --- /dev/null +++ b/changelogs/unreleased/gh-10090-tuple-hash-double-fix.md @@ -0,0 +1,6 @@ +## bugfix/core + +* Fixed a bug when hashing a tuple with `double` fields could crash. + The bug could trigger a crash in memtx while inserting a tuple into + a `hash` index and in vinyl while writing a bloom filter on dump or + compaction (gh-10090). diff --git a/src/box/tuple_hash.cc b/src/box/tuple_hash.cc index 1c921dda12..4e8e2bac45 100644 --- a/src/box/tuple_hash.cc +++ b/src/box/tuple_hash.cc @@ -313,7 +313,7 @@ tuple_hash_field(uint32_t *ph1, uint32_t *pcarry, const char **field, * This will only fail if the mp_type is not numeric, which is * impossible here (see field_mp_plain_type_is_compatible). */ - if (mp_read_double_lossy(&f, &value) == -1) + if (mp_read_double_lossy(field, &value) == -1) unreachable(); char *double_msgpack_end = mp_encode_double(buf, value); size = double_msgpack_end - buf; diff --git a/test/box-luatest/gh_10090_tuple_hash_double_test.lua b/test/box-luatest/gh_10090_tuple_hash_double_test.lua new file mode 100644 index 0000000000..141587be99 --- /dev/null +++ b/test/box-luatest/gh_10090_tuple_hash_double_test.lua @@ -0,0 +1,37 @@ +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group() + +g.before_all(function(cg) + cg.server = server:new() + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.space.test ~= nil then + box.space.test:drop() + end + end) +end) + +g.test_tuple_hash_double = function(cg) + cg.server:exec(function() + local s = box.schema.create_space('test') + s:create_index('pk', { + type = 'hash', + parts = { + {1, 'double'}, {2, 'string'}, {3, 'double'}, + }, + }) + s:insert{1, 'x', 0.5} + s:insert{0.5, 'y', 1} + s:delete{0.5, 'y', 1} + t.assert_equals(s:select({}, {fullscan = true}), {{1, 'x', 0.5}}) + end) +end diff --git a/test/vinyl-luatest/gh_10090_tuple_hash_double_test.lua b/test/vinyl-luatest/gh_10090_tuple_hash_double_test.lua new file mode 100644 index 0000000000..f75a83864c --- /dev/null +++ b/test/vinyl-luatest/gh_10090_tuple_hash_double_test.lua @@ -0,0 +1,38 @@ +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group() + +g.before_all(function(cg) + cg.server = server:new() + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.space.test ~= nil then + box.space.test:drop() + end + end) +end) + +g.test_tuple_hash_double = function(cg) + cg.server:exec(function() + local s = box.schema.create_space('test', {engine = 'vinyl'}) + s:create_index('pk', { + parts = { + {1, 'double'}, {2, 'string'}, {3, 'double'}, + }, + }) + s:insert{1, 'x', 0.5} + s:insert{0.5, 'y', 1} + box.snapshot() + s:delete{0.5, 'y', 1} + box.snapshot() + t.assert_equals(s:select({}, {fullscan = true}), {{1, 'x', 0.5}}) + end) +end -- GitLab