Skip to content

Commit 9aa227a

Browse files
Mike PallBuristan
authored andcommitted
Correctly close VM state after early OOM during open.
Reported by Assumeru. (cherry picked from commit 5ca25ee) `lua_newstate()` sets `g->str.mask` to `~(MSize)0` before calling `cpluaopen(). If OOM happens before the `lj_str_init()` call, `lj_gc_freeall()` calls `gc_sweepstr()` in a loop with incorrect top limit `g->str.mask`, which leads to the crash. This patch changes the order of the loop iteration with the correct bottom limit. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#11691
1 parent a74e5be commit 9aa227a

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

src/lj_gc.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -598,12 +598,11 @@ void lj_gc_finalize_cdata(lua_State *L)
598598
/* Free all remaining GC objects. */
599599
void lj_gc_freeall(global_State *g)
600600
{
601-
MSize i, strmask;
601+
MSize i;
602602
/* Free everything, except super-fixed objects (the main thread). */
603603
g->gc.currentwhite = LJ_GC_WHITES | LJ_GC_SFIXED;
604604
gc_fullsweep(g, &g->gc.root);
605-
strmask = g->strmask;
606-
for (i = 0; i <= strmask; i++) /* Free all string hash chains. */
605+
for (i = g->strmask; i != ~(MSize)0; i--) /* Free all string hash chains. */
607606
gc_fullsweep(g, &g->strhash[i]);
608607
}
609608

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "lua.h"
2+
/* XXX: The "lj_arch.h" header is included for the skipcond. */
3+
#include "lj_arch.h"
4+
5+
#include "test.h"
6+
7+
#include <stdlib.h>
8+
9+
/*
10+
* LuaJIT requires at least 12000 something bytes for initial
11+
* allocations. The `GG_State` requires a little bit more than
12+
* 6000 bytes (around 3000 bytes is the `jit_State`).
13+
*/
14+
15+
/* Currently allocated Lua memory and its limit. */
16+
static size_t current_memory = 0;
17+
const size_t memory_limit = 7000;
18+
19+
void *limited_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize)
20+
{
21+
void *ret_ptr = NULL;
22+
/* Overflow is OK here. */
23+
const size_t requested_diff = nsize - osize;
24+
(void)msp;
25+
26+
if (current_memory + requested_diff > memory_limit)
27+
return NULL;
28+
29+
if (nsize == 0) {
30+
free(ptr);
31+
current_memory -= osize;
32+
} else if (ptr == NULL) {
33+
ret_ptr = malloc(nsize);
34+
current_memory += ret_ptr ? nsize : 0;
35+
} else {
36+
ret_ptr = realloc(ptr, nsize);
37+
current_memory += ret_ptr ? requested_diff : 0;
38+
}
39+
return ret_ptr;
40+
}
41+
42+
static int limited_memory_on_lua_newstate(void *test_state)
43+
{
44+
(void)test_state;
45+
#if LJ_64 && !LJ_GC64
46+
(void)limited_alloc_f;
47+
return skip("Can't use custom allocator for 64-bit host without GC64");
48+
#else
49+
/*
50+
* Check that there is no crash and the limit is small enough.
51+
*/
52+
const lua_State *L = lua_newstate(limited_alloc_f, NULL);
53+
assert_true(L == NULL);
54+
return TEST_EXIT_SUCCESS;
55+
#endif
56+
}
57+
58+
#ifndef LJ_NO_UNWIND
59+
# define LJ_NO_UNWIND 0
60+
#endif
61+
62+
int main(void)
63+
{
64+
/* See https://github.com/LuaJIT/LuaJIT/issues/1311. */
65+
if (!LJ_NO_UNWIND)
66+
return skip_all("Disabled for external unwinding build due to #1311");
67+
const struct test_unit tgroup[] = {
68+
test_unit_def(limited_memory_on_lua_newstate),
69+
};
70+
return test_run_group(tgroup, NULL);
71+
}

0 commit comments

Comments
 (0)