MEDIUM: lua: implement a simple memory allocator

Lua supports a memory allocator. This is very important as it's the
only way we can control the amount of memory allocatable by Lua scripts.
That avoids prevents bogus scripts from eating all of the system's memory.
The value can be enforced using tune.lua.maxmem in the global section.
This commit is contained in:
Willy Tarreau 2015-03-18 17:54:59 +01:00
parent 36d1374484
commit 32f61e288d
2 changed files with 78 additions and 0 deletions

View file

@ -487,6 +487,7 @@ The following keywords are supported in the "global" section :
- tune.http.maxhdr
- tune.idletimer
- tune.lua.forced-yield
- tune.lua.maxmem
- tune.lua.session-timeout
- tune.lua.task-timeout
- tune.maxaccept
@ -999,6 +1000,12 @@ tune.lua.forced-yield <number>
lowered. If the Lua code is quite long and its result is absolutely required
to process the data, the <number> can be increased.
tune.lua.maxmem
Sets the maximum amount of RAM in megabytes per process usable by Lua. By
default it is zero which means unlimited. It is important to set a limit to
ensure that a bug in a script will not result in the system running out of
memory.
tune.lua.session-timeout <timeout>
This is the execution timeout for the Lua sessions. This is useful for
preventing infinite loops or spending too much time in Lua. This timeout has a

View file

@ -112,6 +112,16 @@ static unsigned int hlua_timeout_task = TICK_ETERNITY; /* task timeout. */
*/
static unsigned int hlua_nb_instruction = 10000;
/* Descriptor for the memory allocation state. If limit is not null, it will
* be enforced on any memory allocation.
*/
struct hlua_mem_allocator {
size_t allocated;
size_t limit;
};
static struct hlua_mem_allocator hlua_global_allocator;
/* These functions converts types between HAProxy internal args or
* sample and LUA types. Another function permits to check if the
* LUA stack contains arguments according with an required ARG_T
@ -4417,6 +4427,25 @@ static int hlua_forced_yield(char **args, int section_type, struct proxy *curpx,
return 0;
}
static int hlua_parse_maxmem(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
char *error;
if (*(args[1]) == 0) {
memprintf(err, "'%s' expects an integer argument (Lua memory size in MB).\n", args[0]);
return -1;
}
hlua_global_allocator.limit = strtoll(args[1], &error, 10) * 1024L * 1024L;
if (*error != '\0') {
memprintf(err, "%s: invalid number %s (error at '%c')", args[0], args[1], *error);
return -1;
}
return 0;
}
/* This function is called by the main configuration key "lua-load". It loads and
* execute an lua file during the parsing of the HAProxy configuration file. It is
* the main lua entry point.
@ -4476,6 +4505,7 @@ static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "tune.lua.session-timeout", hlua_session_timeout },
{ CFG_GLOBAL, "tune.lua.task-timeout", hlua_task_timeout },
{ CFG_GLOBAL, "tune.lua.forced-yield", hlua_forced_yield },
{ CFG_GLOBAL, "tune.lua.maxmem", hlua_parse_maxmem },
{ 0, NULL, NULL },
}};
@ -4528,6 +4558,44 @@ int hlua_post_init()
return 1;
}
/* The memory allocator used by the Lua stack. <ud> is a pointer to the
* allocator's context. <ptr> is the pointer to alloc/free/realloc. <osize>
* is the previously allocated size or the kind of object in case of a new
* allocation. <nsize> is the requested new size.
*/
static void *hlua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
struct hlua_mem_allocator *zone = ud;
if (nsize == 0) {
/* it's a free */
if (ptr)
zone->allocated -= osize;
free(ptr);
return NULL;
}
if (!ptr) {
/* it's a new allocation */
if (zone->limit && zone->allocated + nsize > zone->limit)
return NULL;
ptr = malloc(nsize);
if (ptr)
zone->allocated += nsize;
return ptr;
}
/* it's a realloc */
if (zone->limit && zone->allocated + nsize - osize > zone->limit)
return NULL;
ptr = realloc(ptr, nsize);
if (ptr)
zone->allocated += nsize - osize;
return ptr;
}
void hlua_init(void)
{
int i;
@ -4572,6 +4640,9 @@ void hlua_init(void)
gL.Tref = LUA_REFNIL;
gL.task = NULL;
/* change the memory allocators to track memory usage */
lua_setallocf(gL.T, hlua_alloc, &hlua_global_allocator);
/* Initialise lua. */
luaL_openlibs(gL.T);