• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / luajit / 9873466312

10 Jul 2024 11:36AM UTC coverage: 92.772% (+0.03%) from 92.745%
9873466312

push

github

ligurio
FFI: Turn FFI finalizer table into a proper GC root.

Reported by Sergey Bronnikov.

(cherry picked from commit f5affaa6c)

Previous patch fixes the problem partially because the introduced
GC root may not exist at the start phase of the GC cycle. In that
case, the cdata finalizer table will be collected at the end of
the cycle. Access to the cdata finalizer table exhibits heap use
after free. The patch turns the finalizer table into a proper
GC root. Note, that finalizer table is created on the
initialization of the main Lua State instead of loading the FFI
library.

Sergey Bronnikov:
* added the description and the tests for the problem

Part of tarantool/tarantool#10199

5674 of 6025 branches covered (94.17%)

Branch coverage included in aggregate %.

26 of 26 new or added lines in 5 files covered. (100.0%)

18 existing lines in 5 files now uncovered.

21653 of 23431 relevant lines covered (92.41%)

2941091.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.65
/src/lj_state.c
1
/*
2
** State and stack handling.
3
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
4
**
5
** Portions taken verbatim or adapted from the Lua interpreter.
6
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7
*/
8

9
#define lj_state_c
10
#define LUA_CORE
11

12
#include "lj_obj.h"
13
#include "lj_gc.h"
14
#include "lj_err.h"
15
#include "lj_buf.h"
16
#include "lj_str.h"
17
#include "lj_tab.h"
18
#include "lj_func.h"
19
#include "lj_meta.h"
20
#include "lj_state.h"
21
#include "lj_frame.h"
22
#if LJ_HASFFI
23
#include "lj_ctype.h"
24
#endif
25
#include "lj_trace.h"
26
#include "lj_dispatch.h"
27
#include "lj_vm.h"
28
#include "lj_lex.h"
29
#include "lj_alloc.h"
30
#include "luajit.h"
31

32
#if LJ_HASMEMPROF
33
#include "lj_memprof.h"
34
#endif
35

36
#if LJ_HASSYSPROF
37
#include "lj_sysprof.h"
38
#endif
39

40
/* -- Stack handling ------------------------------------------------------ */
41

42
/* Stack sizes. */
43
#define LJ_STACK_MIN        LUA_MINSTACK        /* Min. stack size. */
44
#define LJ_STACK_MAX        LUAI_MAXSTACK        /* Max. stack size. */
45
#define LJ_STACK_START        (2*LJ_STACK_MIN)        /* Starting stack size. */
46
#define LJ_STACK_MAXEX        (LJ_STACK_MAX + 1 + LJ_STACK_EXTRA)
47

48
/* Explanation of LJ_STACK_EXTRA:
49
**
50
** Calls to metamethods store their arguments beyond the current top
51
** without checking for the stack limit. This avoids stack resizes which
52
** would invalidate passed TValue pointers. The stack check is performed
53
** later by the function header. This can safely resize the stack or raise
54
** an error. Thus we need some extra slots beyond the current stack limit.
55
**
56
** Most metamethods need 4 slots above top (cont, mobj, arg1, arg2) plus
57
** one extra slot if mobj is not a function. Only lj_meta_tset needs 5
58
** slots above top, but then mobj is always a function. So we can get by
59
** with 5 extra slots.
60
** LJ_FR2: We need 2 more slots for the frame PC and the continuation PC.
61
*/
62

63
/* Resize stack slots and adjust pointers in state. */
64
static void resizestack(lua_State *L, MSize n)
539✔
65
{
66
  TValue *st, *oldst = tvref(L->stack);
539✔
67
  ptrdiff_t delta;
539✔
68
  MSize oldsize = L->stacksize;
539✔
69
  MSize realsize = n + 1 + LJ_STACK_EXTRA;
539✔
70
  GCobj *up;
539✔
71
  int32_t oldvmstate = G(L)->vmstate;
539✔
72

73
  lj_assertL((MSize)(tvref(L->maxstack)-oldst) == L->stacksize-LJ_STACK_EXTRA-1,
539✔
74
             "inconsistent stack size");
75

76
  /*
77
  ** Lua stack is inconsistent while reallocation, profilers
78
  ** depend on vmstate during reports, so set vmstate to INTERP
79
  ** to avoid inconsistent behaviour.
80
  */
81
  setvmstate(G(L), INTERP);
539✔
82
  st = (TValue *)lj_mem_realloc(L, tvref(L->stack),
1,078✔
83
                                (MSize)(oldsize*sizeof(TValue)),
539✔
84
                                (MSize)(realsize*sizeof(TValue)));
539✔
85
  setmref(L->stack, st);
539✔
86
  delta = (char *)st - (char *)oldst;
539✔
87
  setmref(L->maxstack, st + n);
539✔
88
  while (oldsize < realsize)  /* Clear new slots. */
448,278✔
89
    setnilV(st + oldsize++);
447,739✔
90
  L->stacksize = realsize;
539✔
91
  if ((size_t)(mref(G(L)->jit_base, char) - (char *)oldst) < oldsize)
539✔
92
    setmref(G(L)->jit_base, mref(G(L)->jit_base, char) + delta);
×
93
  L->base = (TValue *)((char *)L->base + delta);
539✔
94
  L->top = (TValue *)((char *)L->top + delta);
539✔
95
  for (up = gcref(L->openupval); up != NULL; up = gcnext(up))
3,347✔
96
    setmref(gco2uv(up)->v, (TValue *)((char *)uvval(gco2uv(up)) + delta));
2,808✔
97

98
  G(L)->vmstate = oldvmstate;
539✔
99
}
539✔
100

101
/* Relimit stack after error, in case the limit was overdrawn. */
102
void lj_state_relimitstack(lua_State *L)
50,305✔
103
{
104
  if (L->stacksize > LJ_STACK_MAXEX && L->top-tvref(L->stack) < LJ_STACK_MAX-1)
50,305✔
105
    resizestack(L, LJ_STACK_MAX);
5✔
106
}
50,305✔
107

108
/* Try to shrink the stack (called from GC). */
109
void lj_state_shrinkstack(lua_State *L, MSize used)
1,570,383✔
110
{
111
  if (L->stacksize > LJ_STACK_MAXEX)
1,570,383✔
112
    return;  /* Avoid stack shrinking while handling stack overflow. */
113
  if (4*used < L->stacksize &&
1,570,380✔
114
      2*(LJ_STACK_START+LJ_STACK_EXTRA) < L->stacksize &&
95✔
115
      /* Don't shrink stack of live trace. */
116
      (tvref(G(L)->jit_base) == NULL || obj2gco(L) != gcref(G(L)->cur_L)))
95✔
117
    resizestack(L, L->stacksize >> 1);
91✔
118
}
119

120
/* Try to grow stack. */
121
void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need)
455✔
122
{
123
  MSize n;
455✔
124
  if (L->stacksize >= LJ_STACK_MAXEX) {
455✔
125
    /* 4. Throw 'error in error handling' when we are _over_ the limit. */
126
    if (L->stacksize > LJ_STACK_MAXEX)
18✔
127
      lj_err_throw(L, LUA_ERRERR);  /* Does not invoke an error handler. */
2✔
128
    /* 1. We are _at_ the limit after the last growth. */
129
    if (L->status < LUA_ERRRUN) {  /* 2. Throw 'stack overflow'. */
16✔
130
      L->status = LUA_ERRRUN;  /* Prevent ending here again for pushed msg. */
10✔
131
      lj_err_msg(L, LJ_ERR_STKOV);  /* May invoke an error handler. */
10✔
132
    }
133
    /* 3. Add space (over the limit) for pushed message and error handler. */
134
  }
135
  n = L->stacksize + need;
443✔
136
  if (n > LJ_STACK_MAX) {
443✔
137
    n += 2*LUA_MINSTACK;
6✔
138
  } else if (n < 2*L->stacksize) {
437✔
139
    n = 2*L->stacksize;
420✔
140
    if (n >= LJ_STACK_MAX)
420✔
141
      n = LJ_STACK_MAX;
142
  }
143
  resizestack(L, n);
443✔
144
}
443✔
145

146
void LJ_FASTCALL lj_state_growstack1(lua_State *L)
29✔
147
{
148
  lj_state_growstack(L, 1);
29✔
149
}
25✔
150

151
/* Allocate basic stack for new state. */
152
static void stack_init(lua_State *L1, lua_State *L)
1,446✔
153
{
154
  TValue *stend, *st = lj_mem_newvec(L, LJ_STACK_START+LJ_STACK_EXTRA, TValue);
1,446✔
155
  setmref(L1->stack, st);
1,446✔
156
  L1->stacksize = LJ_STACK_START + LJ_STACK_EXTRA;
1,446✔
157
  stend = st + L1->stacksize;
1,446✔
158
  setmref(L1->maxstack, stend - LJ_STACK_EXTRA - 1);
1,446✔
159
  setthreadV(L1, st++, L1);  /* Needed for curr_funcisL() on empty stack. */
1,446✔
160
  if (LJ_FR2) setnilV(st++);
1,446✔
161
  L1->base = L1->top = st;
1,446✔
162
  while (st < stend)  /* Clear new slots. */
66,516✔
163
    setnilV(st++);
65,070✔
164
}
1,446✔
165

166
/* -- State handling ------------------------------------------------------ */
167

168
/* Open parts that may cause memory-allocation errors. */
169
static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud)
343✔
170
{
171
  global_State *g = G(L);
343✔
172
  UNUSED(dummy);
343✔
173
  UNUSED(ud);
343✔
174
  stack_init(L, L);
343✔
175
  /* NOBARRIER: State initialization, all objects are white. */
176
  setgcref(L->env, obj2gco(lj_tab_new(L, 0, LJ_MIN_GLOBAL)));
343✔
177
  settabV(L, registry(L), lj_tab_new(L, 0, LJ_MIN_REGISTRY));
343✔
178
  lj_str_resize(L, LJ_MIN_STRTAB-1);
343✔
179
  lj_meta_init(L);
343✔
180
  lj_lex_init(L);
343✔
181
  fixstring(lj_err_str(L, LJ_ERR_ERRMEM));  /* Preallocate memory error msg. */
343✔
182
  g->gc.threshold = 4*g->gc.total;
343✔
183
#if LJ_HASFFI
184
  lj_ctype_initfin(L);
343✔
185
#endif
186
  lj_trace_initstate(g);
343✔
187
  lj_err_verify();
343✔
188
  return NULL;
343✔
189
}
190

191
static void close_state(lua_State *L)
333✔
192
{
193
  global_State *g = G(L);
333✔
194
  lj_func_closeuv(L, tvref(L->stack));
333✔
195
  lj_gc_freeall(g);
333✔
196
  lj_assertG(gcref(g->gc.root) == obj2gco(L),
333✔
197
             "main thread is not first GC object");
198
  lj_assertG(g->strnum == 0, "leaked %d strings", g->strnum);
333✔
199
  lj_trace_freestate(g);
333✔
200
#if LJ_HASFFI
201
  lj_ctype_freestate(g);
333✔
202
#endif
203
  lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef);
333✔
204
  lj_buf_free(g, &g->tmpbuf);
333✔
205
  lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue);
333✔
206
#if LJ_64
207
  if (mref(g->gc.lightudseg, uint32_t)) {
333✔
208
    MSize segnum = g->gc.lightudnum ? (2 << lj_fls(g->gc.lightudnum)) : 2;
324✔
209
    lj_mem_freevec(g, mref(g->gc.lightudseg, uint32_t), segnum, uint32_t);
324✔
210
  }
211
#endif
212
  lj_assertG(g->gc.total == sizeof(GG_State),
333✔
213
             "memory leak of %lld bytes",
214
             (long long)(g->gc.total - sizeof(GG_State)));
215
#ifndef LUAJIT_USE_SYSMALLOC
216
  if (g->allocf == lj_alloc_f)
333✔
217
    lj_alloc_destroy(g->allocd);
333✔
218
  else
219
#endif
UNCOV
220
    g->allocf(g->allocd, G2GG(g), sizeof(GG_State), 0);
×
221
}
333✔
222

223
#if LJ_64 && !LJ_GC64 && !(defined(LUAJIT_USE_VALGRIND) && defined(LUAJIT_USE_SYSMALLOC))
224
lua_State *lj_state_newstate(lua_Alloc f, void *ud)
225
#else
226
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
343✔
227
#endif
228
{
229
  GG_State *GG = (GG_State *)f(ud, NULL, 0, sizeof(GG_State));
343✔
230
  lua_State *L = &GG->L;
343✔
231
  global_State *g = &GG->g;
343✔
232
  if (GG == NULL || !checkptrGC(GG)) return NULL;
343✔
233
  memset(GG, 0, sizeof(GG_State));
343✔
234
  L->gct = ~LJ_TTHREAD;
343✔
235
  L->marked = LJ_GC_WHITE0 | LJ_GC_FIXED | LJ_GC_SFIXED;  /* Prevent free. */
343✔
236
  L->dummy_ffid = FF_C;
343✔
237
  setmref(L->glref, g);
343✔
238
  g->gc.currentwhite = LJ_GC_WHITE0 | LJ_GC_FIXED;
343✔
239
  g->strempty.marked = LJ_GC_WHITE0;
343✔
240
  g->strempty.gct = ~LJ_TSTR;
343✔
241
  g->allocf = f;
343✔
242
  g->allocd = ud;
343✔
243
  setgcref(g->mainthref, obj2gco(L));
343✔
244
  setgcref(g->uvhead.prev, obj2gco(&g->uvhead));
343✔
245
  setgcref(g->uvhead.next, obj2gco(&g->uvhead));
343✔
246
  g->strmask = ~(MSize)0;
343✔
247
  setnilV(registry(L));
343✔
248
  setnilV(&g->nilnode.val);
343✔
249
  setnilV(&g->nilnode.key);
343✔
250
#if !LJ_GC64
251
  setmref(g->nilnode.freetop, &g->nilnode);
252
#endif
253
  lj_buf_init(NULL, &g->tmpbuf);
343✔
254
  g->gc.state = GCSpause;
343✔
255
  setgcref(g->gc.root, obj2gco(L));
343✔
256
  setmref(g->gc.sweep, &g->gc.root);
343✔
257
  g->gc.allocated = g->gc.total = sizeof(GG_State);
343✔
258
  g->gc.pause = LUAI_GCPAUSE;
343✔
259
  g->gc.stepmul = LUAI_GCMUL;
343✔
260
  lj_dispatch_init((GG_State *)L);
343✔
261
  L->status = LUA_ERRERR+1;  /* Avoid touching the stack upon memory error. */
343✔
262
  if (lj_vm_cpcall(L, NULL, NULL, cpluaopen) != 0) {
343✔
263
    /* Memory allocation error: free partial state. */
UNCOV
264
    close_state(L);
×
UNCOV
265
    return NULL;
×
266
  }
267
  L->status = LUA_OK;
343✔
268
  return L;
343✔
269
}
270

271
static TValue *cpfinalize(lua_State *L, lua_CFunction dummy, void *ud)
342✔
272
{
273
  UNUSED(dummy);
342✔
274
  UNUSED(ud);
342✔
275
  lj_gc_finalize_cdata(L);
342✔
276
  lj_gc_finalize_udata(L);
342✔
277
  /* Frame pop omitted. */
278
  return NULL;
342✔
279
}
280

281
LUA_API void lua_close(lua_State *L)
333✔
282
{
283
  global_State *g = G(L);
333✔
284
  int i;
333✔
285
  L = mainthread(g);  /* Only the main thread can be closed. */
333✔
286
#if LJ_HASMEMPROF
287
  lj_memprof_stop(L);
333✔
288
#endif
289
#if LJ_HASSYSPROF
290
  lj_sysprof_stop(L);
333✔
291
#endif
292
#if LJ_HASPROFILE
293
  luaJIT_profile_stop(L);
333✔
294
#endif
295
  setgcrefnull(g->cur_L);
333✔
296
  lj_func_closeuv(L, tvref(L->stack));
333✔
297
  lj_gc_separateudata(g, 1);  /* Separate udata which have GC metamethods. */
333✔
298
#if LJ_HASJIT
299
  G2J(g)->flags &= ~JIT_F_ON;
333✔
300
  G2J(g)->state = LJ_TRACE_IDLE;
333✔
301
  lj_dispatch_update(g);
333✔
302
#endif
303
  for (i = 0;;) {
333✔
304
    hook_enter(g);
342✔
305
    L->status = LUA_OK;
342✔
306
    L->base = L->top = tvref(L->stack) + 1 + LJ_FR2;
342✔
307
    L->cframe = NULL;
342✔
308
    if (lj_vm_cpcall(L, NULL, NULL, cpfinalize) == LUA_OK) {
342✔
309
      if (++i >= 10) break;
342✔
310
      lj_gc_separateudata(g, 1);  /* Separate udata again. */
341✔
311
      if (gcref(g->gc.mmudata) == NULL)  /* Until nothing is left to do. */
341✔
312
        break;
313
    }
314
  }
315
  close_state(L);
333✔
316
}
333✔
317

318
lua_State *lj_state_new(lua_State *L)
1,103✔
319
{
320
  lua_State *L1 = lj_mem_newobj(L, lua_State);
1,103✔
321
  L1->gct = ~LJ_TTHREAD;
1,103✔
322
  L1->dummy_ffid = FF_C;
1,103✔
323
  L1->status = LUA_OK;
1,103✔
324
  L1->stacksize = 0;
1,103✔
325
  setmref(L1->stack, NULL);
1,103✔
326
  L1->cframe = NULL;
1,103✔
327
  /* NOBARRIER: The lua_State is new (marked white). */
328
  setgcrefnull(L1->openupval);
1,103✔
329
  setmrefr(L1->glref, L->glref);
1,103✔
330
  setgcrefr(L1->env, L->env);
1,103✔
331
  stack_init(L1, L);  /* init stack */
1,103✔
332
  lj_assertL(iswhite(obj2gco(L1)), "new thread object is not white");
1,103✔
333
  return L1;
1,103✔
334
}
335

336
void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L)
1,103✔
337
{
338
  lj_assertG(L != mainthread(g), "free of main thread");
1,103✔
339
  if (obj2gco(L) == gcref(g->cur_L))
1,103✔
UNCOV
340
    setgcrefnull(g->cur_L);
×
341
  lj_func_closeuv(L, tvref(L->stack));
1,103✔
342
  lj_assertG(gcref(L->openupval) == NULL, "stale open upvalues");
1,103✔
343
  lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue);
1,103✔
344
  lj_mem_freet(g, L);
1,103✔
345
}
1,103✔
346

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc