diff --git a/doc/configuration.txt b/doc/configuration.txt index 9a0420035..34a1e4915 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 82058d885..1e1c11441 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);