[Tarantool-patches] [PATCH luajit 1/2] Add support for full-range 64 bit lightuserdata.
Sergey Kaplun
skaplun at tarantool.org
Tue Jul 6 20:40:05 MSK 2021
From: Mike Pall <mike>
(cherry picked from commit e9af1abec542e6f9851ff2368e7f196b6382a44c)
LuaJIT uses special NaN-tagging technique to store internal type on
the Lua stack. In case LJ_GC64 first 13 bits are set in special NaN
type (0xfff8...). FPU generates the only one type. The next 4 bits are
used for an internal LuaJIT type of object on stack. The next 47 bits
are used for storing this object's content. For userdata, it is its
address. In case arm64 the pointer can have more than 47 significant
bits [1]. In this case the error BADLU error is raised.
For the support of full 64-bit range lightuserdata pointers two new
fields in GCState are added:
`lightudseg` - vector of segments of lightuserdata storing 32-bit
values. MS 25 bits equal to MS bits of lightuserdata address, the
rest are filled with zeros. The lentgh of the vector is power of 2.
`lightudnum` - the length - 1 of aforementioned vector (up to 255).
When lightuserdata is pushed on the stack, if its segment is not stored
in vector new value is pushed on top. The maximum amount of segments is
256, BADLU error is raised in case when user tried to add userdata with
the new 257-th segment.
Also, in this patch all internal usage of lightuserdata (for hooks,
profilers, built-in package, ir and so on) is changed to special values
on Lua Stack.
Also, conversion of TValue to ffi C type with store is no longer
compiled for lightuserdata.
[1]: https://www.kernel.org/doc/html/latest/arm64/memory.html
Sergey Kaplun:
* added the description and the test for the problem
Needed for tarantool/tarantool#6154
Resolves tarantool/tarantool#2712
---
doc/status.html | 11 ----
src/jit/dump.lua | 4 +-
src/lib_debug.c | 12 ++--
src/lib_jit.c | 14 ++---
src/lib_package.c | 8 +--
src/lib_string.c | 2 +-
src/lj_api.c | 40 +++++++++++--
src/lj_ccall.c | 2 +-
src/lj_cconv.c | 2 +-
src/lj_crecord.c | 6 +-
src/lj_dispatch.c | 2 +-
src/lj_ir.c | 6 +-
src/lj_obj.c | 5 +-
src/lj_obj.h | 57 ++++++++++++-------
src/lj_snap.c | 7 ++-
src/lj_state.c | 6 ++
src/lj_strfmt.c | 2 +-
test/tarantool-tests/CMakeLists.txt | 1 +
.../lj-49-bad-lightuserdata.test.lua | 10 ++++
.../lj-49-bad-lightuserdata/CMakeLists.txt | 1 +
.../testlightuserdata.c | 52 +++++++++++++++++
21 files changed, 183 insertions(+), 67 deletions(-)
create mode 100644 test/tarantool-tests/lj-49-bad-lightuserdata.test.lua
create mode 100644 test/tarantool-tests/lj-49-bad-lightuserdata/CMakeLists.txt
create mode 100644 test/tarantool-tests/lj-49-bad-lightuserdata/testlightuserdata.c
diff --git a/doc/status.html b/doc/status.html
index cad6ca65..c89255b6 100644
--- a/doc/status.html
+++ b/doc/status.html
@@ -97,17 +97,6 @@ handled correctly. The error may fall through an on-trace
<tt>lua_atpanic</tt> on x64. This issue will be fixed with the new
garbage collector.
</li>
-<li>
-LuaJIT on 64 bit systems provides a <b>limited range</b> of 47 bits for the
-<b>legacy <tt>lightuserdata</tt></b> data type.
-This is only relevant on x64 systems which use the negative part of the
-virtual address space in user mode, e.g. Solaris/x64, and on ARM64 systems
-configured with a 48 bit or 52 bit VA.
-Avoid using <tt>lightuserdata</tt> to hold pointers that may point outside
-of that range, e.g. variables on the stack. In general, avoid this data
-type for new code and replace it with (much more performant) FFI bindings.
-FFI cdata pointers can address the full 64 bit range.
-</li>
</ul>
<br class="flush">
</div>
diff --git a/src/jit/dump.lua b/src/jit/dump.lua
index 2bea652b..27b5c2ae 100644
--- a/src/jit/dump.lua
+++ b/src/jit/dump.lua
@@ -315,7 +315,9 @@ local function formatk(tr, idx, sn)
local tn = type(k)
local s
if tn == "number" then
- if band(sn or 0, 0x30000) ~= 0 then
+ if t < 12 then
+ s = k == 0 and "NULL" or format("[0x%08x]", k)
+ elseif band(sn or 0, 0x30000) ~= 0 then
s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
elseif k == 2^52+2^51 then
s = "bias"
diff --git a/src/lib_debug.c b/src/lib_debug.c
index f112b5bc..8fdfda03 100644
--- a/src/lib_debug.c
+++ b/src/lib_debug.c
@@ -231,8 +231,8 @@ LJLIB_CF(debug_upvalueid)
int32_t n = lj_lib_checkint(L, 2) - 1;
if ((uint32_t)n >= fn->l.nupvalues)
lj_err_arg(L, 2, LJ_ERR_IDXRNG);
- setlightudV(L->top-1, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) :
- (void *)&fn->c.upvalue[n]);
+ lua_pushlightuserdata(L, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) :
+ (void *)&fn->c.upvalue[n]);
return 1;
}
@@ -283,13 +283,13 @@ LJLIB_CF(debug_setuservalue)
/* ------------------------------------------------------------------------ */
-#define KEY_HOOK ((void *)0x3004)
+#define KEY_HOOK (U64x(80000000,00000000)|'h')
static void hookf(lua_State *L, lua_Debug *ar)
{
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail return"};
- lua_pushlightuserdata(L, KEY_HOOK);
+ (L->top++)->u64 = KEY_HOOK;
lua_rawget(L, LUA_REGISTRYINDEX);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
@@ -334,7 +334,7 @@ LJLIB_CF(debug_sethook)
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
- lua_pushlightuserdata(L, KEY_HOOK);
+ (L->top++)->u64 = KEY_HOOK;
lua_pushvalue(L, arg+1);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_sethook(L, func, mask, count);
@@ -349,7 +349,7 @@ LJLIB_CF(debug_gethook)
if (hook != NULL && hook != hookf) { /* external hook? */
lua_pushliteral(L, "external hook");
} else {
- lua_pushlightuserdata(L, KEY_HOOK);
+ (L->top++)->u64 = KEY_HOOK;
lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */
}
lua_pushstring(L, unmakemask(mask, buff));
diff --git a/src/lib_jit.c b/src/lib_jit.c
index 22ca0a1a..40aa2b51 100644
--- a/src/lib_jit.c
+++ b/src/lib_jit.c
@@ -540,15 +540,15 @@ LJLIB_CF(jit_opt_start)
/* Not loaded by default, use: local profile = require("jit.profile") */
-static const char KEY_PROFILE_THREAD = 't';
-static const char KEY_PROFILE_FUNC = 'f';
+#define KEY_PROFILE_THREAD (U64x(80000000,00000000)|'t')
+#define KEY_PROFILE_FUNC (U64x(80000000,00000000)|'f')
static void jit_profile_callback(lua_State *L2, lua_State *L, int samples,
int vmstate)
{
TValue key;
cTValue *tv;
- setlightudV(&key, (void *)&KEY_PROFILE_FUNC);
+ key.u64 = KEY_PROFILE_FUNC;
tv = lj_tab_get(L, tabV(registry(L)), &key);
if (tvisfunc(tv)) {
char vmst = (char)vmstate;
@@ -575,9 +575,9 @@ LJLIB_CF(jit_profile_start)
lua_State *L2 = lua_newthread(L); /* Thread that runs profiler callback. */
TValue key;
/* Anchor thread and function in registry. */
- setlightudV(&key, (void *)&KEY_PROFILE_THREAD);
+ key.u64 = KEY_PROFILE_THREAD;
setthreadV(L, lj_tab_set(L, registry, &key), L2);
- setlightudV(&key, (void *)&KEY_PROFILE_FUNC);
+ key.u64 = KEY_PROFILE_FUNC;
setfuncV(L, lj_tab_set(L, registry, &key), func);
lj_gc_anybarriert(L, registry);
luaJIT_profile_start(L, mode ? strdata(mode) : "",
@@ -592,9 +592,9 @@ LJLIB_CF(jit_profile_stop)
TValue key;
luaJIT_profile_stop(L);
registry = tabV(registry(L));
- setlightudV(&key, (void *)&KEY_PROFILE_THREAD);
+ key.u64 = KEY_PROFILE_THREAD;
setnilV(lj_tab_set(L, registry, &key));
- setlightudV(&key, (void *)&KEY_PROFILE_FUNC);
+ key.u64 = KEY_PROFILE_FUNC;
setnilV(lj_tab_set(L, registry, &key));
lj_gc_anybarriert(L, registry);
return 0;
diff --git a/src/lib_package.c b/src/lib_package.c
index a0bca572..8573b9f9 100644
--- a/src/lib_package.c
+++ b/src/lib_package.c
@@ -412,7 +412,7 @@ static int lj_cf_package_loader_preload(lua_State *L)
/* ------------------------------------------------------------------------ */
-#define sentinel ((void *)0x4004)
+#define KEY_SENTINEL (U64x(80000000,00000000)|'s')
static int lj_cf_package_require(lua_State *L)
{
@@ -422,7 +422,7 @@ static int lj_cf_package_require(lua_State *L)
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
- if (lua_touserdata(L, -1) == sentinel) /* check loops */
+ if ((L->top-1)->u64 == KEY_SENTINEL) /* check loops */
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
return 1; /* package is already loaded */
}
@@ -445,14 +445,14 @@ static int lj_cf_package_require(lua_State *L)
else
lua_pop(L, 1);
}
- lua_pushlightuserdata(L, sentinel);
+ (L->top++)->u64 = KEY_SENTINEL;
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
lua_call(L, 1, 1); /* run loaded module */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
lua_getfield(L, 2, name);
- if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
+ if ((L->top-1)->u64 == KEY_SENTINEL) { /* module did not set a value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_setfield(L, 2, name); /* _LOADED[name] = true */
diff --git a/src/lib_string.c b/src/lib_string.c
index 76b0730a..156dae66 100644
--- a/src/lib_string.c
+++ b/src/lib_string.c
@@ -714,7 +714,7 @@ again:
lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg));
break;
case STRFMT_PTR: /* No formatting. */
- lj_strfmt_putptr(sb, lj_obj_ptr(L->base+arg-1));
+ lj_strfmt_putptr(sb, lj_obj_ptr(G(L), L->base+arg-1));
break;
default:
lua_assert(0);
diff --git a/src/lj_api.c b/src/lj_api.c
index c9b5d220..c7a0b327 100644
--- a/src/lj_api.c
+++ b/src/lj_api.c
@@ -617,7 +617,7 @@ LUA_API void *lua_touserdata(lua_State *L, int idx)
if (tvisudata(o))
return uddata(udataV(o));
else if (tvislightud(o))
- return lightudV(o);
+ return lightudV(G(L), o);
else
return NULL;
}
@@ -630,7 +630,7 @@ LUA_API lua_State *lua_tothread(lua_State *L, int idx)
LUA_API const void *lua_topointer(lua_State *L, int idx)
{
- return lj_obj_ptr(index2adr(L, idx));
+ return lj_obj_ptr(G(L), index2adr(L, idx));
}
/* -- Stack setters (object creation) ------------------------------------- */
@@ -716,9 +716,38 @@ LUA_API void lua_pushboolean(lua_State *L, int b)
incr_top(L);
}
+#if LJ_64
+static void *lightud_intern(lua_State *L, void *p)
+{
+ global_State *g = G(L);
+ uint64_t u = (uint64_t)p;
+ uint32_t up = lightudup(u);
+ uint32_t *segmap = mref(g->gc.lightudseg, uint32_t);
+ MSize segnum = g->gc.lightudnum;
+ if (segmap) {
+ MSize seg;
+ for (seg = 0; seg <= segnum; seg++)
+ if (segmap[seg] == up) /* Fast path. */
+ return (void *)(((uint64_t)seg << LJ_LIGHTUD_BITS_LO) | lightudlo(u));
+ segnum++;
+ }
+ if (!((segnum-1) & segnum) && segnum != 1) {
+ if (segnum >= (1 << LJ_LIGHTUD_BITS_SEG)) lj_err_msg(L, LJ_ERR_BADLU);
+ lj_mem_reallocvec(L, segmap, segnum, segnum ? 2*segnum : 2u, uint32_t);
+ setmref(g->gc.lightudseg, segmap);
+ }
+ g->gc.lightudnum = segnum;
+ segmap[segnum] = up;
+ return (void *)(((uint64_t)segnum << LJ_LIGHTUD_BITS_LO) | lightudlo(u));
+}
+#endif
+
LUA_API void lua_pushlightuserdata(lua_State *L, void *p)
{
- setlightudV(L->top, checklightudptr(L, p));
+#if LJ_64
+ p = lightud_intern(L, p);
+#endif
+ setrawlightudV(L->top, p);
incr_top(L);
}
@@ -1167,7 +1196,10 @@ static TValue *cpcall(lua_State *L, lua_CFunction func, void *ud)
fn->c.f = func;
setfuncV(L, top++, fn);
if (LJ_FR2) setnilV(top++);
- setlightudV(top++, checklightudptr(L, ud));
+#if LJ_64
+ ud = lightud_intern(L, ud);
+#endif
+ setrawlightudV(top++, ud);
cframe_nres(L->cframe) = 1+0; /* Zero results. */
L->top = top;
return top-1; /* Now call the newly allocated C function. */
diff --git a/src/lj_ccall.c b/src/lj_ccall.c
index 06d1b3bd..f26f1fea 100644
--- a/src/lj_ccall.c
+++ b/src/lj_ccall.c
@@ -1150,7 +1150,7 @@ int lj_ccall_func(lua_State *L, GCcdata *cd)
lj_vm_ffi_call(&cc);
if (cts->cb.slot != ~0u) { /* Blacklist function that called a callback. */
TValue tv;
- setlightudV(&tv, (void *)cc.func);
+ 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. */
diff --git a/src/lj_cconv.c b/src/lj_cconv.c
index 13b8230d..ca2a5d30 100644
--- a/src/lj_cconv.c
+++ b/src/lj_cconv.c
@@ -611,7 +611,7 @@ void lj_cconv_ct_tv(CTState *cts, CType *d,
if (ud->udtype == UDTYPE_IO_FILE)
tmpptr = *(void **)tmpptr;
} else if (tvislightud(o)) {
- tmpptr = lightudV(o);
+ tmpptr = lightudV(cts->g, o);
} else if (tvisfunc(o)) {
void *p = lj_ccallback_new(cts, d, funcV(o));
if (p) {
diff --git a/src/lj_crecord.c b/src/lj_crecord.c
index e32ae23e..59186dab 100644
--- a/src/lj_crecord.c
+++ b/src/lj_crecord.c
@@ -643,8 +643,7 @@ static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval)
}
} else if (tref_islightud(sp)) {
#if LJ_64
- sp = emitir(IRT(IR_BAND, IRT_P64), sp,
- lj_ir_kint64(J, U64x(00007fff,ffffffff)));
+ lj_trace_err(J, LJ_TRERR_NYICONV);
#endif
} else { /* NYI: tref_istab(sp). */
IRType t;
@@ -1209,8 +1208,7 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
TRef tr;
TValue tv;
/* Check for blacklisted C functions that might call a callback. */
- setlightudV(&tv,
- cdata_getptr(cdataptr(cd), (LJ_64 && tp == IRT_P64) ? 8 : 4));
+ tv.u64 = ((uintptr_t)cdata_getptr(cdataptr(cd), (LJ_64 && tp == IRT_P64) ? 8 : 4) >> 2) | U64x(800000000, 00000000);
if (tvistrue(lj_tab_get(J->L, cts->miscmap, &tv)))
lj_trace_err(J, LJ_TRERR_BLACKL);
if (ctype_isvoid(ctr->info)) {
diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c
index b694d8fd..ee735450 100644
--- a/src/lj_dispatch.c
+++ b/src/lj_dispatch.c
@@ -308,7 +308,7 @@ int luaJIT_setmode(lua_State *L, int idx, int mode)
if (idx != 0) {
cTValue *tv = idx > 0 ? L->base + (idx-1) : L->top + idx;
if (tvislightud(tv))
- g->wrapf = (lua_CFunction)lightudV(tv);
+ g->wrapf = (lua_CFunction)lightudV(g, tv);
else
return 0; /* Failed. */
} else {
diff --git a/src/lj_ir.c b/src/lj_ir.c
index 5baece67..2f7ddb24 100644
--- a/src/lj_ir.c
+++ b/src/lj_ir.c
@@ -386,8 +386,10 @@ void lj_ir_kvalue(lua_State *L, TValue *tv, const IRIns *ir)
case IR_KPRI: setpriV(tv, irt_toitype(ir->t)); break;
case IR_KINT: setintV(tv, ir->i); break;
case IR_KGC: setgcV(L, tv, ir_kgc(ir), irt_toitype(ir->t)); break;
- case IR_KPTR: case IR_KKPTR: setlightudV(tv, ir_kptr(ir)); break;
- case IR_KNULL: setlightudV(tv, NULL); break;
+ case IR_KPTR: case IR_KKPTR:
+ setnumV(tv, (lua_Number)(uintptr_t)ir_kptr(ir));
+ break;
+ case IR_KNULL: setintV(tv, 0); break;
case IR_KNUM: setnumV(tv, ir_knum(ir)->n); break;
#if LJ_HASFFI
case IR_KINT64: {
diff --git a/src/lj_obj.c b/src/lj_obj.c
index ee33aeb3..746da6ef 100644
--- a/src/lj_obj.c
+++ b/src/lj_obj.c
@@ -34,12 +34,13 @@ int LJ_FASTCALL lj_obj_equal(cTValue *o1, cTValue *o2)
}
/* Return pointer to object or its object data. */
-const void * LJ_FASTCALL lj_obj_ptr(cTValue *o)
+const void * LJ_FASTCALL lj_obj_ptr(global_State *g, cTValue *o)
{
+ UNUSED(g);
if (tvisudata(o))
return uddata(udataV(o));
else if (tvislightud(o))
- return lightudV(o);
+ return lightudV(g, o);
else if (LJ_HASFFI && tviscdata(o))
return cdataptr(cdataV(o));
else if (tvisgcv(o))
diff --git a/src/lj_obj.h b/src/lj_obj.h
index 4a4d77f5..d26e60be 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -232,7 +232,7 @@ typedef const TValue cTValue;
** ---MSW---.---LSW---
** primitive types | itype | |
** lightuserdata | itype | void * | (32 bit platforms)
-** lightuserdata |ffff| void * | (64 bit platforms, 47 bit pointers)
+** lightuserdata |ffff|seg| ofs | (64 bit platforms)
** GC objects | itype | GCRef |
** int (LJ_DUALNUM)| itype | int |
** number -------double------
@@ -245,7 +245,8 @@ typedef const TValue cTValue;
**
** ------MSW------.------LSW------
** primitive types |1..1|itype|1..................1|
-** GC objects/lightud |1..1|itype|-------GCRef--------|
+** GC objects |1..1|itype|-------GCRef--------|
+** lightuserdata |1..1|itype|seg|------ofs-------|
** int (LJ_DUALNUM) |1..1|itype|0..0|-----int-------|
** number ------------double-------------
**
@@ -285,6 +286,12 @@ typedef const TValue cTValue;
#define LJ_GCVMASK (((uint64_t)1 << 47) - 1)
#endif
+#if LJ_64
+/* To stay within 47 bits, lightuserdata is segmented. */
+#define LJ_LIGHTUD_BITS_SEG 8
+#define LJ_LIGHTUD_BITS_LO (47 - LJ_LIGHTUD_BITS_SEG)
+#endif
+
/* -- String object ------------------------------------------------------- */
/* String object header. String payload follows. */
@@ -597,7 +604,11 @@ typedef struct GCState {
uint8_t currentwhite; /* Current white color. */
uint8_t state; /* GC state. */
uint8_t nocdatafin; /* No cdata finalizer called. */
- uint8_t unused2;
+#if LJ_64
+ uint8_t lightudnum; /* Number of lightuserdata segments - 1. */
+#else
+ uint8_t unused1;
+#endif
MSize sweepstr; /* Sweep position in string table. */
GCRef root; /* List of all collectable objects. */
MRef sweep; /* Sweep position in root list. */
@@ -609,6 +620,9 @@ typedef struct GCState {
GCSize estimate; /* Estimate of memory actually in use. */
MSize stepmul; /* Incremental GC step granularity. */
MSize pause; /* Pause between successive GC cycles. */
+#if LJ_64
+ MRef lightudseg; /* Upper bits of lightuserdata segments. */
+#endif
size_t freed; /* Total amount of freed memory. */
size_t allocated; /* Total amount of allocated memory. */
@@ -834,10 +848,23 @@ typedef union GCobj {
#endif
#define boolV(o) check_exp(tvisbool(o), (LJ_TFALSE - itype(o)))
#if LJ_64
-#define lightudV(o) \
- check_exp(tvislightud(o), (void *)((o)->u64 & U64x(00007fff,ffffffff)))
+#define lightudseg(u) \
+ (((u) >> LJ_LIGHTUD_BITS_LO) & ((1 << LJ_LIGHTUD_BITS_SEG)-1))
+#define lightudlo(u) \
+ ((u) & (((uint64_t)1 << LJ_LIGHTUD_BITS_LO) - 1))
+#define lightudup(p) \
+ ((uint32_t)(((p) >> LJ_LIGHTUD_BITS_LO) << (LJ_LIGHTUD_BITS_LO-32)))
+static LJ_AINLINE void *lightudV(global_State *g, cTValue *o)
+{
+ uint64_t u = o->u64;
+ uint64_t seg = lightudseg(u);
+ uint32_t *segmap = mref(g->gc.lightudseg, uint32_t);
+ lua_assert(tvislightud(o));
+ lua_assert(seg <= g->gc.lightudnum);
+ return (void *)(((uint64_t)segmap[seg] << 32) | lightudlo(u));
+}
#else
-#define lightudV(o) check_exp(tvislightud(o), gcrefp((o)->gcr, void))
+#define lightudV(g, o) check_exp(tvislightud(o), gcrefp((o)->gcr, void))
#endif
#define gcV(o) check_exp(tvisgcv(o), gcval(o))
#define strV(o) check_exp(tvisstr(o), &gcval(o)->str)
@@ -863,7 +890,7 @@ typedef union GCobj {
#define setpriV(o, i) (setitype((o), (i)))
#endif
-static LJ_AINLINE void setlightudV(TValue *o, void *p)
+static LJ_AINLINE void setrawlightudV(TValue *o, void *p)
{
#if LJ_GC64
o->u64 = (uint64_t)p | (((uint64_t)LJ_TLIGHTUD) << 47);
@@ -874,24 +901,14 @@ static LJ_AINLINE void setlightudV(TValue *o, void *p)
#endif
}
-#if LJ_64
-#define checklightudptr(L, p) \
- (((uint64_t)(p) >> 47) ? (lj_err_msg(L, LJ_ERR_BADLU), NULL) : (p))
-#else
-#define checklightudptr(L, p) (p)
-#endif
-
-#if LJ_FR2
+#if LJ_FR2 || LJ_32
#define contptr(f) ((void *)(f))
#define setcont(o, f) ((o)->u64 = (uint64_t)(uintptr_t)contptr(f))
-#elif LJ_64
+#else
#define contptr(f) \
((void *)(uintptr_t)(uint32_t)((intptr_t)(f) - (intptr_t)lj_vm_asm_begin))
#define setcont(o, f) \
((o)->u64 = (uint64_t)(void *)(f) - (uint64_t)lj_vm_asm_begin)
-#else
-#define contptr(f) ((void *)(f))
-#define setcont(o, f) setlightudV((o), contptr(f))
#endif
#define tvchecklive(L, o) \
@@ -1014,6 +1031,6 @@ LJ_DATA const char *const lj_obj_itypename[~LJ_TNUMX+1];
/* Compare two objects without calling metamethods. */
LJ_FUNC int LJ_FASTCALL lj_obj_equal(cTValue *o1, cTValue *o2);
-LJ_FUNC const void * LJ_FASTCALL lj_obj_ptr(cTValue *o);
+LJ_FUNC const void * LJ_FASTCALL lj_obj_ptr(global_State *g, cTValue *o);
#endif
diff --git a/src/lj_snap.c b/src/lj_snap.c
index 4cf27e76..2f7cf80a 100644
--- a/src/lj_snap.c
+++ b/src/lj_snap.c
@@ -626,7 +626,12 @@ static void snap_restoreval(jit_State *J, GCtrace *T, ExitState *ex,
IRType1 t = ir->t;
RegSP rs = ir->prev;
if (irref_isk(ref)) { /* Restore constant slot. */
- lj_ir_kvalue(J->L, o, ir);
+ if (ir->o == IR_KPTR) {
+ o->u64 = (uint64_t)(uintptr_t)ir_kptr(ir);
+ } else {
+ lua_assert(!(ir->o == IR_KKPTR || ir->o == IR_KNULL));
+ lj_ir_kvalue(J->L, o, ir);
+ }
return;
}
if (LJ_UNLIKELY(bloomtest(rfilt, ref)))
diff --git a/src/lj_state.c b/src/lj_state.c
index 448f5134..f82b1b5b 100644
--- a/src/lj_state.c
+++ b/src/lj_state.c
@@ -186,6 +186,12 @@ static void close_state(lua_State *L)
lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef);
lj_buf_free(g, &g->tmpbuf);
lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue);
+#if LJ_64
+ if (mref(g->gc.lightudseg, uint32_t)) {
+ MSize segnum = g->gc.lightudnum ? (2 << lj_fls(g->gc.lightudnum)) : 2;
+ lj_mem_freevec(g, mref(g->gc.lightudseg, uint32_t), segnum, uint32_t);
+ }
+#endif
lua_assert(g->gc.total == sizeof(GG_State));
#ifndef LUAJIT_USE_SYSMALLOC
if (g->allocf == lj_alloc_f)
diff --git a/src/lj_strfmt.c b/src/lj_strfmt.c
index d7893ce9..237cc575 100644
--- a/src/lj_strfmt.c
+++ b/src/lj_strfmt.c
@@ -393,7 +393,7 @@ GCstr * LJ_FASTCALL lj_strfmt_obj(lua_State *L, cTValue *o)
p = lj_buf_wmem(p, "builtin#", 8);
p = lj_strfmt_wint(p, funcV(o)->c.ffid);
} else {
- p = lj_strfmt_wptr(p, lj_obj_ptr(o));
+ p = lj_strfmt_wptr(p, lj_obj_ptr(G(L), o));
}
return lj_str_new(L, buf, (size_t)(p - buf));
}
diff --git a/test/tarantool-tests/CMakeLists.txt b/test/tarantool-tests/CMakeLists.txt
index 2fdb4d1f..3e881411 100644
--- a/test/tarantool-tests/CMakeLists.txt
+++ b/test/tarantool-tests/CMakeLists.txt
@@ -57,6 +57,7 @@ macro(BuildTestCLib lib sources)
endmacro()
add_subdirectory(gh-4427-ffi-sandwich)
+add_subdirectory(lj-49-bad-lightuserdata)
add_subdirectory(lj-flush-on-trace)
add_subdirectory(misclib-getmetrics-capi)
diff --git a/test/tarantool-tests/lj-49-bad-lightuserdata.test.lua b/test/tarantool-tests/lj-49-bad-lightuserdata.test.lua
new file mode 100644
index 00000000..111d6a70
--- /dev/null
+++ b/test/tarantool-tests/lj-49-bad-lightuserdata.test.lua
@@ -0,0 +1,10 @@
+local tap = require('tap')
+
+local test = tap.test('lj-49-bad-lightuserdata')
+test:plan(1)
+
+local testlightuserdata = require('testlightuserdata')
+
+test:ok(testlightuserdata.longptr())
+
+os.exit(test:check() and 0 or 1)
diff --git a/test/tarantool-tests/lj-49-bad-lightuserdata/CMakeLists.txt b/test/tarantool-tests/lj-49-bad-lightuserdata/CMakeLists.txt
new file mode 100644
index 00000000..ec6bb62c
--- /dev/null
+++ b/test/tarantool-tests/lj-49-bad-lightuserdata/CMakeLists.txt
@@ -0,0 +1 @@
+BuildTestCLib(testlightuserdata testlightuserdata.c)
diff --git a/test/tarantool-tests/lj-49-bad-lightuserdata/testlightuserdata.c b/test/tarantool-tests/lj-49-bad-lightuserdata/testlightuserdata.c
new file mode 100644
index 00000000..801c7fe1
--- /dev/null
+++ b/test/tarantool-tests/lj-49-bad-lightuserdata/testlightuserdata.c
@@ -0,0 +1,52 @@
+#include <lua.h>
+#include <lauxlib.h>
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#define START ((void *)-1)
+
+static int longptr(lua_State *L)
+{
+ /*
+ * We know that for arm64 at least 48 bits are available.
+ * So emulate manually push of lightuseradata within
+ * this range.
+ */
+ void *longptr = (void *)(1llu << 48);
+ lua_pushlightuserdata(L, longptr);
+ assert(longptr == lua_topointer(L, -1));
+ /*
+ * If start mapping address is not NULL, then the kernel
+ * takes it as a hint about where to place the mapping, so
+ * we try to get the highest memory address by hint
+ * equals -1.
+ */
+ const size_t pagesize = getpagesize();
+ void *mmaped = mmap(START, pagesize, PROT_NONE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (mmaped != MAP_FAILED) {
+ lua_pushlightuserdata(L, mmaped);
+ assert(mmaped == lua_topointer(L, -1));
+ assert(munmap(mmaped, pagesize) == 0);
+ }
+ /* Clear our stack. */
+ lua_pop(L, 0);
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+static const struct luaL_Reg testlightuserdata[] = {
+ {"longptr", longptr},
+ {NULL, NULL}
+};
+
+LUA_API int luaopen_testlightuserdata(lua_State *L)
+{
+ luaL_register(L, "testlightuserdata", testlightuserdata);
+ return 1;
+}
+
--
2.31.0
More information about the Tarantool-patches
mailing list