From: William Lallemand Date: Wed, 27 May 2026 17:54:48 +0000 (+0200) Subject: MINOR: lua: defer Lua VM initialisation to the first Lua config keyword X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=8b0e2920a3aa0b50ed5f995fd7a6d19b869b7cff;p=thirdparty%2Fhaproxy.git MINOR: lua: defer Lua VM initialisation to the first Lua config keyword HAProxy used to call hlua_init() unconditionally from step_init_1(), before any configuration file was parsed. As a consequence, Lua states 0 and 1 were always created with hlua_openlibs_flags set to its default value (HLUA_OPENLIBS_ALL), regardless of any tune.lua.openlibs directive that appeared later in the global section. With multiple threads, states 2..N were created correctly in hlua_post_init() after the config had been parsed, while states 0 and 1 retained the full standard-library set. This produced the observable bug reported in GitHub issue #3396: a script loaded with lua-load-per-thread could see require() as a function on thread 1 but nil on thread 2 when tune.lua.openlibs was used to restrict the available libraries. The initialisation is now lazy. hlua_init() is idempotent: it returns immediately if the states already exist (hlua_states[0] != NULL). It is called explicitly from the three config keyword handlers that need the Lua states to be live before they can do their work (lua-load, lua-load-per-thread, lua-prepend-path) and from tune.lua.openlibs, after the hlua_openlibs_flags variable has been updated, so that the states are always created with the correct library set. hlua_post_init() calls hlua_init() unconditionally as a safety net, covering the case where no Lua directive appeared in the configuration at all (no global section, or only pure-tuning directives such as timeouts and memory limits), and ensuring correct behaviour with multiple consecutive global sections. The pure flag-setting handlers (tune.lua.session-timeout, tune.lua.task-timeout, tune.lua.service-timeout, tune.lua.burst-timeout, tune.lua.forced-yield, tune.lua.maxmem, tune.lua.log.loggers, tune.lua.log.stderr, tune.lua.bool-sample-conversion) do not call hlua_init() themselves: they only write C globals that are read at runtime, not at init time, and triggering initialisation from them would silently force tune.lua.openlibs to appear before them. As a result of this change, tune.lua.openlibs must now appear before lua-load, lua-load-per-thread, and lua-prepend-path in the configuration; if any of those keywords is encountered first, the Lua states will already be initialised and tune.lua.openlibs with a non-default value will return a parse error. No backport needed. --- diff --git a/src/haproxy.c b/src/haproxy.c index 4d0f1e0da..1060fbc05 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2139,9 +2139,6 @@ static void step_init_1() if (init_acl() != 0) exit(1); - /* Initialise lua. */ - hlua_init(); - /* set modes given from cmdline */ global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE | MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING diff --git a/src/hlua.c b/src/hlua.c index 2e06fffa5..4e2d6576e 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -13392,7 +13392,18 @@ static int hlua_cfg_parse_openlibs(char **args, int section_type, struct proxy * return -1; } + /* Reject a non-default restriction if the Lua VM is already initialised, + * which happens when lua-prepend-path appeared before this directive. + */ + if (flags != HLUA_OPENLIBS_ALL && hlua_states[0]) { + memprintf(err, "the Lua VM is already initialised; " + "the library restriction can't apply correctly. Use \"%s\" before any lua keyword", + args[0]); + return -1; + } + hlua_openlibs_flags = flags; + hlua_init(); return 0; } @@ -13492,6 +13503,8 @@ static int hlua_load(char **args, int section_type, struct proxy *curpx, return -1; } + hlua_init(); + /* loading for global state */ hlua_state_id = 0; ha_set_thread(NULL); @@ -13510,6 +13523,8 @@ static int hlua_load_per_thread(char **args, int section_type, struct proxy *cur return -1; } + hlua_init(); + if (per_thread_load == NULL) { /* allocate the first entry large enough to store the final NULL */ per_thread_load = calloc(1, sizeof(*per_thread_load)); @@ -13598,6 +13613,8 @@ static int hlua_config_prepend_path(char **args, int section_type, struct proxy struct prepend_path *p = NULL; size_t i; + hlua_init(); + if (too_many_args(2, args, err, NULL)) { goto err; } @@ -13688,6 +13705,7 @@ static struct cfg_kw_list cfg_kws = {{ },{ INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); + #ifdef USE_OPENSSL /* @@ -14006,6 +14024,11 @@ int hlua_post_init() hlua_body = 0; + /* Ensure the Lua VM is initialised even if no Lua directive appeared + * in the configuration (e.g. no global section at all). + */ + hlua_init(); + #if defined(USE_OPENSSL) /* Initialize SSL server. */ if (socket_ssl->xprt->prepare_srv) { @@ -14833,6 +14856,9 @@ void hlua_init(void) { }; #endif + if (hlua_states[0]) + return; /* already initialised */ + /* Init post init function list head */ for (i = 0; i < MAX_THREADS + 1; i++) LIST_INIT(&hlua_init_functions[i]);