From 822aedfed2ed8c4b80d466c97ff02ceca299c556 Mon Sep 17 00:00:00 2001 From: Alexander Turenko <alexander.turenko@tarantool.org> Date: Fri, 31 May 2024 12:43:38 +0300 Subject: [PATCH] odict: fix ffi.new('void *') as a key The problem is found by @ochaton. NO_DOC=bugfix NO_CHANGELOG=not a public API --- src/box/lua/config/utils/odict.lua | 8 +++---- test/config-luatest/odict_test.lua | 36 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/box/lua/config/utils/odict.lua b/src/box/lua/config/utils/odict.lua index ba973f7f4c..bb92e89e87 100644 --- a/src/box/lua/config/utils/odict.lua +++ b/src/box/lua/config/utils/odict.lua @@ -65,7 +65,7 @@ local function gen(od, prev_key) -- The previous key is nil only on the first call of the -- generator function. - local id = prev_key == nil and 0 or ctx.key2id[prev_key] + local id = type(prev_key) == 'nil' and 0 or ctx.key2id[prev_key] -- NB: This assert doesn't catch all the kinds of changes -- during an iteration. It rather verifies a precondition -- for the following cycle. @@ -80,7 +80,7 @@ local function gen(od, prev_key) -- id2key may contain a stalled entry, because __newindex -- is not called on assignment of an existing field, -- including assignment to nil. - if key ~= nil and od[key] ~= nil then + if type(key) ~= 'nil' and od[key] ~= nil then return key, od[key] end end @@ -148,11 +148,11 @@ local function reindex(od, ctx) for id = 1, old_max_id do local key = ctx.id2key[id] -- Drop the given key-id pair from the key<->id mappings. - if key ~= nil then + if type(key) ~= 'nil' then release(ctx, key) end -- Add the new key-id pair into the key<->id mappings. - if key ~= nil and od[key] ~= nil then + if type(key) ~= 'nil' and od[key] ~= nil then track(ctx, key) end end diff --git a/test/config-luatest/odict_test.lua b/test/config-luatest/odict_test.lua index 0ddd52dcd9..a66b943d3b 100644 --- a/test/config-luatest/odict_test.lua +++ b/test/config-luatest/odict_test.lua @@ -1,4 +1,5 @@ local fun = require('fun') +local ffi = require('ffi') local odict = require('internal.config.utils.odict') local t = require('luatest') @@ -117,6 +118,41 @@ g.test_pairs_delete_and_set = function() }) end +-- Using cdata as a key is unusual, but it is legal in LuaJIT. +-- +-- There is a potential pitfall: ffi.new('void *') == nil. Let's +-- verify that we handle such a key correctly. +g.test_null_as_key = function() + local od = odict.new() + + local nulls = { + ffi.new('void *'), + ffi.new('void *'), + ffi.new('void *'), + } + + od[nulls[1]] = 1 + od[nulls[2]] = 2 + od[nulls[3]] = 3 + + local res = {} + for k, v in odict.pairs(od) do + table.insert(res, {k, v}) + end + + -- It doesn't differentiate nulls. + t.assert_equals(res, { + {nulls[1], 1}, + {nulls[2], 2}, + {nulls[3], 3}, + }) + + -- It does. + t.assert(rawequal(res[1][1], nulls[1])) + t.assert(rawequal(res[2][1], nulls[2])) + t.assert(rawequal(res[3][1], nulls[3])) +end + -- {{{ Helpers for reindexing test cases -- Parse a range expressed like Python's slice operator. -- GitLab