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 9F70B14FDB50; Fri, 22 Aug 2025 09:36:09 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 9F70B14FDB50 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1755844569; bh=eiN/yenOPVES3PJLkVmvJoX7o9W76bA0h8G0b22BKLM=; 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=HD95rHxACxcvKQVaIP/xx7mCTTfY8GoX2unW34zKjIpd1LyU6MjDs4pOLiH2yI+BT OrW0yHcVUFdfhyFdd1pnixMTiCrH/nCGnxH3VwJT9CepgsAmqoLQ811CqWPuNDQfuI PNHtmw1Q3CtGlDcHmr++F6PrnBnpesOZ6citlAso= Received: from send149.i.mail.ru (send149.i.mail.ru [89.221.237.244]) (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 EA99314FDB50 for ; Fri, 22 Aug 2025 09:35:39 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org EA99314FDB50 Received: by exim-smtp-59d7b54549-zp5cw with esmtpa (envelope-from ) id 1upLND-00000000DVz-03b9; Fri, 22 Aug 2025 09:35:39 +0300 To: Sergey Bronnikov Date: Fri, 22 Aug 2025 09:36:17 +0300 Message-ID: X-Mailer: git-send-email 2.50.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD9CD668969C51240A43B00F94B44AE3F7F179517C04BFA5284182A05F5380850406A69C6A2CC73FE963DE06ABAFEAF670553F24876C3B5C82F8CDE13A9094B77F40D92EFB1A9252998 X-7FA49CB5: 4BFC5D8501E51E47CA8F6BE7C18995EBF17A87A288458776E79944A8D2A15AF4A6333920E0F9D84FF01C4FAB541DE28FCE208E8FC270FF883B56EE1CD41719AD6EEED3D1F8F94EF624AD3460B7163A6507627EB66F572D0C63D3EDE7CE9F49F504D6AF0AB02B6E8AB3511083BD625FA657FDCB845166BEDE31F443FECF87DDAC7145D407EA63E20314A8176813119CCC6BCF00562456A40908BD49AA745F4EB9A320717265661E70386B6D831821CFCFEDA412B152283F287BD448575E1279A514A8176813119CCC953E0C1C45D6C23378D0058C30BC5C781A495265874E50822C0CE862CFDDF4E111CEB150E6113A5DB94301A5F42BD15882F519A0EFA9D3BDFD1713539A3C69E83BDB488C0B47A6F318BB8896F27FB42885BD5A8B2055D3F3F17A87A288458776F113C106F3FD22680E36F106248C3CC5EEA1A822253BD6E0 X-C1DE0DAB: B30BEEBCB9DAB3F04F1D36FF21F74672478DEAD3877DCF16CE13EA3996C79D05CA760A85A008FCF6BCC2DF43DD22AB26DB841BC0FD54B82633012DAE6C4C67E065C483732B0C61AE0404AC03FA789E4130AFED0B0E96279AAB8C77F2232E6289 X-C8649E89: 1C3962B70DF3F0ADE00A9FD3E00BEEDF3FED46C3ACD6F73ED3581295AF09D3DF87807E0823442EA2ED31085941D9CD0AF7F820E7B07EA4CF31CB278115131485B4F53397AC94CD8BCD228A16EFEFE3EC2A0F70FED1927217B268D298D11CBB1A69713F3DA683E8D5DC2F84A2275F0B6CBD621E3271146666662B0E3FCCEC94855F4332CA8FE04980913E6812662D5F2A5EAB5682573093F7837F15F2B5E4A70B33F2C28C22F508233FCF178C6DD14203 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu53w8ahmwBjZKM/YPHZyZHvz5uv+WouB9+ObcCpyrx6l7KImUglyhkEat/+ysWwi0gdhEs0JGjl6ggRWTy1haxBpVdbIX1nthFXMZebaIdHP2ghjoIc/363UZI6Kf1ptIMVUcWHxAuDCnGGpr3uMgeu9g= X-DA7885C5: 5B7F14BF3A243653F255D290C0D534F9AB66AB4974BB86A2FAAA41A264FB3D89F9C394A209ABEAA65B1A4C17EAA7BC4BEF2421ABFA55128DAF83EF9164C44C7E X-Mailru-Sender: 689FA8AB762F7393FE9E42A757851DB67B072A35EDE9D1F9404CC6CF53F09F95555CBF065CB367ABE49D44BB4BD9522A059A1ED8796F048DB274557F927329BE89D5A3BC2B10C37545BD1C3CC395C826B4A721A3011E896F X-Mras: Ok Subject: [Tarantool-patches] [PATCH luajit 1/2] FFI: Fix dangling CType references. 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 Kaplun via Tarantool-patches Reply-To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Mike Pall Reported by Sergey Kaplun. (cherry picked from commit 9c8eb7cfe10ef5939d9b358a0bd805a610818ba5) In case when the `ccall_set_args()` (and `crec_call_args()` in JIT) reallocates the ctype table, the references of the `ct` become invalid, and `ct->info` dereferences the invalid pointer. This patch fixes this by preserving the ctype info and ctype id to avoid dereferencing invalid pointer. This patch doesn't properly fix the issue for the JIT recording, since the child ctype reference is still in use. It will be fixed in the next patch. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#11691 --- src/lj_ccall.c | 19 +++-- src/lj_crecord.c | 21 +++--- ...-1360-dangling-ctype-ref-on-ccall.test.lua | 70 +++++++++++++++++++ 3 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 test/tarantool-tests/lj-1360-dangling-ctype-ref-on-ccall.test.lua diff --git a/src/lj_ccall.c b/src/lj_ccall.c index a989f657..c3b27572 100644 --- a/src/lj_ccall.c +++ b/src/lj_ccall.c @@ -890,7 +890,9 @@ void ccall_copy_struct(CCallState *cc, CType *ctr, void *dp, void *sp, int ft) /* -- Common C call handling ---------------------------------------------- */ -/* Infer the destination CTypeID for a vararg argument. */ +/* Infer the destination CTypeID for a vararg argument. +** Note: may reallocate cts->tab and invalidate CType pointers. +*/ CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o) { if (tvisnumber(o)) { @@ -918,13 +920,16 @@ CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o) } } -/* Setup arguments for C call. */ +/* Setup arguments for C call. +** Note: may reallocate cts->tab and invalidate CType pointers. +*/ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, CCallState *cc) { int gcsteps = 0; TValue *o, *top = L->top; CTypeID fid; + CTInfo info = ct->info; /* lj_ccall_ctid_vararg may invalidate ct pointer. */ CType *ctr; MSize maxgpr, ngpr = 0, nsp = 0, narg; #if CCALL_NARG_FPR @@ -943,7 +948,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, #if LJ_TARGET_X86 /* x86 has several different calling conventions. */ cc->resx87 = 0; - switch (ctype_cconv(ct->info)) { + switch (ctype_cconv(info)) { case CTCC_FASTCALL: maxgpr = 2; break; case CTCC_THISCALL: maxgpr = 1; break; default: maxgpr = 0; break; @@ -960,7 +965,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, } else if (ctype_iscomplex(ctr->info) || ctype_isstruct(ctr->info)) { /* Preallocate cdata object and anchor it after arguments. */ CTSize sz = ctr->size; - GCcdata *cd = lj_cdata_new(cts, ctype_cid(ct->info), sz); + GCcdata *cd = lj_cdata_new(cts, ctype_cid(info), sz); void *dp = cdataptr(cd); setcdataV(L, L->top++, cd); if (ctype_isstruct(ctr->info)) { @@ -996,7 +1001,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, lj_assertL(ctype_isfield(ctf->info), "field expected"); did = ctype_cid(ctf->info); } else { - if (!(ct->info & CTF_VARARG)) + if (!(info & CTF_VARARG)) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */ did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */ isva = 1; @@ -1157,11 +1162,11 @@ int lj_ccall_func(lua_State *L, GCcdata *cd) ct = ctype_rawchild(cts, ct); } if (ctype_isfunc(ct->info)) { + CTypeID id = ctype_typeid(cts, ct); CCallState cc; int gcsteps, ret; cc.func = (void (*)(void))cdata_getptr(cdataptr(cd), sz); gcsteps = ccall_set_args(L, cts, ct, &cc); - ct = (CType *)((intptr_t)ct-(intptr_t)cts->tab); cts->cb.slot = ~0u; lj_vm_ffi_call(&cc); if (cts->cb.slot != ~0u) { /* Blacklist function that called a callback. */ @@ -1169,7 +1174,7 @@ int lj_ccall_func(lua_State *L, GCcdata *cd) tv.u64 = ((uintptr_t)(void *)cc.func >> 2) | U64x(800000000, 00000000); setboolV(lj_tab_set(L, cts->miscmap, &tv), 1); } - ct = (CType *)((intptr_t)ct+(intptr_t)cts->tab); /* May be reallocated. */ + ct = ctype_get(cts, id); /* Table may have been reallocated. */ gcsteps += ccall_get_results(L, cts, ct, &cc, &ret); #if LJ_TARGET_X86 && LJ_ABI_WIN /* Automatically detect __stdcall and fix up C function declaration. */ diff --git a/src/lj_crecord.c b/src/lj_crecord.c index dcb90216..935106dc 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -1099,12 +1099,15 @@ static void crec_alloc(jit_State *J, RecordFFData *rd, CTypeID id) crec_finalizer(J, trcd, 0, fin); } -/* Record argument conversions. */ +/* Record argument conversions. +** Note: may reallocate cts->tab and invalidate CType pointers. +*/ static TRef crec_call_args(jit_State *J, RecordFFData *rd, CTState *cts, CType *ct) { TRef args[CCI_NARGS_MAX]; CTypeID fid; + CTInfo info = ct->info; /* lj_ccall_ctid_vararg may invalidate ct pointer. */ MSize i, n; TRef tr, *base; cTValue *o; @@ -1113,9 +1116,9 @@ static TRef crec_call_args(jit_State *J, RecordFFData *rd, TRef *arg0 = NULL, *arg1 = NULL; #endif int ngpr = 0; - if (ctype_cconv(ct->info) == CTCC_THISCALL) + if (ctype_cconv(info) == CTCC_THISCALL) ngpr = 1; - else if (ctype_cconv(ct->info) == CTCC_FASTCALL) + else if (ctype_cconv(info) == CTCC_FASTCALL) ngpr = 2; #endif @@ -1140,7 +1143,7 @@ static TRef crec_call_args(jit_State *J, RecordFFData *rd, lj_assertJ(ctype_isfield(ctf->info), "field expected"); did = ctype_cid(ctf->info); } else { - if (!(ct->info & CTF_VARARG)) + if (!(info & CTF_VARARG)) lj_trace_err(J, LJ_TRERR_NYICALL); /* Too many arguments. */ did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */ } @@ -1223,12 +1226,14 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) { CTState *cts = ctype_ctsG(J2G(J)); CType *ct = ctype_raw(cts, cd->ctypeid); + CTInfo info; IRType tp = IRT_PTR; if (ctype_isptr(ct->info)) { tp = (LJ_64 && ct->size == 8) ? IRT_P64 : IRT_P32; ct = ctype_rawchild(cts, ct); } - if (ctype_isfunc(ct->info)) { + info = ct->info; /* crec_call_args may invalidate ct pointer. */ + if (ctype_isfunc(info)) { TRef func = emitir(IRT(IR_FLOAD, tp), J->base[0], IRFL_CDATA_PTR); CType *ctr = ctype_rawchild(cts, ct); IRType t = crec_ct2irt(cts, ctr); @@ -1245,9 +1250,9 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) ctype_isenum(ctr->info)) || t == IRT_CDATA) { lj_trace_err(J, LJ_TRERR_NYICALL); } - if ((ct->info & CTF_VARARG) + if ((info & CTF_VARARG) #if LJ_TARGET_X86 - || ctype_cconv(ct->info) != CTCC_CDECL + || ctype_cconv(info) != CTCC_CDECL #endif ) func = emitir(IRT(IR_CARG, IRT_NIL), func, @@ -1270,7 +1275,7 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) } } else if (t == IRT_PTR || (LJ_64 && t == IRT_P32) || t == IRT_I64 || t == IRT_U64 || ctype_isenum(ctr->info)) { - TRef trid = lj_ir_kint(J, ctype_cid(ct->info)); + TRef trid = lj_ir_kint(J, ctype_cid(info)); tr = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, tr); if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J); } else if (t == IRT_FLOAT || t == IRT_U32) { diff --git a/test/tarantool-tests/lj-1360-dangling-ctype-ref-on-ccall.test.lua b/test/tarantool-tests/lj-1360-dangling-ctype-ref-on-ccall.test.lua new file mode 100644 index 00000000..2d790e43 --- /dev/null +++ b/test/tarantool-tests/lj-1360-dangling-ctype-ref-on-ccall.test.lua @@ -0,0 +1,70 @@ +local tap = require('tap') +local ffi = require('ffi') + +-- This test demonstrates LuaJIT's incorrect behaviour when the +-- reallocation of `cts->tab` strikes during the setup arguments +-- for the FFI call. +-- Before the patch, the test failed only under ASAN. +-- See also https://github.com/LuaJIT/LuaJIT/issues/1360. +local test = tap.test('lj-1360-dangling-ctype-ref-on-ccall'):skipcond({ + ['Impossible to predict the value of cts->top'] = _TARANTOOL, +}) + +test:plan(1) + +-- XXX: Declare the structure to increase `cts->top` up to 128 +-- slots. The setting up of function's arguments to the next call +-- will reallocate the `cts->tab` during `lj_ccall_ctid_vararg()` +-- and `ccall_set_args()` will use a dangling reference. +ffi.cdef[[ + struct test { + int a; + int b; + int c; + int d; + int e; + int f; + int g; + int h; + int i; + int j; + int k; + int l; + int m; + int n; + int o; + int p; + int q; + int r; + int s; + int t; + int u; + int v; + int w; + int x; + int y; + int z; + int aa; + int ab; + int ac; + int ad; + }; + // Use an existing function that actually takes no arguments. + // We can declare it however we want. + // Need a vararg function for this issue. + int getppid(...); +]] + +local arg_call = ffi.new('struct test') + +-- Assertions to check the `cts->top` value. +assert(ffi.typeinfo(127), 'cts->top >= 127') +assert(not ffi.typeinfo(128), 'cts->top < 128') + +-- Don't check the result, just check that there is no invalid +-- memory access. +ffi.C.getppid(arg_call) + +test:ok(true, 'no heap-use-after-free in C call') + +test:done(true) -- 2.50.1