]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: lua: implement a simple memory allocator
authorWilly Tarreau <w@1wt.eu>
Wed, 18 Mar 2015 16:54:59 +0000 (17:54 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 18 Mar 2015 16:54:59 +0000 (17:54 +0100)
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.

doc/configuration.txt
src/hlua.c

index 9a0420035bd8431f6a46163498be427c20d6b136..34a1e49156b7cab876d1a6f658f4fbb83d071a4a 100644 (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
index 82058d885d277f81fd7eda4e8de5123e1eb2a574..1e1c11441b7f513b1eadf093a0ccb50356d3a300 100644 (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);