]> git.kaiwu.me - haproxy.git/commit
MINOR: lua: defer Lua VM initialisation to the first Lua config keyword
authorWilliam Lallemand <wlallemand@irq6.net>
Wed, 27 May 2026 17:54:48 +0000 (19:54 +0200)
committerWilliam Lallemand <wlallemand@irq6.net>
Wed, 27 May 2026 18:49:14 +0000 (20:49 +0200)
commit8b0e2920a3aa0b50ed5f995fd7a6d19b869b7cff
tree626d1f01437fb1de53d4d6024b3359ac47efd186
parent091768ab3ed11ce78ca60168bd2bcca950896758
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.
src/haproxy.c
src/hlua.c