From: Willy Tarreau Date: Wed, 18 Mar 2015 16:54:59 +0000 (+0100) Subject: MEDIUM: lua: implement a simple memory allocator X-Git-Tag: v1.6-dev2~301 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=32f61e288defa9b4ca077e999dfda40b8e18e0a4;p=thirdparty%2Fhaproxy.git 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. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 9a0420035b..34a1e49156 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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 lowered. If the Lua code is quite long and its result is absolutely required to process the data, the 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 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 diff --git a/src/hlua.c b/src/hlua.c index 82058d885d..1e1c11441b 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -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. is a pointer to the + * allocator's context. is the pointer to alloc/free/realloc. + * is the previously allocated size or the kind of object in case of a new + * allocation. 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);