Skip to content

VFS handle lines binding causes address sanitizer error and sometimes a crash

Happens on a6676fd6.

Original crash that triggered the investigation: openmw-crash.1740611677.log

ASAN error
==51775==ERROR: AddressSanitizer: heap-use-after-free on address 0x508000e0ff30 at pc 0x705011d0893e bp 0x7ffc9802d640 sp 0x7ffc9802d630
READ of size 8 at 0x508000e0ff30 thread T0
    #0 0x705011d0893d  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x6293d) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #1 0x705011d0ccad in lua_rawgeti (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x66cad) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #2 0x705011dd4cab in luaL_unref (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x12ecab) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #3 0x5681856e4169 in sol::stateless_reference::deref(lua_State*) const /home/elsid/dev/openmw/extern/sol3/sol/reference.hpp:440
    #4 0x5681856e4169 in sol::basic_reference<false>::deref() const /home/elsid/dev/openmw/extern/sol3/sol/reference.hpp:545
    #5 0x5681856e4169 in sol::basic_reference<false>::~basic_reference() /home/elsid/dev/openmw/extern/sol3/sol/reference.hpp:635
    #6 0x5681856e4169 in sol::basic_object_base<sol::basic_reference<false> >::~basic_object_base() /home/elsid/dev/openmw/extern/sol3/sol/object_base.hpp:33
    #7 0x5681856e4169 in sol::basic_object<sol::basic_reference<false> >::~basic_object() /home/elsid/dev/openmw/extern/sol3/sol/object.hpp:35
    #8 0x5681856e4169 in ~<lambda> /home/elsid/dev/openmw/apps/openmw/mwlua/vfsbindings.cpp:195
    #9 0x5681856e4169 in ~functor_function /home/elsid/dev/openmw/extern/sol3/sol/function_types_stateful.hpp:32
    #10 0x5681856e4169 in destroy_at<sol::function_detail::functor_function<MWLua::initVFSPackage(const Context&)::<lambda(sol::this_state, sol::object)>::<lambda()>, false, true> > /usr/include/c++/14.2.1/bits/stl_construct.h:88
    #11 0x5681856e4169 in destroy<sol::function_detail::functor_function<MWLua::initVFSPackage(const Context&)::<lambda(sol::this_state, sol::object)>::<lambda()>, false, true> > /usr/include/c++/14.2.1/bits/alloc_traits.h:599
    #12 0x5681856e4169 in user_alloc_destroy<sol::function_detail::functor_function<MWLua::initVFSPackage(const Context&)::<lambda(sol::this_state, sol::object)>::<lambda()>, false, true> > /home/elsid/dev/openmw/extern/sol3/sol/stack_core.hpp:460
    #13 0x568188124fc5 in int sol::detail::trampoline<int (*&)(lua_State*)>(lua_State*, int (*&)(lua_State*)) /home/elsid/dev/openmw/extern/sol3/sol/trampoline.hpp:158
    #14 0x56818812555c in sol::detail::c_trampoline(lua_State*, int (*)(lua_State*)) /home/elsid/dev/openmw/extern/sol3/sol/trampoline.hpp:183
    #15 0x705011cdc13a  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x3613a) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #16 0x705011cdeac4  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x38ac4) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #17 0x705011cdf1a2  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x391a2) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #18 0x705011ce1cf2  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x3bcf2) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #19 0x705011ce2a37  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x3ca37) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #20 0x705011d0f4a4 in lua_gc (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x694a4) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #21 0x568184ffd0dc in MWLua::LuaManager::clear() /home/elsid/dev/openmw/apps/openmw/mwlua/luamanagerimp.cpp:348
    #22 0x56818500016a in MWLua::LuaManager::noGame() /home/elsid/dev/openmw/apps/openmw/mwlua/luamanagerimp.cpp:397
    #23 0x568187fce0f0 in MWState::StateManager::cleanup(bool) /home/elsid/dev/openmw/apps/openmw/mwstate/statemanagerimp.cpp:71
    #24 0x568187fd18bc in MWState::StateManager::newGame(bool) /home/elsid/dev/openmw/apps/openmw/mwstate/statemanagerimp.cpp:169
    #25 0x568187fce8ed in MWState::StateManager::update(float) /home/elsid/dev/openmw/apps/openmw/mwstate/statemanagerimp.cpp:760
    #26 0x56818803786b in OMW::Engine::frame(unsigned int, float) /home/elsid/dev/openmw/apps/openmw/engine.cpp:238
    #27 0x56818804afb3 in OMW::Engine::go() /home/elsid/dev/openmw/apps/openmw/engine.cpp:1032
    #28 0x568184142327 in runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:228
    #29 0x56818917c805 in Debug::wrapApplication(int (*)(int, char**), int, char**, std::basic_string_view<char, std::char_traits<char> >) /home/elsid/dev/openmw/components/debug/debugging.cpp:457
    #30 0x568184138615 in main /home/elsid/dev/openmw/apps/openmw/main.cpp:240
    #31 0x70500ec35487  (/usr/lib/libc.so.6+0x27487) (BuildId: 0b707b217b15b106c25fe51df3724b25848310c0)
    #32 0x70500ec3554b in __libc_start_main (/usr/lib/libc.so.6+0x2754b) (BuildId: 0b707b217b15b106c25fe51df3724b25848310c0)
    #33 0x5681841383e4 in _start (/home/elsid/dev/openmw/build/gcc/asan/openmw+0x10db3e4) (BuildId: 32536197bbdd55f44f7745cfd34527d8c57aee27)

0x508000e0ff30 is located 16 bytes inside of 96-byte region [0x508000e0ff20,0x508000e0ff80)
freed by thread T0 here:
    #0 0x7050162fc102 in free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x5681880ff1a7 in LuaUtil::LuaState::trackingAllocator(void*, void*, unsigned long, unsigned long) /home/elsid/dev/openmw/components/lua/luastate.cpp:107
    #2 0x705011cf7779  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x51779) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #3 0x705011cde7f3  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x387f3) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #4 0x705011ce1a9a  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x3ba9a) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #5 0x705011ce2a37  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x3ca37) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #6 0x705011d0f4a4 in lua_gc (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x694a4) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #7 0x568184ffd0dc in MWLua::LuaManager::clear() /home/elsid/dev/openmw/apps/openmw/mwlua/luamanagerimp.cpp:348
    #8 0x56818500016a in MWLua::LuaManager::noGame() /home/elsid/dev/openmw/apps/openmw/mwlua/luamanagerimp.cpp:397
    #9 0x568187fce0f0 in MWState::StateManager::cleanup(bool) /home/elsid/dev/openmw/apps/openmw/mwstate/statemanagerimp.cpp:71
    #10 0x568187fd18bc in MWState::StateManager::newGame(bool) /home/elsid/dev/openmw/apps/openmw/mwstate/statemanagerimp.cpp:169
    #11 0x568187fce8ed in MWState::StateManager::update(float) /home/elsid/dev/openmw/apps/openmw/mwstate/statemanagerimp.cpp:760
    #12 0x56818803786b in OMW::Engine::frame(unsigned int, float) /home/elsid/dev/openmw/apps/openmw/engine.cpp:238
    #13 0x56818804afb3 in OMW::Engine::go() /home/elsid/dev/openmw/apps/openmw/engine.cpp:1032
    #14 0x568184142327 in runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:228
    #15 0x56818917c805 in Debug::wrapApplication(int (*)(int, char**), int, char**, std::basic_string_view<char, std::char_traits<char> >) /home/elsid/dev/openmw/components/debug/debugging.cpp:457
    #16 0x568184138615 in main /home/elsid/dev/openmw/apps/openmw/main.cpp:240
    #17 0x70500ec35487  (/usr/lib/libc.so.6+0x27487) (BuildId: 0b707b217b15b106c25fe51df3724b25848310c0)
    #18 0x70500ec3554b in __libc_start_main (/usr/lib/libc.so.6+0x2754b) (BuildId: 0b707b217b15b106c25fe51df3724b25848310c0)
    #19 0x5681841383e4 in _start (/home/elsid/dev/openmw/build/gcc/asan/openmw+0x10db3e4) (BuildId: 32536197bbdd55f44f7745cfd34527d8c57aee27)

previously allocated by thread T0 here:
    #0 0x7050162fc3c2 in realloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:85
    #1 0x5681880fdd40 in LuaUtil::LuaState::trackingAllocator(void*, void*, unsigned long, unsigned long) /home/elsid/dev/openmw/components/lua/luastate.cpp:110
    #2 0x705011ce2fc8  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x3cfc8) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #3 0x705011cf7476  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x51476) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #4 0x705011d0c456 in lua_newthread (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x66456) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #5 0x705011dd53e5  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x12f3e5) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)
    #6 0x705011cdc0c5  (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x360c5) (BuildId: 1249151684379d19b11900f406fea9704a6375cb)

SUMMARY: AddressSanitizer: heap-use-after-free (/home/elsid/dev/LuaJIT/build/gcc/asan/install/lib/libluajit-5.1.so.2+0x6293d) (BuildId: 1249151684379d19b11900f406fea9704a6375cb) 
Shadow bytes around the buggy address:
  0x508000e0fc80: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
  0x508000e0fd00: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 07
  0x508000e0fd80: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
  0x508000e0fe00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x508000e0fe80: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
=>0x508000e0ff00: fa fa fa fa fd fd[fd]fd fd fd fd fd fd fd fd fd
  0x508000e0ff80: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
  0x508000e10000: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x508000e10080: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
  0x508000e10100: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x508000e10180: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 04 fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==51775==ABORTING

Address sanitizer error can be reproduced with following scripts:

menu.lua:

local menu = require('openmw.menu')
local core = require('openmw.core')

local state = 'init'

return {
    engineHandlers = {
        onFrame = function()
            if state == 'init' then
                menu.newGame()
                state = 'vfs'
                return
            end
            if state == 'vfs' then
                core.sendGlobalEvent('run')
                state = 'wait'
                return
            end
            if state == 'wait' then
                return
            end
            if state == 'newGame' then
                menu.newGame()
                state = 'done'
                return
            end
            if state == 'done' then
                menu.quit()
                return
            end
        end,
    },
    eventHandlers = {
        finished = function()
            state = 'newGame'
        end
    }
}

global.lua:

local vfs = require('openmw.vfs')
local world = require('openmw.world')
local types = require('openmw.types')

local started = false

local function makeCoroutine(fn)
    local co = coroutine.create(fn)
    return function()
        if coroutine.status(co) ~= 'dead' then
            coroutine.resume(co)
        end
    end
end

return {
    engineHandlers = {
        onUpdate = makeCoroutine(function()
            while not started do
                coroutine.yield()
            end
            vfs.open('test_vfs_dir/test.txt'):lines()
            types.Player.sendMenuEvent(world.players[1], 'finished')
        end),
    },
    eventHandlers = {
        run = function()
            started = true
        end
    },
}

The error appears on second menu.newGame call. For some reason it happens with coroutine in the global.lua. Attempt to rewrite it as state machine like menu.lua does not reproduce the problem.

OpenMW and LuaJIT have to be compiled with ASAN. Script to compile LuaJIT (https://github.com/LuaJIT/LuaJIT/commit/a4f56a459a588ae768801074b46ba0adcfb49eb1):

#!/bin/bash -ex

SRC="${PWD}"
BUILD=build/gcc/asan
ASAN_FLAGS='-fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak'
export ASAN_OPTIONS='halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=0'

rm -rf "${BUILD}"
mkdir -p "${BUILD}"
git clone . "${BUILD}"
cd "${BUILD}"

git diff

make \
    -j $(nproc) \
    PREFIX="${SRC}/${BUILD}/install" \
    CCOPT='' \
    CC='ccache gcc' \
    CFLAGS="-g -O1 -fno-omit-frame-pointer -DNDEBUG -march=native ${ASAN_FLAGS}" \
    LDFLAGS="-fuse-ld=mold ${ASAN_FLAGS}" \
    install
Edited by elsid