From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <tarantool-patches-bounces@dev.tarantool.org> Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id AFCFB1270B27; Mon, 10 Mar 2025 17:52:39 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org AFCFB1270B27 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1741618359; bh=zDiKdGhsZ2vouDwXlKVoo4MeQ7P7f1x8WbyqqUG3D9k=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Nj5/SE1bkyiDb9whJSzR8v0ZIn3Bf7gqOUmY83BJGn13iBGmL/MBk1pnF9XWk/W9v L1B9/Ixh+4OkT50/pQZAkAkdZjR3PwgglzOPs597W1ePGPM6Xk7P8DhcWTImkZXE2p iD9zp+0P1NtRpzoopeISY6dQr5TmN0raBTzgz4sY= Received: from send194.i.mail.ru (send194.i.mail.ru [95.163.59.33]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 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 788A31270B11 for <tarantool-patches@dev.tarantool.org>; Mon, 10 Mar 2025 17:51:40 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 788A31270B11 Received: by exim-smtp-8cb569c79-b7mkz with esmtpa (envelope-from <skaplun@tarantool.org>) id 1treTj-00000000Jsz-2eh2; Mon, 10 Mar 2025 17:51:40 +0300 To: Sergey Bronnikov <sergeyb@tarantool.org> Date: Mon, 10 Mar 2025 17:51:36 +0300 Message-ID: <9dbb7d455c1953fcab6a82944b073348c17e46ac.1741617766.git.skaplun@tarantool.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <cover.1741617766.git.skaplun@tarantool.org> References: <cover.1741617766.git.skaplun@tarantool.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD9C6CD12EFD8DA450FE5F94F59E023066E5B155EFC5C897E0F00894C459B0CD1B988977EBCB090AFE78E7FD94675F3356575FFA5A87B9AA27B53C58652A57A77A2785316FF18B29525 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7CE54A8686262D0D1EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637AC83A81C8FD4AD23D82A6BABE6F325AC2E85FA5F3EDFCBAA7353EFBB553375666DAE1D1A19A1A03E731B7EF0D9A804C1163AB99D7AE6C33ADE13C47C491026A2389733CBF5DBD5E913377AFFFEAFD269176DF2183F8FC7C0AF05157F0BAFB9978941B15DA834481FCF19DD082D7633A0EF3E4896CB9E6436389733CBF5DBD5E9D5E8D9A59859A8B68CC5112E3E56BCDBCC7F00164DA146DA6F5DAA56C3B73B237318B6A418E8EAB8D32BA5DBAC0009BE9E8FC8737B5C22490F250D17497FEF6176E601842F6C81A12EF20D2F80756B5FB606B96278B59C4276E601842F6C81A127C277FBC8AE2E8B8DBB596EC94336063AA81AA40904B5D99C9F4D5AE37F343AD1F44FA8B9022EA23BBE47FD9DD3FB595F5C1EE8F4F765FC72CEEB2601E22B093A03B725D353964B0B7D0EA88DDEDAC722CA9DD8327EE4930A3850AC1BE2E7356C9A9530EBF72002C4224003CC83647689D4C264860C145E X-C1DE0DAB: 0D63561A33F958A54AB6BE7040D53B2B5002B1117B3ED6967F29E241A038DAD9E99897350C7C491E823CB91A9FED034534781492E4B8EEAD1664F48CFDD0DEADC79554A2A72441328621D336A7BC284946AD531847A6065A17B107DEF921CE79BDAD6C7F3747799A X-C8649E89: 1C3962B70DF3F0ADE00A9FD3E00BEEDF3FED46C3ACD6F73ED3581295AF09D3DF87807E0823442EA2ED31085941D9CD0AF7F820E7B07EA4CF698F8CEF598D76CE1628070B25105493ADED60FE3EEE9E3F988A22A85A12BB128853F4E7B5962328F13513DA892F5B9DD5D3EB0CC0FAD286DAD5AB2F7821127752274A29A37EDCE65F4332CA8FE04980913E6812662D5F2A5EAB5682573093F7837F15F2B5E4A70B33F2C28C22F508233FCF178C6DD14203 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu53w8ahmwBjZKM/YPHZyZHvz5uv+WouB9+ObcCpyrx6l7KImUglyhkEat/+ysWwi0gdhEs0JGjl6ggRWTy1haxBpVdbIX1nthFXMZebaIdHP2ghjoIc/363UZI6Kf1ptIMVQiWK+2I7Y2sdgh5SAGV9Gw= X-Mailru-Sender: 520A125C2F17F0B1A9638AD358559B59641165FBDA3BD7013DE06ABAFEAF670547933BE82C157479B7CBEF92542CD7C88B0A2698F12F5C9EC77752E0C033A69E86920BD37369036789A8C6A0E60D2BB63A5DB60FBEB33A8A0DA7A0AF5A3A8387 X-Mras: Ok Subject: [Tarantool-patches] [PATCH luajit 2/3] Restore state when recording __concat metamethod throws OOM. X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches <tarantool-patches.dev.tarantool.org> List-Unsubscribe: <https://lists.tarantool.org/mailman/options/tarantool-patches>, <mailto:tarantool-patches-request@dev.tarantool.org?subject=unsubscribe> List-Archive: <https://lists.tarantool.org/pipermail/tarantool-patches/> List-Post: <mailto:tarantool-patches@dev.tarantool.org> List-Help: <mailto:tarantool-patches-request@dev.tarantool.org?subject=help> List-Subscribe: <https://lists.tarantool.org/mailman/listinfo/tarantool-patches>, <mailto:tarantool-patches-request@dev.tarantool.org?subject=subscribe> From: Sergey Kaplun via Tarantool-patches <tarantool-patches@dev.tarantool.org> Reply-To: Sergey Kaplun <skaplun@tarantool.org> Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" <tarantool-patches-bounces@dev.tarantool.org> From: Mike Pall <mike> Reported by Sergey Kaplun. (cherry picked from commit 19878ec05c239ccaf5f3d17af27670a963e25b8b) Before the patch, the Lua stack isn't restored if the OOM related to string allocation happens during fold optimization inside `rec_cat()`. This patch moves most of the `rec_cat()` logic to the protected `rec_mm_concat_cp()` to be able to restore the stack in case of OOM. Unfortunately, after this patch, the test <lj-839-concat-recording.test.lua> fails since the incorrect adjusting of the topslot. It will be fixed in the next patch. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#11055 --- src/lj_record.c | 51 +++++++++++------- .../lj-1298-oom-on-concat-recording.test.lua | 53 +++++++++++++++++++ test/tarantool-tests/utils/CMakeLists.txt | 1 + test/tarantool-tests/utils/allocinject.c | 21 ++++++++ 4 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 test/tarantool-tests/lj-1298-oom-on-concat-recording.test.lua diff --git a/src/lj_record.c b/src/lj_record.c index 5345fa63..7a481a51 100644 --- a/src/lj_record.c +++ b/src/lj_record.c @@ -1943,25 +1943,19 @@ static TRef rec_tnew(jit_State *J, uint32_t ah) typedef struct RecCatDataCP { jit_State *J; - RecordIndex *ix; + BCReg baseslot, topslot; + TRef tr; } RecCatDataCP; static TValue *rec_mm_concat_cp(lua_State *L, lua_CFunction dummy, void *ud) { RecCatDataCP *rcd = (RecCatDataCP *)ud; - UNUSED(L); UNUSED(dummy); - rec_mm_arith(rcd->J, rcd->ix, MM_concat); /* Call __concat metamethod. */ - return NULL; -} - -static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot) -{ + jit_State *J = rcd->J; + BCReg baseslot = rcd->baseslot, topslot = rcd->topslot; TRef *top = &J->base[topslot]; - TValue savetv[5+LJ_FR2]; BCReg s; RecordIndex ix; - RecCatDataCP rcd; - int errcode; + UNUSED(L); UNUSED(dummy); lj_assertJ(baseslot < topslot, "bad CAT arg"); for (s = baseslot; s <= topslot; s++) (void)getslot(J, s); /* Ensure all arguments have a reference. */ @@ -1983,7 +1977,10 @@ static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot) } while (trp <= top); tr = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr); J->maxslot = (BCReg)(xbase - J->base); - if (xbase == base) return tr; /* Return simple concatenation result. */ + if (xbase == base) { + rcd->tr = tr; /* Return simple concatenation result. */ + return NULL; + } /* Pass partial result. */ topslot = J->maxslot--; *xbase = tr; @@ -1996,13 +1993,31 @@ static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot) copyTV(J->L, &ix.tabv, &J->L->base[topslot-1]); ix.tab = top[-1]; ix.key = top[0]; - memcpy(savetv, &J->L->base[topslot-1], sizeof(savetv)); /* Save slots. */ + rec_mm_arith(J, &ix, MM_concat); /* Call __concat metamethod. */ + rcd->tr = 0; /* No result yet. */ + return NULL; +} + +static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot) +{ + lua_State *L = J->L; + ptrdiff_t delta = L->top - L->base; + TValue savetv[5+LJ_FR2], errobj; + RecCatDataCP rcd; + int errcode; rcd.J = J; - rcd.ix = &ix; - errcode = lj_vm_cpcall(J->L, NULL, &rcd, rec_mm_concat_cp); - memcpy(&J->L->base[topslot-1], savetv, sizeof(savetv)); /* Restore slots. */ - if (errcode) return (TRef)(-errcode); - return 0; /* No result yet. */ + rcd.baseslot = baseslot; + rcd.topslot = topslot; + memcpy(savetv, &L->base[topslot-1], sizeof(savetv)); /* Save slots. */ + errcode = lj_vm_cpcall(L, NULL, &rcd, rec_mm_concat_cp); + if (errcode) copyTV(L, &errobj, L->top-1); + memcpy(&L->base[topslot-1], savetv, sizeof(savetv)); /* Restore slots. */ + if (errcode) { + L->top = L->base + delta; + copyTV(L, L->top++, &errobj); + return (TRef)(-errcode); + } + return rcd.tr; } /* -- Record bytecode ops ------------------------------------------------- */ diff --git a/test/tarantool-tests/lj-1298-oom-on-concat-recording.test.lua b/test/tarantool-tests/lj-1298-oom-on-concat-recording.test.lua new file mode 100644 index 00000000..961df798 --- /dev/null +++ b/test/tarantool-tests/lj-1298-oom-on-concat-recording.test.lua @@ -0,0 +1,53 @@ +local tap = require('tap') + +-- Test file to demonstrate unbalanced Lua stack after instruction +-- recording due to throwing an OOM error at the moment of +-- recording without restoring the Lua stack back. +-- See also: https://github.com/LuaJIT/LuaJIT/issues/1298. + +local test = tap.test('lj-1298-oom-on-concat-recording'):skipcond({ + ['Test requires JIT enabled'] = not jit.status(), +}) + +local allocinject = require('allocinject') + +test:plan(2) + +jit.opt.start('hotloop=1') + +-- Allocation limit to return `NULL`. +local ALLOC_LIMIT = 2048 + +local bigstr = string.rep('x', ALLOC_LIMIT) +local __concat = function() + return 'concated' +end + +-- Need to use metamethod call in the concat recording. +-- We may use any object with a metamethod, but let's use a table +-- as the most common one. +local concatable_t = setmetatable({}, { + __concat = __concat, +}) + +local function concat_with_err() + local counter = 0 + local _ + while counter < 1 do + counter = counter + 1 + _ = bigstr .. concatable_t .. '' + end + assert(false, 'unreachable, should raise an error before') +end + +-- Returns `NULL` on any allocation beyond the given limit. +allocinject.enable_null_limited_alloc(ALLOC_LIMIT) + +local status, errmsg = pcall(concat_with_err) + +allocinject.disable() + +test:ok(not status, 'no assertion failure during recording, correct status') +test:like(errmsg, 'not enough memory', 'correct error message') + +test:done(true) diff --git a/test/tarantool-tests/utils/CMakeLists.txt b/test/tarantool-tests/utils/CMakeLists.txt index 624cc933..15871934 100644 --- a/test/tarantool-tests/utils/CMakeLists.txt +++ b/test/tarantool-tests/utils/CMakeLists.txt @@ -2,5 +2,6 @@ list(APPEND tests lj-1166-error-stitch-oom-ir-buff.test.lua lj-1166-error-stitch-oom-snap-buff.test.lua lj-1247-fin-tab-rehashing-on-trace.test.lua + lj-1298-oom-on-concat-recording.test.lua ) BuildTestCLib(allocinject allocinject.c "${tests}") diff --git a/test/tarantool-tests/utils/allocinject.c b/test/tarantool-tests/utils/allocinject.c index 21e1d611..7ac60625 100644 --- a/test/tarantool-tests/utils/allocinject.c +++ b/test/tarantool-tests/utils/allocinject.c @@ -34,6 +34,19 @@ static void *allocf_inj_null_doubling_realloc(void *ud, void *ptr, size_t osize, return old_allocf(ud, ptr, osize, nsize); } +static size_t limit = 0; +/* Returns `NULL` on allocations beyond the given limit. */ +static void *allocf_inj_null_limited_alloc(void *ud, void *ptr, size_t osize, + size_t nsize) +{ + assert(old_allocf != NULL); + assert(limit != 0); + /* Check the specific allocation. */ + if (osize == 0 && nsize > limit) + return NULL; + return old_allocf(ud, ptr, osize, nsize); +} + static int enable(lua_State *L, lua_Alloc allocf_with_injection) { assert(old_allocf == NULL); @@ -52,6 +65,13 @@ static int enable_null_doubling_realloc(lua_State *L) return enable(L, allocf_inj_null_doubling_realloc); } +static int enable_null_limited_alloc(lua_State *L) +{ + limit = lua_tointeger(L, 1); + assert(limit != 0); + return enable(L, allocf_inj_null_limited_alloc); +} + /* Restore the default allocator function. */ static int disable(lua_State *L) { @@ -65,6 +85,7 @@ static int disable(lua_State *L) static const struct luaL_Reg allocinject[] = { {"enable_null_alloc", enable_null_alloc}, {"enable_null_doubling_realloc", enable_null_doubling_realloc}, + {"enable_null_limited_alloc", enable_null_limited_alloc}, {"disable", disable}, {NULL, NULL} }; -- 2.48.1