From e3c70a7d813ec7e3226510acedd64fc96021d4b0 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 10 Mar 2025 00:05:08 +0100 Subject: [PATCH 01/21] macOS: Fix support for Apple hardened runtime. Reported by Christian Clason. #1334 --- src/lj_mcode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lj_mcode.c b/src/lj_mcode.c index 43694226a6..d8fa165e1d 100644 --- a/src/lj_mcode.c +++ b/src/lj_mcode.c @@ -138,7 +138,7 @@ static void mcode_free(jit_State *J, void *p, size_t sz) static int mcode_setprot(void *p, size_t sz, int prot) { #if MCMAP_CREATE - pthread_jit_write_protect_np((prot & PROC_EXEC)); + pthread_jit_write_protect_np((prot & PROT_EXEC)); return 0; #else return mprotect(p, sz, prot); From 4f2bb199fe7138247e0b075c886c9e9197cf0271 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 10 Mar 2025 02:53:20 +0100 Subject: [PATCH 02/21] macOS: Fix Apple hardened runtime support and put behind build option. Reported by vanc. #1334 --- src/lj_ccallback.c | 2 +- src/lj_mcode.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lj_ccallback.c b/src/lj_ccallback.c index d93dbc6457..7f08f0a848 100644 --- a/src/lj_ccallback.c +++ b/src/lj_ccallback.c @@ -263,7 +263,7 @@ static void *callback_mcode_init(global_State *g, uint32_t *page) #endif /* Check for macOS hardened runtime. */ -#if LUAJIT_SECURITY_MCODE != 0 && defined(MAP_JIT) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 110000 +#if defined(LUAJIT_ENABLE_OSX_HRT) && LUAJIT_SECURITY_MCODE != 0 && defined(MAP_JIT) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 110000 #include #define CCMAP_CREATE MAP_JIT #else diff --git a/src/lj_mcode.c b/src/lj_mcode.c index d8fa165e1d..2b8ac2df58 100644 --- a/src/lj_mcode.c +++ b/src/lj_mcode.c @@ -99,7 +99,7 @@ static int mcode_setprot(void *p, size_t sz, DWORD prot) #endif /* Check for macOS hardened runtime. */ -#if LUAJIT_SECURITY_MCODE != 0 && defined(MAP_JIT) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 110000 +#if defined(LUAJIT_ENABLE_OSX_HRT) && LUAJIT_SECURITY_MCODE != 0 && defined(MAP_JIT) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 110000 #include #define MCMAP_CREATE MAP_JIT #else @@ -111,6 +111,8 @@ static int mcode_setprot(void *p, size_t sz, DWORD prot) #define MCPROT_RWX (PROT_READ|PROT_WRITE|PROT_EXEC) #ifdef PROT_MPROTECT #define MCPROT_CREATE (PROT_MPROTECT(MCPROT_RWX)) +#elif MCMAP_CREATE +#define MCPROT_CREATE PROT_EXEC #else #define MCPROT_CREATE 0 #endif From 84cb21ffaf648b472ff3884556e2c413e8abe179 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 10 Mar 2025 02:56:07 +0100 Subject: [PATCH 03/21] REVERT: Change handling of nil value markers in template tables. --- src/lj_bcread.c | 10 ++++------ src/lj_bcwrite.c | 8 +++----- src/lj_opt_fold.c | 6 ++---- src/lj_opt_mem.c | 4 +--- src/lj_parse.c | 20 +++++++++++++++----- src/lj_tab.c | 1 - 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/lj_bcread.c b/src/lj_bcread.c index 37e909b391..ee7d7c1870 100644 --- a/src/lj_bcread.c +++ b/src/lj_bcread.c @@ -179,7 +179,7 @@ static const void *bcread_varinfo(GCproto *pt) } /* Read a single constant key/value of a template table. */ -static void bcread_ktabk(LexState *ls, TValue *o, GCtab *t) +static void bcread_ktabk(LexState *ls, TValue *o) { MSize tp = bcread_uleb128(ls); if (tp >= BCDUMP_KTAB_STR) { @@ -191,8 +191,6 @@ static void bcread_ktabk(LexState *ls, TValue *o, GCtab *t) } else if (tp == BCDUMP_KTAB_NUM) { o->u32.lo = bcread_uleb128(ls); o->u32.hi = bcread_uleb128(ls); - } else if (tp == BCDUMP_KTAB_NIL) { /* Restore nil value marker. */ - settabV(ls->L, o, t); } else { lj_assertLS(tp <= BCDUMP_KTAB_TRUE, "bad constant type %d", tp); setpriV(o, ~tp); @@ -209,15 +207,15 @@ static GCtab *bcread_ktab(LexState *ls) MSize i; TValue *o = tvref(t->array); for (i = 0; i < narray; i++, o++) - bcread_ktabk(ls, o, t); + bcread_ktabk(ls, o); } if (nhash) { /* Read hash entries. */ MSize i; for (i = 0; i < nhash; i++) { TValue key; - bcread_ktabk(ls, &key, t); + bcread_ktabk(ls, &key); lj_assertLS(!tvisnil(&key), "nil key"); - bcread_ktabk(ls, lj_tab_set(ls->L, t, &key), t); + bcread_ktabk(ls, lj_tab_set(ls->L, t, &key)); } } return t; diff --git a/src/lj_bcwrite.c b/src/lj_bcwrite.c index ec6f13c8d5..de200ef4ad 100644 --- a/src/lj_bcwrite.c +++ b/src/lj_bcwrite.c @@ -71,8 +71,6 @@ static void bcwrite_ktabk(BCWriteCtx *ctx, cTValue *o, int narrow) *p++ = BCDUMP_KTAB_NUM; p = lj_strfmt_wuleb128(p, o->u32.lo); p = lj_strfmt_wuleb128(p, o->u32.hi); - } else if (tvistab(o)) { /* Write the nil value marker as a nil. */ - *p++ = BCDUMP_KTAB_NIL; } else { lj_assertBCW(tvispri(o), "unhandled type %d", itype(o)); *p++ = BCDUMP_KTAB_NIL+~itype(o); @@ -135,7 +133,7 @@ static void bcwrite_ktab_sorted_hash(BCWriteCtx *ctx, Node *node, MSize nhash) TValue **heap = ctx->heap; MSize i = nhash; for (;; node--) { /* Build heap. */ - if (!tvisnil(&node->val)) { + if (!tvisnil(&node->key)) { bcwrite_ktabk_heap_insert(heap, --i, nhash, &node->key); if (i == 0) break; } @@ -165,7 +163,7 @@ static void bcwrite_ktab(BCWriteCtx *ctx, char *p, const GCtab *t) MSize i, hmask = t->hmask; Node *node = noderef(t->node); for (i = 0; i <= hmask; i++) - nhash += !tvisnil(&node[i].val); + nhash += !tvisnil(&node[i].key); } /* Write number of array slots and hash slots. */ p = lj_strfmt_wuleb128(p, narray); @@ -186,7 +184,7 @@ static void bcwrite_ktab(BCWriteCtx *ctx, char *p, const GCtab *t) } else { MSize i = nhash; for (;; node--) - if (!tvisnil(&node->val)) { + if (!tvisnil(&node->key)) { bcwrite_ktabk(ctx, &node->key, 0); bcwrite_ktabk(ctx, &node->val, 1); if (--i == 0) break; diff --git a/src/lj_opt_fold.c b/src/lj_opt_fold.c index 6fdf45663f..36aacebb03 100644 --- a/src/lj_opt_fold.c +++ b/src/lj_opt_fold.c @@ -2217,11 +2217,9 @@ LJFOLD(HREF TDUP KNUM) LJFOLDF(fwd_href_tdup) { TValue keyv; - cTValue *val; lj_ir_kvalue(J->L, &keyv, fright); - val = lj_tab_get(J->L, ir_ktab(IR(fleft->op1)), &keyv); - /* Check for either nil or the nil value marker in the template table. */ - if ((tvisnil(val) || tvistab(val)) && lj_opt_fwd_href_nokey(J)) + if (lj_tab_get(J->L, ir_ktab(IR(fleft->op1)), &keyv) == niltvg(J2G(J)) && + lj_opt_fwd_href_nokey(J)) return lj_ir_kkptr(J, niltvg(J2G(J))); return NEXTFOLD; } diff --git a/src/lj_opt_mem.c b/src/lj_opt_mem.c index 6f956b37e9..8cacfcfef9 100644 --- a/src/lj_opt_mem.c +++ b/src/lj_opt_mem.c @@ -233,9 +233,7 @@ static TRef fwd_ahload(jit_State *J, IRRef xref) return lj_ir_knum_u64(J, tv->u64); else if (tvisint(tv)) return lj_ir_kint(J, intV(tv)); - else if (tvistab(tv)) /* Template table nil value marker. */ - return TREF_NIL; - else if (tvisstr(tv)) + else if (tvisgcv(tv)) return lj_ir_kstr(J, strV(tv)); } /* Othwerwise: don't intern as a constant. */ diff --git a/src/lj_parse.c b/src/lj_parse.c index f41163804a..7009759808 100644 --- a/src/lj_parse.c +++ b/src/lj_parse.c @@ -1725,7 +1725,7 @@ static void expr_table(LexState *ls, ExpDesc *e) FuncState *fs = ls->fs; BCLine line = ls->linenumber; GCtab *t = NULL; - int vcall = 0, needarr = 0; + int vcall = 0, needarr = 0, fixt = 0; uint32_t narr = 1; /* First array index. */ uint32_t nhash = 0; /* Number of hash entries. */ BCReg freg = fs->freereg; @@ -1769,10 +1769,9 @@ static void expr_table(LexState *ls, ExpDesc *e) lj_gc_anybarriert(fs->L, t); if (expr_isk_nojump(&val)) { /* Add const key/value to template table. */ expr_kvalue(fs, v, &val); - /* Mark nil value with table value itself to preserve the key. */ - if (key.k == VKSTR && tvisnil(v)) settabV(fs->L, v, t); - } else { /* Preserve the key for the following non-const store. */ - settabV(fs->L, v, t); + } else { /* Otherwise create dummy string key (avoids lj_tab_newkey). */ + settabV(fs->L, v, t); /* Preserve key with table itself as value. */ + fixt = 1; /* Fix this later, after all resizes. */ goto nonconst; } } else { @@ -1814,6 +1813,17 @@ static void expr_table(LexState *ls, ExpDesc *e) } else { if (needarr && t->asize < narr) lj_tab_reasize(fs->L, t, narr-1); + if (fixt) { /* Fix value for dummy keys in template table. */ + Node *node = noderef(t->node); + uint32_t i, hmask = t->hmask; + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + if (tvistab(&n->val)) { + lj_assertFS(tabV(&n->val) == t, "bad dummy key in template table"); + setnilV(&n->val); /* Turn value into nil. */ + } + } + } lj_gc_check(fs->L); } } diff --git a/src/lj_tab.c b/src/lj_tab.c index 62e336111a..2d08055206 100644 --- a/src/lj_tab.c +++ b/src/lj_tab.c @@ -194,7 +194,6 @@ GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt) Node *next = nextnode(kn); /* Don't use copyTV here, since it asserts on a copy of a dead key. */ n->val = kn->val; n->key = kn->key; - if (tvistab(&n->val)) setnilV(&n->val); /* Replace nil value marker. */ setmref(n->next, next == NULL? next : (Node *)((char *)next + d)); } } From 538a82133ad6fddfd0ca64de167c4aca3bc1a2da Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 11 Mar 2025 23:04:30 +0100 Subject: [PATCH 04/21] Change handling of nil value markers in template tables. Reported by Bernhard M. Wiedemann. #1348 #1155 Fixes from Peter Cawley, Christian Clason, Lewis Russell. --- src/lj_bcread.c | 10 ++++++---- src/lj_bcwrite.c | 8 +++++--- src/lj_opt_fold.c | 6 ++++-- src/lj_opt_mem.c | 4 +++- src/lj_parse.c | 20 +++++--------------- src/lj_tab.c | 1 + 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/lj_bcread.c b/src/lj_bcread.c index ee7d7c1870..5570952208 100644 --- a/src/lj_bcread.c +++ b/src/lj_bcread.c @@ -179,7 +179,7 @@ static const void *bcread_varinfo(GCproto *pt) } /* Read a single constant key/value of a template table. */ -static void bcread_ktabk(LexState *ls, TValue *o) +static void bcread_ktabk(LexState *ls, TValue *o, GCtab *t) { MSize tp = bcread_uleb128(ls); if (tp >= BCDUMP_KTAB_STR) { @@ -191,6 +191,8 @@ static void bcread_ktabk(LexState *ls, TValue *o) } else if (tp == BCDUMP_KTAB_NUM) { o->u32.lo = bcread_uleb128(ls); o->u32.hi = bcread_uleb128(ls); + } else if (t && tp == BCDUMP_KTAB_NIL) { /* Restore nil value marker. */ + settabV(ls->L, o, t); } else { lj_assertLS(tp <= BCDUMP_KTAB_TRUE, "bad constant type %d", tp); setpriV(o, ~tp); @@ -207,15 +209,15 @@ static GCtab *bcread_ktab(LexState *ls) MSize i; TValue *o = tvref(t->array); for (i = 0; i < narray; i++, o++) - bcread_ktabk(ls, o); + bcread_ktabk(ls, o, NULL); } if (nhash) { /* Read hash entries. */ MSize i; for (i = 0; i < nhash; i++) { TValue key; - bcread_ktabk(ls, &key); + bcread_ktabk(ls, &key, NULL); lj_assertLS(!tvisnil(&key), "nil key"); - bcread_ktabk(ls, lj_tab_set(ls->L, t, &key)); + bcread_ktabk(ls, lj_tab_set(ls->L, t, &key), t); } } return t; diff --git a/src/lj_bcwrite.c b/src/lj_bcwrite.c index de200ef4ad..ec6f13c8d5 100644 --- a/src/lj_bcwrite.c +++ b/src/lj_bcwrite.c @@ -71,6 +71,8 @@ static void bcwrite_ktabk(BCWriteCtx *ctx, cTValue *o, int narrow) *p++ = BCDUMP_KTAB_NUM; p = lj_strfmt_wuleb128(p, o->u32.lo); p = lj_strfmt_wuleb128(p, o->u32.hi); + } else if (tvistab(o)) { /* Write the nil value marker as a nil. */ + *p++ = BCDUMP_KTAB_NIL; } else { lj_assertBCW(tvispri(o), "unhandled type %d", itype(o)); *p++ = BCDUMP_KTAB_NIL+~itype(o); @@ -133,7 +135,7 @@ static void bcwrite_ktab_sorted_hash(BCWriteCtx *ctx, Node *node, MSize nhash) TValue **heap = ctx->heap; MSize i = nhash; for (;; node--) { /* Build heap. */ - if (!tvisnil(&node->key)) { + if (!tvisnil(&node->val)) { bcwrite_ktabk_heap_insert(heap, --i, nhash, &node->key); if (i == 0) break; } @@ -163,7 +165,7 @@ static void bcwrite_ktab(BCWriteCtx *ctx, char *p, const GCtab *t) MSize i, hmask = t->hmask; Node *node = noderef(t->node); for (i = 0; i <= hmask; i++) - nhash += !tvisnil(&node[i].key); + nhash += !tvisnil(&node[i].val); } /* Write number of array slots and hash slots. */ p = lj_strfmt_wuleb128(p, narray); @@ -184,7 +186,7 @@ static void bcwrite_ktab(BCWriteCtx *ctx, char *p, const GCtab *t) } else { MSize i = nhash; for (;; node--) - if (!tvisnil(&node->key)) { + if (!tvisnil(&node->val)) { bcwrite_ktabk(ctx, &node->key, 0); bcwrite_ktabk(ctx, &node->val, 1); if (--i == 0) break; diff --git a/src/lj_opt_fold.c b/src/lj_opt_fold.c index 36aacebb03..6fdf45663f 100644 --- a/src/lj_opt_fold.c +++ b/src/lj_opt_fold.c @@ -2217,9 +2217,11 @@ LJFOLD(HREF TDUP KNUM) LJFOLDF(fwd_href_tdup) { TValue keyv; + cTValue *val; lj_ir_kvalue(J->L, &keyv, fright); - if (lj_tab_get(J->L, ir_ktab(IR(fleft->op1)), &keyv) == niltvg(J2G(J)) && - lj_opt_fwd_href_nokey(J)) + val = lj_tab_get(J->L, ir_ktab(IR(fleft->op1)), &keyv); + /* Check for either nil or the nil value marker in the template table. */ + if ((tvisnil(val) || tvistab(val)) && lj_opt_fwd_href_nokey(J)) return lj_ir_kkptr(J, niltvg(J2G(J))); return NEXTFOLD; } diff --git a/src/lj_opt_mem.c b/src/lj_opt_mem.c index 8cacfcfef9..6f956b37e9 100644 --- a/src/lj_opt_mem.c +++ b/src/lj_opt_mem.c @@ -233,7 +233,9 @@ static TRef fwd_ahload(jit_State *J, IRRef xref) return lj_ir_knum_u64(J, tv->u64); else if (tvisint(tv)) return lj_ir_kint(J, intV(tv)); - else if (tvisgcv(tv)) + else if (tvistab(tv)) /* Template table nil value marker. */ + return TREF_NIL; + else if (tvisstr(tv)) return lj_ir_kstr(J, strV(tv)); } /* Othwerwise: don't intern as a constant. */ diff --git a/src/lj_parse.c b/src/lj_parse.c index 7009759808..f41163804a 100644 --- a/src/lj_parse.c +++ b/src/lj_parse.c @@ -1725,7 +1725,7 @@ static void expr_table(LexState *ls, ExpDesc *e) FuncState *fs = ls->fs; BCLine line = ls->linenumber; GCtab *t = NULL; - int vcall = 0, needarr = 0, fixt = 0; + int vcall = 0, needarr = 0; uint32_t narr = 1; /* First array index. */ uint32_t nhash = 0; /* Number of hash entries. */ BCReg freg = fs->freereg; @@ -1769,9 +1769,10 @@ static void expr_table(LexState *ls, ExpDesc *e) lj_gc_anybarriert(fs->L, t); if (expr_isk_nojump(&val)) { /* Add const key/value to template table. */ expr_kvalue(fs, v, &val); - } else { /* Otherwise create dummy string key (avoids lj_tab_newkey). */ - settabV(fs->L, v, t); /* Preserve key with table itself as value. */ - fixt = 1; /* Fix this later, after all resizes. */ + /* Mark nil value with table value itself to preserve the key. */ + if (key.k == VKSTR && tvisnil(v)) settabV(fs->L, v, t); + } else { /* Preserve the key for the following non-const store. */ + settabV(fs->L, v, t); goto nonconst; } } else { @@ -1813,17 +1814,6 @@ static void expr_table(LexState *ls, ExpDesc *e) } else { if (needarr && t->asize < narr) lj_tab_reasize(fs->L, t, narr-1); - if (fixt) { /* Fix value for dummy keys in template table. */ - Node *node = noderef(t->node); - uint32_t i, hmask = t->hmask; - for (i = 0; i <= hmask; i++) { - Node *n = &node[i]; - if (tvistab(&n->val)) { - lj_assertFS(tabV(&n->val) == t, "bad dummy key in template table"); - setnilV(&n->val); /* Turn value into nil. */ - } - } - } lj_gc_check(fs->L); } } diff --git a/src/lj_tab.c b/src/lj_tab.c index 2d08055206..62e336111a 100644 --- a/src/lj_tab.c +++ b/src/lj_tab.c @@ -194,6 +194,7 @@ GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt) Node *next = nextnode(kn); /* Don't use copyTV here, since it asserts on a copy of a dead key. */ n->val = kn->val; n->key = kn->key; + if (tvistab(&n->val)) setnilV(&n->val); /* Replace nil value marker. */ setmref(n->next, next == NULL? next : (Node *)((char *)next + d)); } } From e9e4b6d302b5e7e4a04a3c7f78cb561a2c156a37 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 7 Apr 2025 09:22:07 +0200 Subject: [PATCH 05/21] Initialize unused value when specializing to cdata metatable. Reported by jakitliang. #1354 --- src/lj_record.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lj_record.c b/src/lj_record.c index 20a7ea36e1..d336f642e4 100644 --- a/src/lj_record.c +++ b/src/lj_record.c @@ -854,7 +854,10 @@ int lj_record_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm) return 0; /* No metamethod. */ } /* The cdata metatable is treated as immutable. */ - if (LJ_HASFFI && tref_iscdata(ix->tab)) goto immutable_mt; + if (LJ_HASFFI && tref_iscdata(ix->tab)) { + mix.tab = TREF_NIL; + goto immutable_mt; + } ix->mt = mix.tab = lj_ir_ktab(J, mt); goto nocheck; } From e76bb50d44702f601ec5dd167b03b475ed53860c Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 7 Apr 2025 10:27:40 +0200 Subject: [PATCH 06/21] Fix error generation in load*. Reported by Sergey Kaplun. #1353 --- src/lj_load.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lj_load.c b/src/lj_load.c index 90a61027ad..6c8ae9f154 100644 --- a/src/lj_load.c +++ b/src/lj_load.c @@ -108,8 +108,9 @@ LUALIB_API int luaL_loadfilex(lua_State *L, const char *filename, copyTV(L, L->top-1, L->top); } if (err) { + const char *fname = filename ? filename : "stdin"; L->top--; - lua_pushfstring(L, "cannot read %s: %s", chunkname+1, strerror(err)); + lua_pushfstring(L, "cannot read %s: %s", fname, strerror(err)); return LUA_ERRFILE; } return status; From c262976486e1e007b56380b6a36bfbea5f51d470 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 10 Apr 2025 22:06:47 +0200 Subject: [PATCH 07/21] ARM64: Fix pass-by-value struct calling conventions. Reported by AnthonyK213. #1357 --- src/lj_ccall.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lj_ccall.c b/src/lj_ccall.c index ae69cd28d1..f003d75674 100644 --- a/src/lj_ccall.c +++ b/src/lj_ccall.c @@ -781,17 +781,24 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct) { CTSize sz = ct->size; unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION); - while (ct->sib) { + while (ct->sib && n <= 4) { + unsigned int m = 1; CType *sct; ct = ctype_get(cts, ct->sib); if (ctype_isfield(ct->info)) { sct = ctype_rawchild(cts, ct); + if (ctype_isarray(sct->info)) { + CType *cct = ctype_rawchild(cts, sct); + if (!cct->size) continue; + m = sct->size / cct->size; + sct = cct; + } if (ctype_isfp(sct->info)) { r |= sct->size; - if (!isu) n++; else if (n == 0) n = 1; + if (!isu) n += m; else if (n < m) n = m; } else if (ctype_iscomplex(sct->info)) { r |= (sct->size >> 1); - if (!isu) n += 2; else if (n < 2) n = 2; + if (!isu) n += 2*m; else if (n < 2*m) n = 2*m; } else if (ctype_isstruct(sct->info)) { goto substruct; } else { @@ -803,10 +810,11 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct) sct = ctype_rawchild(cts, ct); substruct: if (sct->size > 0) { - unsigned int s = ccall_classify_struct(cts, sct); + unsigned int s = ccall_classify_struct(cts, sct), sn; if (s <= 1) goto noth; r |= (s & 255); - if (!isu) n += (s >> 8); else if (n < (s >>8)) n = (s >> 8); + sn = (s >> 8) * m; + if (!isu) n += sn; else if (n < sn) n = sn; } } } From 51d4c26ec7805d77bfc3470fdf99b73c4ef2faec Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 10 Apr 2025 22:45:38 +0200 Subject: [PATCH 08/21] ARM: Fix soft-float math.min()/math.max(). Reported by Dong Jianqiang. #1356 --- src/lj_asm_arm.h | 2 +- src/vm_arm.dasc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lj_asm_arm.h b/src/lj_asm_arm.h index de435057e1..24deaeae27 100644 --- a/src/lj_asm_arm.h +++ b/src/lj_asm_arm.h @@ -1927,7 +1927,7 @@ static void asm_hiop(ASMState *as, IRIns *ir) } else if ((ir-1)->o == IR_MIN || (ir-1)->o == IR_MAX) { as->curins--; /* Always skip the loword min/max. */ if (uselo || usehi) - asm_sfpmin_max(as, ir-1, (ir-1)->o == IR_MIN ? CC_PL : CC_LE); + asm_sfpmin_max(as, ir-1, (ir-1)->o == IR_MIN ? CC_HS : CC_LS); return; #elif LJ_HASFFI } else if ((ir-1)->o == IR_CONV) { diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc index ca08fc117e..86bef0cfbc 100644 --- a/src/vm_arm.dasc +++ b/src/vm_arm.dasc @@ -1717,8 +1717,8 @@ static void build_subroutines(BuildCtx *ctx) |.endif |.endmacro | - | math_minmax math_min, gt, pl - | math_minmax math_max, lt, le + | math_minmax math_min, gt, hs + | math_minmax math_max, lt, ls | |//-- String library ----------------------------------------------------- | From eec7a8016c3381b949b5d84583800d05897fa960 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 10 Apr 2025 22:53:50 +0200 Subject: [PATCH 09/21] Prevent Clang UB 'optimization' which breaks integerness checks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Kacper Michajłow. #1351 #1355 --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index 4a56d1e8e5..c83abfa0b6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -302,6 +302,9 @@ endif ifneq (,$(INSTALL_LJLIBD)) TARGET_XCFLAGS+= -DLUA_LJDIR=\"$(INSTALL_LJLIBD)\" endif +ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-strict-float-cast-overflow 2>/dev/null || echo 1)) + TARGET_XCFLAGS+= -fno-strict-float-cast-overflow +endif ############################################################################## # Target system detection. From 9c8eb7cfe10ef5939d9b358a0bd805a610818ba5 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Wed, 28 May 2025 20:36:24 +0200 Subject: [PATCH 10/21] FFI: Fix dangling CType references. Reported by Sergey Kaplun. Collateral of #1360 --- src/lj_ccall.c | 19 ++++++++++++------- src/lj_crecord.c | 21 +++++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/lj_ccall.c b/src/lj_ccall.c index 9c99bec7fa..5d6bb03d50 100644 --- a/src/lj_ccall.c +++ b/src/lj_ccall.c @@ -623,7 +623,9 @@ static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf) /* -- 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)) { @@ -651,13 +653,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 @@ -676,7 +681,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; @@ -693,7 +698,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)) { @@ -729,7 +734,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, lua_assert(ctype_isfield(ctf->info)); 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; @@ -869,11 +874,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. */ @@ -881,7 +886,7 @@ int lj_ccall_func(lua_State *L, GCcdata *cd) setlightudV(&tv, (void *)cc.func); 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 216144f32d..f686b35f21 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -988,12 +988,15 @@ static void crec_alloc(jit_State *J, RecordFFData *rd, CTypeID id) } } -/* 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; @@ -1002,9 +1005,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 @@ -1029,7 +1032,7 @@ static TRef crec_call_args(jit_State *J, RecordFFData *rd, lua_assert(ctype_isfield(ctf->info)); 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. */ } @@ -1112,12 +1115,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); @@ -1135,9 +1140,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, @@ -1160,7 +1165,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) { From cd4af8ad80bb6430ad2e547f7af236268c9be7d9 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Wed, 28 May 2025 21:02:31 +0200 Subject: [PATCH 11/21] Avoid out-of-range PC for stack overflow error from snapshot restore. Reported by Sergey Kaplun. #1359 --- src/lj_bc.h | 5 +++++ src/lj_parse.c | 14 +------------- src/lj_snap.c | 6 ++++-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/lj_bc.h b/src/lj_bc.h index 3f0563e4b9..0c7249b39f 100644 --- a/src/lj_bc.h +++ b/src/lj_bc.h @@ -255,6 +255,11 @@ static LJ_AINLINE int bc_isret(BCOp op) return (op == BC_RETM || op == BC_RET || op == BC_RET0 || op == BC_RET1); } +static LJ_AINLINE int bc_isret_or_tail(BCOp op) +{ + return (op == BC_CALLMT || op == BC_CALLT || bc_isret(op)); +} + LJ_DATA const uint16_t lj_bc_mode[]; LJ_DATA const uint16_t lj_bc_ofs[]; diff --git a/src/lj_parse.c b/src/lj_parse.c index ffd11b3bd9..3370296f0f 100644 --- a/src/lj_parse.c +++ b/src/lj_parse.c @@ -1529,23 +1529,11 @@ static void fs_fixup_var(LexState *ls, GCproto *pt, uint8_t *p, size_t ofsvar) #endif -/* Check if bytecode op returns. */ -static int bcopisret(BCOp op) -{ - switch (op) { - case BC_CALLMT: case BC_CALLT: - case BC_RETM: case BC_RET: case BC_RET0: case BC_RET1: - return 1; - default: - return 0; - } -} - /* Fixup return instruction for prototype. */ static void fs_fixup_ret(FuncState *fs) { BCPos lastpc = fs->pc; - if (lastpc <= fs->lasttarget || !bcopisret(bc_op(fs->bcbase[lastpc-1].ins))) { + if (lastpc <= fs->lasttarget || !bc_isret_or_tail(bc_op(fs->bcbase[lastpc-1].ins))) { if ((fs->bl->flags & FSCOPE_UPVAL)) bcemit_AJ(fs, BC_UCLO, 0, 0); bcemit_AD(fs, BC_RET0, 0, 1); /* Need final return. */ diff --git a/src/lj_snap.c b/src/lj_snap.c index 82ab6983d3..5426002119 100644 --- a/src/lj_snap.c +++ b/src/lj_snap.c @@ -872,8 +872,10 @@ const BCIns *lj_snap_restore(jit_State *J, void *exptr) const BCIns *pc = snap_pc(map[nent]); lua_State *L = J->L; - /* Set interpreter PC to the next PC to get correct error messages. */ - setcframe_pc(L->cframe, pc+1); + /* Set interpreter PC to the next PC to get correct error messages. + ** But not for returns or tail calls, since pc+1 may be out-of-range. + */ + setcframe_pc(L->cframe, bc_isret_or_tail(bc_op(*pc)) ? pc : pc+1); setcframe_pc(cframe_raw(cframe_prev(L->cframe)), pc); /* Make sure the stack is big enough for the slots from the snapshot. */ From 048972dbfdb6b441fe8a9bfe4d1f048966579ba8 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Wed, 28 May 2025 21:13:17 +0200 Subject: [PATCH 12/21] Fix JIT slot overflow during up-recursion. Reported by Sergey Kaplun. #1358 --- src/lj_record.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lj_record.c b/src/lj_record.c index d336f642e4..1d535a2299 100644 --- a/src/lj_record.c +++ b/src/lj_record.c @@ -749,7 +749,8 @@ void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults) lj_trace_err(J, LJ_TRERR_LLEAVE); } else if (J->needsnap) { /* Tailcalled to ff with side-effects. */ lj_trace_err(J, LJ_TRERR_NYIRETL); /* No way to insert snapshot here. */ - } else if (1 + pt->framesize >= LJ_MAX_JSLOTS) { + } else if (1 + pt->framesize >= LJ_MAX_JSLOTS || + J->baseslot + J->maxslot >= LJ_MAX_JSLOTS) { lj_trace_err(J, LJ_TRERR_STACKOV); } else { /* Return to lower frame. Guard for the target we return to. */ TRef trpt = lj_ir_kgc(J, obj2gco(pt), IRT_PROTO); From c64020f3c6d124503213147f2fb47c20335a395b Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 24 Jul 2025 15:29:54 +0200 Subject: [PATCH 13/21] FFI: Fix dangling CType references (again). Reported by Sergey Kaplun. Collateral of #1360 --- src/lj_crecord.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lj_crecord.c b/src/lj_crecord.c index f686b35f21..80e25ef8a1 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -1125,6 +1125,7 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) if (ctype_isfunc(info)) { TRef func = emitir(IRT(IR_FLOAD, tp), J->base[0], IRFL_CDATA_PTR); CType *ctr = ctype_rawchild(cts, ct); + CTInfo ctr_info = ctr->info; /* crec_call_args may invalidate ctr. */ IRType t = crec_ct2irt(cts, ctr); TRef tr; TValue tv; @@ -1133,11 +1134,11 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) cdata_getptr(cdataptr(cd), (LJ_64 && tp == IRT_P64) ? 8 : 4)); if (tvistrue(lj_tab_get(J->L, cts->miscmap, &tv))) lj_trace_err(J, LJ_TRERR_BLACKL); - if (ctype_isvoid(ctr->info)) { + if (ctype_isvoid(ctr_info)) { t = IRT_NIL; rd->nres = 0; - } else if (!(ctype_isnum(ctr->info) || ctype_isptr(ctr->info) || - ctype_isenum(ctr->info)) || t == IRT_CDATA) { + } else if (!(ctype_isnum(ctr_info) || ctype_isptr(ctr_info) || + ctype_isenum(ctr_info)) || t == IRT_CDATA) { lj_trace_err(J, LJ_TRERR_NYICALL); } if ((info & CTF_VARARG) @@ -1148,7 +1149,7 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) func = emitir(IRT(IR_CARG, IRT_NIL), func, lj_ir_kint(J, ctype_typeid(cts, ct))); tr = emitir(IRT(IR_CALLXS, t), crec_call_args(J, rd, cts, ct), func); - if (ctype_isbool(ctr->info)) { + if (ctype_isbool(ctr_info)) { if (frame_islua(J->L->base-1) && bc_b(frame_pc(J->L->base-1)[-1]) == 1) { /* Don't check result if ignored. */ tr = TREF_NIL; @@ -1164,7 +1165,7 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) tr = TREF_TRUE; } } else if (t == IRT_PTR || (LJ_64 && t == IRT_P32) || - t == IRT_I64 || t == IRT_U64 || ctype_isenum(ctr->info)) { + t == IRT_I64 || t == IRT_U64 || ctype_isenum(ctr_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); From e3fa3c48d8a4aadcf86429e9f7f6f1171914b15a Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 24 Jul 2025 15:35:56 +0200 Subject: [PATCH 14/21] Avoid out-of-range PC for stack overflow error from snapshot restore. Reported by Sergey Kaplun. #1369 --- src/lj_debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lj_debug.c b/src/lj_debug.c index a639cddf8f..f340964917 100644 --- a/src/lj_debug.c +++ b/src/lj_debug.c @@ -101,6 +101,7 @@ static BCPos debug_framepc(lua_State *L, GCfunc *fn, cTValue *nextframe) pt = funcproto(fn); pos = proto_bcpos(pt, ins) - 1; #if LJ_HASJIT + if (pos == NO_BCPOS) return 1; /* Pretend it's the first bytecode. */ if (pos > pt->sizebc) { /* Undo the effects of lj_trace_exit for JLOOP. */ if (bc_isret(bc_op(ins[-1]))) { GCtrace *T = (GCtrace *)((char *)(ins-1) - offsetof(GCtrace, startins)); From c92d0cb19263e7e302b4740ba6617a32c201c613 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 24 Jul 2025 15:38:45 +0200 Subject: [PATCH 15/21] x86/x64: Don't use undefined MUL/IMUL zero flag. Reported by VrIgHtEr. #1376 --- src/lj_asm_x86.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h index 239066d4a5..8b6ce47983 100644 --- a/src/lj_asm_x86.h +++ b/src/lj_asm_x86.h @@ -1841,7 +1841,8 @@ static void asm_intarith(ASMState *as, IRIns *ir, x86Arith xa) RegSet allow = RSET_GPR; Reg dest, right; int32_t k = 0; - if (as->flagmcp == as->mcp) { /* Drop test r,r instruction. */ + if (as->flagmcp == as->mcp && xa != XOg_X_IMUL) { + /* Drop test r,r instruction. */ MCode *p = as->mcp + ((LJ_64 && *as->mcp < XI_TESTb) ? 3 : 2); MCode *q = p[0] == 0x0f ? p+1 : p; if ((*q & 15) < 14) { From 871db2c84ecefd70a850e03a6c340214a81739f0 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 24 Jul 2025 15:45:24 +0200 Subject: [PATCH 16/21] Windows: Add lua52compat option to msvcbuild.bat. Thanks to Gil Reis. #1366 --- src/msvcbuild.bat | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat index 69c0c61a9f..d6aed17009 100644 --- a/src/msvcbuild.bat +++ b/src/msvcbuild.bat @@ -5,11 +5,12 @@ @rem Then cd to this directory and run this script. Use the following @rem options (in order), if needed. The default is a dynamic release build. @rem -@rem nogc64 disable LJ_GC64 mode for x64 -@rem debug emit debug symbols -@rem amalg amalgamated build -@rem static create static lib to statically link into your project -@rem mixed create static lib to build a DLL in your project +@rem nogc64 disable LJ_GC64 mode for x64 +@rem debug emit debug symbols +@rem lua52compat enable extra Lua 5.2 extensions +@rem amalg amalgamated build +@rem static create static lib to statically link into your project +@rem mixed create static lib to build a DLL in your project @if not defined INCLUDE goto :FAIL @@ -101,6 +102,10 @@ buildvm -m folddef -o lj_folddef.h lj_opt_fold.c @set LJDYNBUILD=%LJDYNBUILD_DEBUG% @set LJLINKTYPE=%LJLINKTYPE_DEBUG% :NODEBUG +@if "%1" neq "lua52compat" goto :NOLUA52COMPAT +@shift +@set LJCOMPILE=%LJCOMPILE% /DLUAJIT_ENABLE_LUA52COMPAT +:NOLUA52COMPAT @set LJCOMPILE=%LJCOMPILE% %LJCOMPILETARGET% @set LJLINK=%LJLINK% %LJLINKTYPE% %LJLINKTARGET% @if "%1"=="amalg" goto :AMALGDLL From 54a162688ed25902122077149df9b456bc5a763e Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 16 Oct 2025 13:11:02 +0200 Subject: [PATCH 17/21] Fix reporting of an error during error handling. Reported by Sergey Kaplun. #1381 --- src/lj_err.c | 10 ++++++++++ src/lj_state.c | 1 + 2 files changed, 11 insertions(+) diff --git a/src/lj_err.c b/src/lj_err.c index 03b5030be6..e8e1875805 100644 --- a/src/lj_err.c +++ b/src/lj_err.c @@ -803,9 +803,17 @@ LJ_NOINLINE GCstr *lj_err_str(lua_State *L, ErrMsg em) return lj_str_newz(L, err2msg(em)); } +LJ_NORET LJ_NOINLINE static void lj_err_err(lua_State *L) +{ + setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRERR)); + lj_err_throw(L, LUA_ERRERR); +} + /* Out-of-memory error. */ LJ_NOINLINE void lj_err_mem(lua_State *L) { + if (L->status == LUA_ERRERR) + lj_err_err(L); if (L->status == LUA_ERRERR+1) /* Don't touch the stack during lua_open. */ lj_vm_unwind_c(L->cframe, LUA_ERRMEM); if (LJ_HASJIT) { @@ -902,6 +910,8 @@ LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L) /* Stack overflow error. */ void LJ_FASTCALL lj_err_stkov(lua_State *L) { + if (L->status == LUA_ERRERR) + lj_err_err(L); lj_debug_addloc(L, err2msg(LJ_ERR_STKOV), L->base-1, NULL); lj_err_run(L); } diff --git a/src/lj_state.c b/src/lj_state.c index d8fc545a0d..3cad8cc184 100644 --- a/src/lj_state.c +++ b/src/lj_state.c @@ -195,6 +195,7 @@ static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud) lj_meta_init(L); lj_lex_init(L); fixstring(lj_err_str(L, LJ_ERR_ERRMEM)); /* Preallocate memory error msg. */ + fixstring(lj_err_str(L, LJ_ERR_ERRERR)); /* Preallocate err in err msg. */ g->gc.threshold = 4*g->gc.total; #if LJ_HASFFI lj_ctype_initfin(L); From a69aef43fe1838da26c193d188580229b2387583 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 16 Oct 2025 13:13:51 +0200 Subject: [PATCH 18/21] Fix io.write() of newly created buffer. Reported by vfprintf. #1386 --- src/lj_strfmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lj_strfmt.c b/src/lj_strfmt.c index 04aebaa472..bb649fc840 100644 --- a/src/lj_strfmt.c +++ b/src/lj_strfmt.c @@ -170,7 +170,7 @@ const char *lj_strfmt_wstrnum(lua_State *L, cTValue *o, MSize *lenp) } else if (tvisbuf(o)) { SBufExt *sbx = bufV(o); *lenp = sbufxlen(sbx); - return sbx->r; + return sbx->r ? sbx->r : ""; } else if (tvisint(o)) { sb = lj_strfmt_putint(lj_buf_tmp_(L), intV(o)); } else if (tvisnum(o)) { From a21ba1c9b5218ef83eb8bc6d374764da84f77ffd Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 16 Oct 2025 13:17:45 +0200 Subject: [PATCH 19/21] Add GNU/Hurd build support. Note: this is not an officially supported target. Contributed by Pino Toscano and Samuel Thibault. #1383 #1384 --- src/Makefile | 3 +++ src/lj_arch.h | 3 +++ src/lj_prng.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c83abfa0b6..5dd98a31f6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -357,6 +357,9 @@ else ifeq (GNU/kFreeBSD,$(TARGET_SYS)) TARGET_XLIBS+= -ldl endif + ifeq (GNU,$(TARGET_SYS)) + TARGET_XLIBS+= -ldl + endif endif endif endif diff --git a/src/lj_arch.h b/src/lj_arch.h index a4eecf27e0..865bfa2322 100644 --- a/src/lj_arch.h +++ b/src/lj_arch.h @@ -96,6 +96,9 @@ #elif defined(__QNX__) #define LJ_TARGET_QNX 1 #define LUAJIT_OS LUAJIT_OS_POSIX +#elif defined(__GNU__) +#define LJ_TARGET_HURD 1 +#define LUAJIT_OS LUAJIT_OS_POSIX #else #define LUAJIT_OS LUAJIT_OS_OTHER #endif diff --git a/src/lj_prng.c b/src/lj_prng.c index 02146b273a..1bbb7eaba3 100644 --- a/src/lj_prng.c +++ b/src/lj_prng.c @@ -125,7 +125,7 @@ static PRGR libfunc_rgr; #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 #define LJ_TARGET_HAS_GETENTROPY 1 #endif -#elif (LJ_TARGET_BSD && !defined(__NetBSD__)) || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN || LJ_TARGET_QNX +#elif (LJ_TARGET_BSD && !defined(__NetBSD__)) || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN || LJ_TARGET_QNX || LJ_TARGET_HURD #define LJ_TARGET_HAS_GETENTROPY 1 #endif From 5c3254d68d2579bf8c5bd1e39e612582fb5a04f6 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 16 Oct 2025 13:23:51 +0200 Subject: [PATCH 20/21] Gracefully handle broken custom allocator. Reported by Alex Orlenko. #1393 --- src/lj_state.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lj_state.c b/src/lj_state.c index 3cad8cc184..fb6d41a5f9 100644 --- a/src/lj_state.c +++ b/src/lj_state.c @@ -261,7 +261,11 @@ LUA_API lua_State *lua_newstate(lua_Alloc allocf, void *allocd) } #endif GG = (GG_State *)allocf(allocd, NULL, 0, sizeof(GG_State)); - if (GG == NULL || !checkptrGC(GG)) return NULL; + if (GG == NULL) return NULL; + if (!checkptrGC(GG)) { + allocf(allocd, GG, sizeof(GG_State), 0); + return NULL; + } memset(GG, 0, sizeof(GG_State)); L = &GG->L; g = &GG->g; From 25a61a182166fec06f1a1a025eb8fabbb6cf483e Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 16 Oct 2025 14:24:52 +0200 Subject: [PATCH 21/21] x64: Add support for CET IBT. Note: this is not enabled by default, look for CET in lj_arch.h Contributed by Yuichiro Naito. #1391 --- src/Makefile | 4 ++++ src/jit/dis_x86.lua | 20 +++++++++++++++- src/lj_arch.h | 11 +++++++++ src/lj_asm.c | 3 +++ src/lj_emit_x86.h | 7 ++++++ src/lj_target_x86.h | 3 +++ src/vm_x64.dasc | 57 ++++++++++++++++++++++++++++++++++++++------- 7 files changed, 95 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5dd98a31f6..d23e0db255 100644 --- a/src/Makefile +++ b/src/Makefile @@ -446,6 +446,10 @@ ifneq (,$(findstring LJ_ABI_PAUTH 1,$(TARGET_TESTARCH))) DASM_AFLAGS+= -D PAUTH TARGET_ARCH+= -DLJ_ABI_PAUTH=1 endif +ifneq (,$(findstring LJ_CET_BR 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D CET_BR + TARGET_ARCH+= -DLJ_CET_BR=1 +endif DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH)))) ifeq (Windows,$(TARGET_SYS)) DASM_AFLAGS+= -D WIN diff --git a/src/jit/dis_x86.lua b/src/jit/dis_x86.lua index b1de0eeae1..6b04ee8495 100644 --- a/src/jit/dis_x86.lua +++ b/src/jit/dis_x86.lua @@ -122,7 +122,7 @@ local map_opc2 = { "movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm", "movhpsXmr||movhpdXmr", "$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm", -"hintnopVm","hintnopVm","hintnopVm","hintnopVm", +"hintnopVm","hintnopVm","endbr*hintnopVm","hintnopVm", --2x "movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil, "movapsXrm||movapdXrm", @@ -804,6 +804,24 @@ map_act = { return dispatch(ctx, map_opcvm[ctx.mrm]) end, + -- Special NOP for endbr64/endbr32. + endbr = function(ctx, name, pat) + if ctx.rep then + local pos = ctx.pos + local b = byte(ctx.code, pos) + local text + if b == 0xfa then text = "endbr64" + elseif b == 0xfb then text = "endbr64" + end + if text then + ctx.pos = pos + 1 + ctx.rep = nil + return putop(ctx, text) + end + end + return dispatch(ctx, pat) + end, + -- Floating point opcode dispatch. fp = function(ctx, name, pat) local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end diff --git a/src/lj_arch.h b/src/lj_arch.h index 865bfa2322..42c65879bd 100644 --- a/src/lj_arch.h +++ b/src/lj_arch.h @@ -219,6 +219,17 @@ #error "macOS requires GC64 -- don't disable it" #endif +#if (__CET__ & 1) && defined(LUAJIT_ENABLE_CET_BR) +/* +** Control-Flow Enforcement Technique (CET) indirect branch tracking (IBT). +** This is not enabled by default because it causes a notable slowdown of +** the interpreter on all x64 CPUs, whether they have CET enabled or not. +** If your toolchain enables -fcf-protection=branch by default, you need +** to build with: make XCFLAGS=-DLUAJIT_ENABLE_CET_BR +*/ +#define LJ_CET_BR 1 +#endif + #elif LUAJIT_TARGET == LUAJIT_ARCH_ARM #define LJ_ARCH_NAME "arm" diff --git a/src/lj_asm.c b/src/lj_asm.c index fec4351251..e7f3ec1cd5 100644 --- a/src/lj_asm.c +++ b/src/lj_asm.c @@ -2586,6 +2586,9 @@ void lj_asm_trace(jit_State *J, GCtrace *T) asm_head_side(as); else asm_head_root(as); +#if LJ_CET_BR + emit_endbr(as); +#endif asm_phi_fixup(as); if (J->curfinal->nins >= T->nins) { /* IR didn't grow? */ diff --git a/src/lj_emit_x86.h b/src/lj_emit_x86.h index f477301162..848301bce1 100644 --- a/src/lj_emit_x86.h +++ b/src/lj_emit_x86.h @@ -70,6 +70,13 @@ static LJ_AINLINE MCode *emit_op(x86Op xo, Reg rr, Reg rb, Reg rx, return p; } +#if LJ_CET_BR +static void emit_endbr(ASMState *as) +{ + emit_u32(as, XI_ENDBR64); +} +#endif + /* op + modrm */ #define emit_opm(xo, mode, rr, rb, p, delta) \ (p[(delta)-1] = MODRM((mode), (rr), (rb)), \ diff --git a/src/lj_target_x86.h b/src/lj_target_x86.h index 6a528e8288..fa32a5d46f 100644 --- a/src/lj_target_x86.h +++ b/src/lj_target_x86.h @@ -242,6 +242,9 @@ typedef enum { XV_SHLX = XV_660f38(f7), XV_SHRX = XV_f20f38(f7), + /* Special NOP instructions. */ + XI_ENDBR64 = 0xfa1e0ff3, + /* Variable-length opcodes. XO_* prefix. */ XO_OR = XO_(0b), XO_MOV = XO_(8b), diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc index f501495b11..52ef88af42 100644 --- a/src/vm_x64.dasc +++ b/src/vm_x64.dasc @@ -189,16 +189,24 @@ | |.endif | +|//-- Control-Flow Enforcement Technique (CET) --------------------------- +| +|.if CET_BR +|.macro endbr; endbr64; .endmacro +|.else +|.macro endbr; .endmacro +|.endif +| |//----------------------------------------------------------------------- | |// Instruction headers. -|.macro ins_A; .endmacro -|.macro ins_AD; .endmacro -|.macro ins_AJ; .endmacro -|.macro ins_ABC; movzx RBd, RCH; movzx RCd, RCL; .endmacro -|.macro ins_AB_; movzx RBd, RCH; .endmacro -|.macro ins_A_C; movzx RCd, RCL; .endmacro -|.macro ins_AND; not RD; .endmacro +|.macro ins_A; endbr; .endmacro +|.macro ins_AD; endbr; .endmacro +|.macro ins_AJ; endbr; .endmacro +|.macro ins_ABC; endbr; movzx RBd, RCH; movzx RCd, RCL; .endmacro +|.macro ins_AB_; endbr; movzx RBd, RCH; .endmacro +|.macro ins_A_C; endbr; movzx RCd, RCL; .endmacro +|.macro ins_AND; endbr; not RD; .endmacro | |// Instruction decode+dispatch. Carefully tuned (nope, lodsd is not faster). |.macro ins_NEXT @@ -479,20 +487,24 @@ static void build_subroutines(BuildCtx *ctx) | jmp <3 | |->vm_unwind_yield: + | endbr | mov al, LUA_YIELD | jmp ->vm_unwind_c_eh | |->vm_unwind_c: // Unwind C stack, return from vm_pcall. + | endbr | // (void *cframe, int errcode) | mov eax, CARG2d // Error return status for vm_pcall. | mov rsp, CARG1 |->vm_unwind_c_eh: // Landing pad for external unwinder. + | endbr | mov L:RB, SAVE_L | mov GL:RB, L:RB->glref | mov dword GL:RB->vmstate, ~LJ_VMST_C | jmp ->vm_leave_unw | |->vm_unwind_rethrow: + | endbr |.if not X64WIN | mov CARG1, SAVE_L | mov CARG2d, eax @@ -501,10 +513,12 @@ static void build_subroutines(BuildCtx *ctx) |.endif | |->vm_unwind_ff: // Unwind C stack, return from ff pcall. + | endbr | // (void *cframe) | and CARG1, CFRAME_RAWMASK | mov rsp, CARG1 |->vm_unwind_ff_eh: // Landing pad for external unwinder. + | endbr | mov L:RB, SAVE_L | mov RDd, 1+1 // Really 1+2 results, incr. later. | mov BASE, L:RB->base @@ -675,6 +689,7 @@ static void build_subroutines(BuildCtx *ctx) |//-- Continuation dispatch ---------------------------------------------- | |->cont_dispatch: + | endbr | // BASE = meta base, RA = resultofs, RD = nresults+1 (also in MULTRES) | add RA, BASE | and PC, -8 @@ -706,6 +721,7 @@ static void build_subroutines(BuildCtx *ctx) |.endif | |->cont_cat: // BASE = base, RC = result, RB = mbase + | endbr | movzx RAd, PC_RB | sub RB, 32 | lea RA, [BASE+RA*8] @@ -774,6 +790,7 @@ static void build_subroutines(BuildCtx *ctx) | test RC, RC | jz >3 |->cont_ra: // BASE = base, RC = result + | endbr | movzx RAd, PC_RA | mov RB, [RC] | mov [BASE+RA*8], RB @@ -851,6 +868,7 @@ static void build_subroutines(BuildCtx *ctx) | mov RB, [BASE+RA*8] | mov [RC], RB |->cont_nop: // BASE = base, (RC = result) + | endbr | ins_next | |3: // Call __newindex metamethod. @@ -921,6 +939,7 @@ static void build_subroutines(BuildCtx *ctx) | ins_next | |->cont_condt: // BASE = base, RC = result + | endbr | add PC, 4 | mov ITYPE, [RC] | sar ITYPE, 47 @@ -929,6 +948,7 @@ static void build_subroutines(BuildCtx *ctx) | jmp <6 | |->cont_condf: // BASE = base, RC = result + | endbr | mov ITYPE, [RC] | sar ITYPE, 47 | cmp ITYPEd, LJ_TISTRUECOND // Branch if result is false. @@ -1132,16 +1152,17 @@ static void build_subroutines(BuildCtx *ctx) | |.macro .ffunc, name |->ff_ .. name: + | endbr |.endmacro | |.macro .ffunc_1, name |->ff_ .. name: - | cmp NARGS:RDd, 1+1; jb ->fff_fallback + | endbr; cmp NARGS:RDd, 1+1; jb ->fff_fallback |.endmacro | |.macro .ffunc_2, name |->ff_ .. name: - | cmp NARGS:RDd, 2+1; jb ->fff_fallback + | endbr; cmp NARGS:RDd, 2+1; jb ->fff_fallback |.endmacro | |.macro .ffunc_n, name, op @@ -2207,6 +2228,7 @@ static void build_subroutines(BuildCtx *ctx) | |->vm_record: // Dispatch target for recording phase. |.if JIT + | endbr | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)] | test RDL, HOOK_VMEVENT // No recording while in vmevent. | jnz >5 @@ -2220,12 +2242,14 @@ static void build_subroutines(BuildCtx *ctx) |.endif | |->vm_rethook: // Dispatch target for return hooks. + | endbr | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)] | test RDL, HOOK_ACTIVE // Hook already active? | jnz >5 | jmp >1 | |->vm_inshook: // Dispatch target for instr/line hooks. + | endbr | movzx RDd, byte [DISPATCH+DISPATCH_GL(hookmask)] | test RDL, HOOK_ACTIVE // Hook already active? | jnz >5 @@ -2253,6 +2277,7 @@ static void build_subroutines(BuildCtx *ctx) | jmp aword [DISPATCH+OP*8+GG_DISP2STATIC] // Re-dispatch to static ins. | |->cont_hook: // Continue from hook yield. + | endbr | add PC, 4 | mov RA, [RB-40] | mov MULTRES, RAd // Restore MULTRES for *M ins. @@ -2277,6 +2302,7 @@ static void build_subroutines(BuildCtx *ctx) |.endif | |->vm_callhook: // Dispatch target for call hooks. + | endbr | mov SAVE_PC, PC |.if JIT | jmp >1 @@ -2312,6 +2338,7 @@ static void build_subroutines(BuildCtx *ctx) | |->cont_stitch: // Trace stitching. |.if JIT + | endbr | // BASE = base, RC = result, RB = mbase | mov TRACE:ITYPE, [RB-40] // Save previous trace. | cleartp TRACE:ITYPE @@ -2364,6 +2391,7 @@ static void build_subroutines(BuildCtx *ctx) | |->vm_profhook: // Dispatch target for profiler hook. #if LJ_HASPROFILE + | endbr | mov L:RB, SAVE_L | mov L:RB->base, BASE | mov CARG2, PC // Caveat: CARG2 == BASE @@ -2383,6 +2411,7 @@ static void build_subroutines(BuildCtx *ctx) |// The 16 bit exit number is stored with two (sign-extended) push imm8. |->vm_exit_handler: |.if JIT + | endbr | push r13; push r12 | push r11; push r10; push r9; push r8 | push rdi; push rsi; push rbp; lea rbp, [rsp+88]; push rbp @@ -2431,6 +2460,7 @@ static void build_subroutines(BuildCtx *ctx) | jmp >1 |.endif |->vm_exit_interp: + | endbr | // RD = MULTRES or negated error code, BASE, PC and DISPATCH set. |.if JIT | // Restore additional callee-save registers only used in compiled code. @@ -2524,6 +2554,7 @@ static void build_subroutines(BuildCtx *ctx) |.macro vm_round, name, mode, cond |->name: |->name .. _sse: + | endbr | sseconst_abs xmm2, RD | sseconst_2p52 xmm3, RD | movaps xmm1, xmm0 @@ -2634,6 +2665,7 @@ static void build_subroutines(BuildCtx *ctx) |// Next idx returned in edx. |->vm_next: |.if JIT + | endbr | mov NEXT_ASIZE, NEXT_TAB->asize |1: // Traverse array part. | cmp NEXT_IDX, NEXT_ASIZE; jae >5 @@ -4087,6 +4119,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) case BC_ITERN: |.if JIT + | endbr | hotloop RBd |.endif |->vm_IITERN: @@ -4266,6 +4299,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | jnz >7 // Not returning to a fixarg Lua func? switch (op) { case BC_RET: + | endbr |->BC_RET_Z: | mov KBASE, BASE // Use KBASE for result move. | sub RDd, 1 @@ -4284,10 +4318,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ja >6 break; case BC_RET1: + | endbr | mov RB, [BASE+RA] | mov [BASE-16], RB /* fallthrough */ case BC_RET0: + | endbr |5: | cmp PC_RB, RDL // More results expected? | ja >6 @@ -4334,6 +4370,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) case BC_FORL: |.if JIT + | endbr | hotloop RBd |.endif | // Fall through. Assumes BC_IFORL follows and ins_AJ is a no-op. @@ -4485,6 +4522,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) case BC_ITERL: |.if JIT + | endbr | hotloop RBd |.endif | // Fall through. Assumes BC_IITERL follows and ins_AJ is a no-op. @@ -4578,6 +4616,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) case BC_FUNCF: |.if JIT + | endbr | hotcall RBd |.endif case BC_FUNCV: /* NYI: compiled vararg functions. */