From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id D3156EBCBA4; Wed, 29 Jan 2025 12:48:17 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org D3156EBCBA4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1738144098; bh=rZApEAunbtQQi9wq4lGhljdlM6KIeKcRUjxAZUEzce0=; h=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=xci4trDcJ1TWtGcKVtTFqg8fg3dZA+/35KfT6RMgbQZSdO3uQ+wf1pQre0SqvTRHG +wuNWRKFQb6wnz8SdzRhM40LNIrxtjHVlAhDZ/w0Udhw4e6QLqOdyFyPAlE64/z8Gn nHzH5h6+fRo3Hiq5KsONLx6D7I70w6/iJ1pEqbIM= Received: from mail-lf1-f49.google.com (mail-lf1-f49.google.com [209.85.167.49]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 279F0EBCBA4 for ; Wed, 29 Jan 2025 12:48:17 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 279F0EBCBA4 Received: by mail-lf1-f49.google.com with SMTP id 2adb3069b0e04-54298ec925bso7674929e87.3 for ; Wed, 29 Jan 2025 01:48:17 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738144096; x=1738748896; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=oXvhGqfBPFhhtKCMN1ONfa9Bd9lqlN+LiTpalmkLnw0=; b=ZHJRT8HVa+6Zv5xavVk3VtODAEDCuzwMR/Y3YPbPGRg9TtYeLytCVGkf8ldlkNPW+v L8EXhZFaR5X68mHHcHRUw0jGg0xpgRd2f6opekOZSXDvuxrNst//nf8T34UZlyEt8Fse 6G4k0plCbDl59miNtfX5SVOBQF7okDiTDOJNBRiCwdnWmi/W1smbjVcfdTBbrp7QS+Rn DVcBYB9M4ief0lNUvI0YRRcp++5Vyd/WwpRmr6Ws8p7CEV7a2bTPZTFxHvzAW0IAEM2t yH3W+8JNBNh134t3iNu8J0KKwqC6AN6tKFidw7MsarPFc828uhQlLvBKgNSsSWkzlrHq h1iA== X-Gm-Message-State: AOJu0YxAXGTmwT+ynQUNuwFQxY7hg0FXqo3IlG73oUdadtzWwZnI6BAO nJPJAfWewndxma54arxqIZEAeVIC8qtj7I2fLSOY9fnz8nmLE1NktXS+eg== X-Gm-Gg: ASbGncueRFU+sdxY1qb6IJU+Pi8H6XCEubRG6z7bk/9BsqIiXZ8bkJZdmE00sEVfmyG /uvQSGgQ0vH9KdEIsQy4flz80B7UpKMNaAvHKrQtV7smrTh/lbdsOB1gxKg1ux4hfF38dmWcHrn uvYx0w7j9x2uDlBEF1q91+DzwNCLrVBE0kM667z5ggN77DtLkkl/S7rM1cS/ygE0dYljCqgqa33 UByBpVcijeRBzNnEaCdG1PuK1d1WKte8WmZmpiHSWzhmVyb3U08agFO5R/qGZj0cs7fm0MBiC0D JBQFP/gt4GH5+VYX X-Google-Smtp-Source: AGHT+IFkMPVpJezVsytxpAY3ASH45+eXg/2244/eAJxHGKo8UYcDkGcQ0LPA/TVidgpDzVepUqSK/w== X-Received: by 2002:a05:6512:3b07:b0:540:1fd6:4e4f with SMTP id 2adb3069b0e04-543e4beb22dmr714780e87.22.1738144095887; Wed, 29 Jan 2025 01:48:15 -0800 (PST) Received: from localhost ([5.181.62.98]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-543e4b36203sm207260e87.146.2025.01.29.01.48.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jan 2025 01:48:14 -0800 (PST) X-Google-Original-From: Sergey Bronnikov To: tarantool-patches@dev.tarantool.org, Sergey Kaplun Date: Wed, 29 Jan 2025 12:46:34 +0300 Message-Id: X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH luajit] Handle all stack layouts in (delayed) TRACE vmevent. X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Bronnikov via Tarantool-patches Reply-To: Sergey Bronnikov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Mike Pall Thanks to Sergey Bronnikov and Peter Cawley. (cherry picked from commit b138ccfa918518a152bc830fef3d53cd0a922e36) When recording a trace using a Lua code like `repeat until `a >= 'b' > 'b'` a Lua error is encountered (`attempt to compare string with nil`), which (along with raising the error) causes an asynchronous trace abort. The trace abort remains pending until the call of `lua_pcall(L, 2, 0, 0)` that actually calls `jit.attach(trace_cb, nil)`. On handling abort LuaJIT is searching for the topmost Lua frame on the stack, that was active when the trace abort happened, it is needed to generate a better error message. Unfortunately, because the abort was due to an error, and the error was caught by a `lua_pcall` with unspecified error function (4th argument), the Lua frame that caused the abort was already removed as part of error processing, so the search cannot find it. Furthermore, in this particular case, there are no Lua frames on the stack, which isn't something the search code had considered possible. Sergey Bronnikov: * added the description and the test for the problem Part of tarantool/tarantool#10709 --- Branch: https://github.com/tarantool/luajit/tree/ligurio/lj-1087-vm-handler-call Related issues: - https://github.com/LuaJIT/LuaJIT/issues/1087 - https://github.com/tarantool/tarantool/issues/10709 src/lj_trace.c | 26 +-- test/tarantool-c-tests/CMakeLists.txt | 4 + .../lj-1087-vm-handler-call.test.c | 170 ++++++++++++++++++ 3 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 test/tarantool-c-tests/lj-1087-vm-handler-call.test.c diff --git a/src/lj_trace.c b/src/lj_trace.c index 94cb27e5..6b97cc13 100644 --- a/src/lj_trace.c +++ b/src/lj_trace.c @@ -616,21 +616,27 @@ static int trace_abort(jit_State *J) J->cur.link = 0; J->cur.linktype = LJ_TRLINK_NONE; lj_vmevent_send(L, TRACE, - TValue *frame; + cTValue *bot = tvref(L->stack)+LJ_FR2; + cTValue *frame; const BCIns *pc; - GCfunc *fn; + BCPos pos = 0; setstrV(L, L->top++, lj_str_newlit(L, "abort")); setintV(L->top++, traceno); /* Find original Lua function call to generate a better error message. */ - frame = J->L->base-1; - pc = J->pc; - while (!isluafunc(frame_func(frame))) { - pc = (frame_iscont(frame) ? frame_contpc(frame) : frame_pc(frame)) - 1; - frame = frame_prev(frame); + for (frame = J->L->base-1, pc = J->pc; ; frame = frame_prev(frame)) { + if (isluafunc(frame_func(frame))) { + pos = proto_bcpos(funcproto(frame_func(frame)), pc); + break; + } else if (frame_prev(frame) <= bot) { + break; + } else if (frame_iscont(frame)) { + pc = frame_contpc(frame) - 1; + } else { + pc = frame_pc(frame) - 1; + } } - fn = frame_func(frame); - setfuncV(L, L->top++, fn); - setintV(L->top++, proto_bcpos(funcproto(fn), pc)); + setfuncV(L, L->top++, frame_func(frame)); + setintV(L->top++, pos); copyTV(L, L->top++, restorestack(L, errobj)); copyTV(L, L->top++, &J->errinfo); ); diff --git a/test/tarantool-c-tests/CMakeLists.txt b/test/tarantool-c-tests/CMakeLists.txt index c4a402d0..0e7c4698 100644 --- a/test/tarantool-c-tests/CMakeLists.txt +++ b/test/tarantool-c-tests/CMakeLists.txt @@ -36,6 +36,10 @@ add_test_suite_target(tarantool-c-tests DEPENDS libluajit libtest tarantool-c-tests-build ) +if(NOT LUAJIT_DISABLE_JIT) + AppendFlags(TESTS_C_FLAGS "-DLJ_HASJIT") +endif(NOT LUAJIT_DISABLE_JIT) + set(CTEST_SRC_SUFFIX ".test.c") file(GLOB tests "${CMAKE_CURRENT_SOURCE_DIR}/*${CTEST_SRC_SUFFIX}") foreach(test_source ${tests}) diff --git a/test/tarantool-c-tests/lj-1087-vm-handler-call.test.c b/test/tarantool-c-tests/lj-1087-vm-handler-call.test.c new file mode 100644 index 00000000..b7db9857 --- /dev/null +++ b/test/tarantool-c-tests/lj-1087-vm-handler-call.test.c @@ -0,0 +1,170 @@ +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "test.h" +#include "utils.h" + +/* + * Test file to demonstrate a segmentation fault under + * AddressSanitizer, when C function is used as a VM handler + * in LuaJIT: + * + * Program received signal SIGSEGV, Segmentation fault. + * 0x000055555557e77d in trace_abort (J=0x7ffff7f9b6b8) at lj_trace.c:615 + * 615 lj_vmevent_send(L, TRACE, + * (gdb) bt + * + * See details in https://github.com/LuaJIT/LuaJIT/issues/1087. + */ + +#define UNUSED(x) ((void)(x)) + +static void jit_attach(lua_State *L, void *cb, const char *event) +{ + lua_getglobal(L, "jit"); + lua_getfield(L, -1, "attach"); + lua_pushcfunction(L, (lua_CFunction)cb); + if (event != NULL) { + lua_pushstring(L, event); + } else { + lua_pushnil(L); + } + lua_pcall(L, 2, 0, 0); +} + +static int trace_cb(lua_State *L) { + UNUSED(L); + return 0; +} + +static int handle_luafunc_frame(void *test_state) +{ + /* Setup. */ + lua_State *L = test_state; + jit_attach(L, (void *)trace_cb, "trace"); + + /* Loading and executing of a broken Lua code. */ + int rc = luaL_dostring(L, "repeat until nil > 1"); + assert(rc == 1); + + /* The Lua chunk generates a Lua frame. */ + rc = luaL_dostring(L, "return function() end"); + assert(rc == 0); + + /* Teardown. */ + lua_settop(L, 0); + + return TEST_EXIT_SUCCESS; +} + +static int nop(lua_State *L) +{ + return 0; +} + +static int cframe(lua_State *L) +{ + int rc = luaL_dostring(L, "repeat until nil > 1"); + assert(rc == 1); + lua_pop(L, 1); /* Remove errmsg. */ + + lua_pushcfunction(L, nop); + lua_call(L, 0, 0); + + return 0; +} + +static int handle_c_frame(void *test_state) +{ + /* Setup. */ + lua_State *L = test_state; + jit_attach(L, (void *)trace_cb, "trace"); + + lua_pushcfunction(L, cframe); + lua_call(L, 0, 0); + + /* Teardown. */ + lua_settop(L, 0); + + return TEST_EXIT_SUCCESS; +} + +static int global_f(lua_State* L) +{ + lua_pushstring(L, "М"); + lua_pushstring(L, "И"); + lua_pushstring(L, "Р"); + lua_concat(L, 3); + + return 1; +} + +static int handle_cont_frame(void *test_state) +{ + const char lua_chunk[] = + "local t = setmetatable({}, { __index = global_f })" + "for i = 1, 4 do" + " _ = t[1]" + "end"; + + /* Setup. */ + lua_State *L = test_state; + jit_attach(L, (void *)trace_cb, "trace"); + int res = luaL_dostring(L, "jit.opt.start('minstitch=16')"); + assert(res == 0); + lua_pushcfunction(L, global_f); + lua_setglobal(L, "global_f"); + + res = luaL_loadstring(L, lua_chunk); + assert(res == 0); + lua_pcall(L, 0, LUA_MULTRET, 0); + + /* Teardown. */ + lua_settop(L, 0); + + return TEST_EXIT_SUCCESS; +} + +static int handle_bottom_frame(void *test_state) +{ + lua_State *L = test_state; + + /* Attach VM call handler. */ + jit_attach(L, (void *)trace_cb, "trace"); + + /* Load a Lua code that generate a trace abort. */ + int rc = luaL_dostring(L, "repeat until nil > 1"); + assert(rc == 1); + + /* Triggers segmentation fault. */ + jit_attach(L, (void *)trace_cb, NULL); + + /* Teardown. */ + lua_settop(L, 0); + + return TEST_EXIT_SUCCESS; +} + +int main(void) +{ +#if !defined(LJ_HASJIT) + return skip_all("JIT is disabled"); +#else /* LJ_HASJIT */ + lua_State *L = utils_lua_init(); + const struct test_unit tgroup[] = { + test_unit_def(handle_luafunc_frame), + test_unit_def(handle_bottom_frame), + test_unit_def(handle_cont_frame), + test_unit_def(handle_c_frame), + }; + luaL_openlibs(L); + int res = luaL_dostring(L, "jit.opt.start('hotloop=1')"); + assert(res == 0); + const int test_result = test_run_group(tgroup, L); + utils_lua_close(L); + return test_result; +#endif /* LJ_HASJIT */ +} -- 2.34.1