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

tarantool / luajit / 10774454435

09 Sep 2024 01:39PM UTC coverage: 92.806% (-0.08%) from 92.882%
10774454435

push

github

Buristan
FFI: Drop finalizer table rehash after GC cycle.

Reported by Sergey Kaplun.

(cherry picked from commit fb22d0f80)

The raising of the OOM error when rehashing the finalizer table (when we
can't allocate a new hash part) leads to crashes in either
`lj_trace_exit()` or `lj_trace_unwind()` due to unprotected error
raising, which either has no DWARF eh_frame [1] or loses the context of
the JIT compiler.

This patch drops rehashing of the cdata finalizer table to avoid these
crashes. It will prevent the cdata finalizer table from shrinking when a
huge amount of the cdata objects is collected by the GC. OTOH, the
finzlizer table most probably will grow anyway to the old size, so this
is not crucial.

Sergey Kaplun:
* added the description and the test for the problem

[1]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html

Part of tarantool/tarantool#10199
Resolves tarantool/tarantool#10290

5681 of 6025 branches covered (94.29%)

Branch coverage included in aggregate %.

21655 of 23430 relevant lines covered (92.42%)

2945096.28 hits per line

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

61.83
/src/lj_clib.c
1
/*
2
** FFI C library loader.
3
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
4
*/
5

6
#include "lj_obj.h"
7

8
#if LJ_HASFFI
9

10
#include "lj_gc.h"
11
#include "lj_err.h"
12
#include "lj_tab.h"
13
#include "lj_str.h"
14
#include "lj_udata.h"
15
#include "lj_ctype.h"
16
#include "lj_cconv.h"
17
#include "lj_cdata.h"
18
#include "lj_clib.h"
19
#include "lj_strfmt.h"
20

21
/* -- OS-specific functions ----------------------------------------------- */
22

23
#if LJ_TARGET_DLOPEN
24

25
#include <dlfcn.h>
26
#include <stdio.h>
27

28
#if defined(RTLD_DEFAULT)
29
#define CLIB_DEFHANDLE        RTLD_DEFAULT
30
#elif LJ_TARGET_OSX || LJ_TARGET_BSD
31
#define CLIB_DEFHANDLE        ((void *)(intptr_t)-2)
32
#else
33
#define CLIB_DEFHANDLE        NULL
34
#endif
35

36
LJ_NORET LJ_NOINLINE static void clib_error_(lua_State *L)
×
37
{
38
  lj_err_callermsg(L, dlerror());
×
39
}
40

41
#define clib_error(L, fmt, name)        clib_error_(L)
42

43
#if LJ_TARGET_CYGWIN
44
#define CLIB_SOPREFIX        "cyg"
45
#else
46
#define CLIB_SOPREFIX        "lib"
47
#endif
48

49
#if LJ_TARGET_OSX
50
#define CLIB_SOEXT        "%s.dylib"
51
#elif LJ_TARGET_CYGWIN
52
#define CLIB_SOEXT        "%s.dll"
53
#else
54
#define CLIB_SOEXT        "%s.so"
55
#endif
56

57
static const char *clib_extname(lua_State *L, const char *name)
10✔
58
{
59
  if (!strchr(name, '/')
10✔
60
#if LJ_TARGET_CYGWIN
61
      && !strchr(name, '\\')
62
#endif
63
     ) {
64
    if (!strchr(name, '.')) {
10✔
65
      name = lj_strfmt_pushf(L, CLIB_SOEXT, name);
10✔
66
      L->top--;
10✔
67
#if LJ_TARGET_CYGWIN
68
    } else {
69
      return name;
70
#endif
71
    }
72
    if (!(name[0] == CLIB_SOPREFIX[0] && name[1] == CLIB_SOPREFIX[1] &&
10✔
73
          name[2] == CLIB_SOPREFIX[2])) {
6✔
74
      name = lj_strfmt_pushf(L, CLIB_SOPREFIX "%s", name);
4✔
75
      L->top--;
4✔
76
    }
77
  }
78
  return name;
10✔
79
}
80

81
/* Check for a recognized ld script line. */
82
static const char *clib_check_lds(lua_State *L, const char *buf)
×
83
{
84
  char *p, *e;
×
85
  if ((!strncmp(buf, "GROUP", 5) || !strncmp(buf, "INPUT", 5)) &&
×
86
      (p = strchr(buf, '('))) {
×
87
    while (*++p == ' ') ;
×
88
    for (e = p; *e && *e != ' ' && *e != ')'; e++) ;
×
89
    return strdata(lj_str_new(L, p, e-p));
×
90
  }
91
  return NULL;
92
}
93

94
/* Quick and dirty solution to resolve shared library name from ld script. */
95
static const char *clib_resolve_lds(lua_State *L, const char *name)
×
96
{
97
  FILE *fp = fopen(name, "r");
×
98
  const char *p = NULL;
×
99
  if (fp) {
×
100
    char buf[256];
×
101
    if (fgets(buf, sizeof(buf), fp)) {
×
102
      if (!strncmp(buf, "/* GNU ld script", 16)) {  /* ld script magic? */
×
103
        while (fgets(buf, sizeof(buf), fp)) {  /* Check all lines. */
×
104
          p = clib_check_lds(L, buf);
×
105
          if (p) break;
×
106
        }
107
      } else {  /* Otherwise check only the first line. */
108
        p = clib_check_lds(L, buf);
×
109
      }
110
    }
111
    fclose(fp);
×
112
  }
113
  return p;
×
114
}
115

116
static void *clib_loadlib(lua_State *L, const char *name, int global)
10✔
117
{
118
  void *h = dlopen(clib_extname(L, name),
20✔
119
                   RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
120
  if (!h) {
10✔
121
    const char *e, *err = dlerror();
×
122
    if (*err == '/' && (e = strchr(err, ':')) &&
×
123
        (name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) {
×
124
      h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
×
125
      if (h) return h;
×
126
      err = dlerror();
×
127
    }
128
    lj_err_callermsg(L, err);
×
129
  }
130
  return h;
131
}
132

133
static void clib_unloadlib(CLibrary *cl)
201✔
134
{
135
  if (cl->handle && cl->handle != CLIB_DEFHANDLE)
201✔
136
    dlclose(cl->handle);
8✔
137
}
138

139
static void *clib_getsym(CLibrary *cl, const char *name)
154✔
140
{
141
  void *p = dlsym(cl->handle, name);
308✔
142
  return p;
154✔
143
}
144

145
#elif LJ_TARGET_WINDOWS
146

147
#define WIN32_LEAN_AND_MEAN
148
#include <windows.h>
149

150
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
151
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS        4
152
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT        2
153
BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
154
#endif
155

156
#define CLIB_DEFHANDLE        ((void *)-1)
157

158
/* Default libraries. */
159
enum {
160
  CLIB_HANDLE_EXE,
161
#if !LJ_TARGET_UWP
162
  CLIB_HANDLE_DLL,
163
  CLIB_HANDLE_CRT,
164
  CLIB_HANDLE_KERNEL32,
165
  CLIB_HANDLE_USER32,
166
  CLIB_HANDLE_GDI32,
167
#endif
168
  CLIB_HANDLE_MAX
169
};
170

171
static void *clib_def_handle[CLIB_HANDLE_MAX];
172

173
LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
174
                                            const char *name)
175
{
176
  DWORD err = GetLastError();
177
#if LJ_TARGET_XBOXONE
178
  wchar_t wbuf[128];
179
  char buf[128*2];
180
  if (!FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
181
                      NULL, err, 0, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL) ||
182
      !WideCharToMultiByte(CP_ACP, 0, wbuf, 128, buf, 128*2, NULL, NULL))
183
#else
184
  char buf[128];
185
  if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
186
                      NULL, err, 0, buf, sizeof(buf), NULL))
187
#endif
188
    buf[0] = '\0';
189
  lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, buf));
190
}
191

192
static int clib_needext(const char *s)
193
{
194
  while (*s) {
195
    if (*s == '/' || *s == '\\' || *s == '.') return 0;
196
    s++;
197
  }
198
  return 1;
199
}
200

201
static const char *clib_extname(lua_State *L, const char *name)
202
{
203
  if (clib_needext(name)) {
204
    name = lj_strfmt_pushf(L, "%s.dll", name);
205
    L->top--;
206
  }
207
  return name;
208
}
209

210
static void *clib_loadlib(lua_State *L, const char *name, int global)
211
{
212
  DWORD oldwerr = GetLastError();
213
  void *h = LJ_WIN_LOADLIBA(clib_extname(L, name));
214
  if (!h) clib_error(L, "cannot load module " LUA_QS ": %s", name);
215
  SetLastError(oldwerr);
216
  UNUSED(global);
217
  return h;
218
}
219

220
static void clib_unloadlib(CLibrary *cl)
221
{
222
  if (cl->handle == CLIB_DEFHANDLE) {
223
#if !LJ_TARGET_UWP
224
    MSize i;
225
    for (i = CLIB_HANDLE_KERNEL32; i < CLIB_HANDLE_MAX; i++) {
226
      void *h = clib_def_handle[i];
227
      if (h) {
228
        clib_def_handle[i] = NULL;
229
        FreeLibrary((HINSTANCE)h);
230
      }
231
    }
232
#endif
233
  } else if (cl->handle) {
234
    FreeLibrary((HINSTANCE)cl->handle);
235
  }
236
}
237

238
#if LJ_TARGET_UWP
239
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
240
#endif
241

242
static void *clib_getsym(CLibrary *cl, const char *name)
243
{
244
  void *p = NULL;
245
  if (cl->handle == CLIB_DEFHANDLE) {  /* Search default libraries. */
246
    MSize i;
247
    for (i = 0; i < CLIB_HANDLE_MAX; i++) {
248
      HINSTANCE h = (HINSTANCE)clib_def_handle[i];
249
      if (!(void *)h) {  /* Resolve default library handles (once). */
250
#if LJ_TARGET_UWP
251
        h = (HINSTANCE)&__ImageBase;
252
#else
253
        switch (i) {
254
        case CLIB_HANDLE_EXE: GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, NULL, &h); break;
255
        case CLIB_HANDLE_DLL:
256
          GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
257
                             (const char *)clib_def_handle, &h);
258
          break;
259
        case CLIB_HANDLE_CRT:
260
          GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
261
                             (const char *)&_fmode, &h);
262
          break;
263
        case CLIB_HANDLE_KERNEL32: h = LJ_WIN_LOADLIBA("kernel32.dll"); break;
264
        case CLIB_HANDLE_USER32: h = LJ_WIN_LOADLIBA("user32.dll"); break;
265
        case CLIB_HANDLE_GDI32: h = LJ_WIN_LOADLIBA("gdi32.dll"); break;
266
        }
267
        if (!h) continue;
268
#endif
269
        clib_def_handle[i] = (void *)h;
270
      }
271
      p = (void *)GetProcAddress(h, name);
272
      if (p) break;
273
    }
274
  } else {
275
    p = (void *)GetProcAddress((HINSTANCE)cl->handle, name);
276
  }
277
  return p;
278
}
279

280
#else
281

282
#define CLIB_DEFHANDLE        NULL
283

284
LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
285
                                            const char *name)
286
{
287
  lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, "no support for this OS"));
288
}
289

290
static void *clib_loadlib(lua_State *L, const char *name, int global)
291
{
292
  lj_err_callermsg(L, "no support for loading dynamic libraries for this OS");
293
  UNUSED(name); UNUSED(global);
294
  return NULL;
295
}
296

297
static void clib_unloadlib(CLibrary *cl)
298
{
299
  UNUSED(cl);
300
}
301

302
static void *clib_getsym(CLibrary *cl, const char *name)
303
{
304
  UNUSED(cl); UNUSED(name);
305
  return NULL;
306
}
307

308
#endif
309

310
/* -- C library indexing -------------------------------------------------- */
311

312
#if LJ_TARGET_X86 && LJ_ABI_WIN
313
/* Compute argument size for fastcall/stdcall functions. */
314
static CTSize clib_func_argsize(CTState *cts, CType *ct)
315
{
316
  CTSize n = 0;
317
  while (ct->sib) {
318
    CType *d;
319
    ct = ctype_get(cts, ct->sib);
320
    if (ctype_isfield(ct->info)) {
321
      d = ctype_rawchild(cts, ct);
322
      n += ((d->size + 3) & ~3);
323
    }
324
  }
325
  return n;
326
}
327
#endif
328

329
/* Get redirected or mangled external symbol. */
330
static const char *clib_extsym(CTState *cts, CType *ct, GCstr *name)
154✔
331
{
332
  if (ct->sib) {
154✔
333
    CType *ctf = ctype_get(cts, ct->sib);
133✔
334
    if (ctype_isxattrib(ctf->info, CTA_REDIR))
133✔
335
      return strdata(gco2str(gcref(ctf->name)));
19✔
336
  }
337
  return strdata(name);
135✔
338
}
339

340
/* Index a C library by name. */
341
TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name)
63,338✔
342
{
343
  TValue *tv = lj_tab_setstr(L, cl->cache, name);
63,338✔
344
  if (LJ_UNLIKELY(tvisnil(tv))) {
63,338✔
345
    CTState *cts = ctype_cts(L);
154✔
346
    CType *ct;
154✔
347
    CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX);
154✔
348
    if (!id)
154✔
349
      lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name));
×
350
    if (ctype_isconstval(ct->info)) {
154✔
351
      CType *ctt = ctype_child(cts, ct);
×
352
      lj_assertCTS(ctype_isinteger(ctt->info) && ctt->size <= 4,
×
353
                   "only 32 bit const supported");  /* NYI */
354
      if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0)
×
355
        setnumV(tv, (lua_Number)(uint32_t)ct->size);
×
356
      else
357
        setintV(tv, (int32_t)ct->size);
×
358
    } else {
359
      const char *sym = clib_extsym(cts, ct, name);
154✔
360
#if LJ_TARGET_WINDOWS
361
      DWORD oldwerr = GetLastError();
362
#endif
363
      void *p = clib_getsym(cl, sym);
154✔
364
      GCcdata *cd;
154✔
365
      lj_assertCTS(ctype_isfunc(ct->info) || ctype_isextern(ct->info),
154✔
366
                   "unexpected ctype %08x in clib", ct->info);
367
#if LJ_TARGET_X86 && LJ_ABI_WIN
368
      /* Retry with decorated name for fastcall/stdcall functions. */
369
      if (!p && ctype_isfunc(ct->info)) {
370
        CTInfo cconv = ctype_cconv(ct->info);
371
        if (cconv == CTCC_FASTCALL || cconv == CTCC_STDCALL) {
372
          CTSize sz = clib_func_argsize(cts, ct);
373
          const char *symd = lj_strfmt_pushf(L,
374
                               cconv == CTCC_FASTCALL ? "@%s@%d" : "_%s@%d",
375
                               sym, sz);
376
          L->top--;
377
          p = clib_getsym(cl, symd);
378
        }
379
      }
380
#endif
381
      if (!p)
154✔
382
        clib_error(L, "cannot resolve symbol " LUA_QS ": %s", sym);
×
383
#if LJ_TARGET_WINDOWS
384
      SetLastError(oldwerr);
385
#endif
386
      cd = lj_cdata_new(cts, id, CTSIZE_PTR);
154✔
387
      *(void **)cdataptr(cd) = p;
154✔
388
      setcdataV(L, tv, cd);
154✔
389
      lj_gc_anybarriert(L, cl->cache);
154✔
390
    }
391
  }
392
  return tv;
63,338✔
393
}
394

395
/* -- C library management ------------------------------------------------ */
396

397
/* Create a new CLibrary object and push it on the stack. */
398
static CLibrary *clib_new(lua_State *L, GCtab *mt)
207✔
399
{
400
  GCtab *t = lj_tab_new(L, 0, 0);
207✔
401
  GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t);
207✔
402
  CLibrary *cl = (CLibrary *)uddata(ud);
207✔
403
  cl->cache = t;
207✔
404
  ud->udtype = UDTYPE_FFI_CLIB;
207✔
405
  /* NOBARRIER: The GCudata is new (marked white). */
406
  setgcref(ud->metatable, obj2gco(mt));
207✔
407
  setudataV(L, L->top++, ud);
207✔
408
  return cl;
207✔
409
}
410

411
/* Load a C library. */
412
void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global)
10✔
413
{
414
  void *handle = clib_loadlib(L, strdata(name), global);
10✔
415
  CLibrary *cl = clib_new(L, mt);
10✔
416
  cl->handle = handle;
10✔
417
}
10✔
418

419
/* Unload a C library. */
420
void lj_clib_unload(CLibrary *cl)
201✔
421
{
422
  clib_unloadlib(cl);
201✔
423
  cl->handle = NULL;
201✔
424
}
201✔
425

426
/* Create the default C library object. */
427
void lj_clib_default(lua_State *L, GCtab *mt)
197✔
428
{
429
  CLibrary *cl = clib_new(L, mt);
197✔
430
  cl->handle = CLIB_DEFHANDLE;
197✔
431
}
197✔
432

433
#endif
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