tarantool / tarantool/tarantool
space:select() is broken when called from __gc
Nobody has claimed this yet.
- Dominant language
- Lua
- Stars
- 3.7k
- Forks
- 419
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 88
Description
Space methods are not safe to be used from any __gc handler. This is because of an issue similar to #5632 - use of global objects with no clear ownership. The test:
ffi = require('ffi')
iter_count = 100000
box.cfg{}
s = box.schema.create_space('test')
s:create_index('pk')
box.begin()
for i = 1, 1000 do
s:replace({i})
end
box.commit()
collectgarbage('collect')
function check_space()
local res_set = s:select()
for i, t in pairs(res_set) do
assert(t[1] == i)
end
end
function create_gc()
return ffi.gc(ffi.new('char[1]'), check_space)
end
for i = 1, iter_count do
create_gc()
check_space()
end
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007fff6ed3533a libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff6edf1e60 libsystem_pthread.dylib`pthread_kill + 430
frame #2: 0x00007fff6ecbc808 libsystem_c.dylib`abort + 120
frame #3: 0x00007fff6edb250b libsystem_malloc.dylib`malloc_vreport + 548
frame #4: 0x00007fff6edb540f libsystem_malloc.dylib`malloc_report + 151
frame #5: 0x000000010010aad0 tarantool`port_c_destroy_entry(pe=0x0000000003045e88) at port.c:62:3
frame #6: 0x000000010010a58b tarantool`port_c_destroy(base=0x000000000255abf0) at port.c:82:3
frame #7: 0x000000010028f79d tarantool`port_destroy(port=0x000000000255abf0) at port.c:36:2
frame #8: 0x00000001002b0b9c tarantool`lj_vm_ffi_call + 132
frame #9: 0x000000010034edc6 tarantool`lj_ccall_func(L=0x00000000022c8378, cd=0x0000000004b7e860) at lj_ccall.c:1150:5
frame #10: 0x00000001002e7656 tarantool`lj_cf_ffi_meta___call(L=0x00000000022c8378) at lib_ffi.c:230:15
frame #11: 0x00000001002ae4a3 tarantool`lj_BC_FUNCC + 68
frame #12: 0x00000001002b6866 tarantool`lua_pcall(L=0x00000000022c8378, nargs=0, nresults=0, errfunc=0) at lj_api.c:1158:12
frame #13: 0x0000000100241de3 tarantool`luaT_call(L=0x00000000022c8378, nargs=0, nreturns=0) at utils.c:1022:6
frame #14: 0x00000001002389dc tarantool`lua_main(L=0x00000000022c8378, argc=1, argv=0x0000000002305390) at init.c:556:11
frame #15: 0x00000001002386a5 tarantool`run_script_f(ap=0x0000000003000448) at init.c:657:7
frame #16: 0x00000001000058fa tarantool`fiber_cxx_invoke(f=(tarantool`run_script_f at init.c:567), ap=0x0000000003000448)(__va_list_tag*), __va_list_tag*) at fiber.h:882:10
frame #17: 0x0000000100265b97 tarantool`fiber_loop(data=0x0000000000000000) at fiber.c:879:18
frame #18: 0x000000010058e9f7 tarantool`coro_init at coro.c:110:3
Select fails because it uses a global object port_c in schema.lua file. The same might happen to any other space method using anything global. For instance, space:get(). It may return a tuple not matching the key. The test:
ffi = require('ffi')
iter_count = 100000
box.cfg{}
s = box.schema.create_space('test')
s:create_index('pk')
box.begin()
for i = 1, 1000 do
s:replace({i})
end
box.commit()
collectgarbage('collect')
key = 1
mismatch_tuple = nil
mismatch_key = nil
function check_space()
local loc_key = key % 1000 + 1
key = key + 1
local t = s:get({loc_key})
assert(t)
if t[1] ~= loc_key then
mismatch_tuple = t
mismatch_key = loc_key
assert(false)
end
end
function create_gc()
for i = 1, 100 do
ffi.gc(ffi.new('char[1]'), check_space)
end
end
for i = 1, iter_count do
create_gc()
check_space()
end
It fails on my machine in 100% cases. Example of the mismatched values: mismatch_tuple = [983], mismatch_key = 568, which is clearly wrong. The issue exists because there is a global object ptuple in schema.lua. The same can be said about box.error(), box.error.last() and all the other functions using the global diagnostics area. The same for errno Lua module, and so on for all the globals we have either in Lua, or in C but exposed to Lua.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the Lua reproducer from the issue, then read the global objects in schema.lua and the cleanup path shown in port.c. Trace the space methods and diagnostic globals used from __gc handlers. Done means these calls no longer crash or return mismatched data when garbage-collection handlers run.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, lua
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100