tarantool / tarantool/tarantool

space:select() is broken when called from __gc

Open
#5,898 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug crash lua
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.