mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-02-04 02:20:30 +01:00
Merge branch 'wip/lua-pcall-timeout' into 'main'
lua: install a timeout hook before any pcalls to prevent infinite loops Closes #1245 See merge request libinput/libinput!1416
This commit is contained in:
commit
d72c2b7152
2 changed files with 97 additions and 0 deletions
|
|
@ -116,6 +116,8 @@ struct libinput_lua_plugin {
|
|||
|
||||
struct libinput_plugin_timer *timer;
|
||||
bool in_timer_func;
|
||||
|
||||
usec_t lua_pcall_timeout_end;
|
||||
};
|
||||
|
||||
static struct libinput_lua_plugin *
|
||||
|
|
@ -291,12 +293,34 @@ out:
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
lua_timeout_hook(lua_State *L, lua_Debug *debug)
|
||||
{
|
||||
struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
|
||||
struct libinput *libinput = lua_get_libinput(L);
|
||||
usec_t now = libinput_now(libinput);
|
||||
|
||||
if (usec_cmp(now, plugin->lua_pcall_timeout_end) > 0) {
|
||||
luaL_error(L, "Plugin execution timeout (exceeded 1 second)");
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
libinput_lua_pcall(struct libinput_lua_plugin *plugin, int narg, int nres)
|
||||
{
|
||||
lua_State *L = plugin->L;
|
||||
struct libinput *libinput = lua_get_libinput(L);
|
||||
|
||||
plugin->lua_pcall_timeout_end = usec_add_millis(libinput_now(libinput), 1000);
|
||||
|
||||
/* Hook is called every 10M instructions (10-1000ms, depending operations) */
|
||||
lua_sethook(L, lua_timeout_hook, LUA_MASKCOUNT, 10000000);
|
||||
|
||||
int rc = lua_pcall(L, narg, nres, 0);
|
||||
|
||||
/* Clear the hook */
|
||||
lua_sethook(L, NULL, 0, 0);
|
||||
plugin->lua_pcall_timeout_end = usec_from_millis(0);
|
||||
if (rc != LUA_OK) {
|
||||
auto libinput_plugin = plugin->parent;
|
||||
const char *errormsg = lua_tostring(L, -1);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <valgrind/valgrind.h>
|
||||
|
||||
#include "util-files.h"
|
||||
#include "util-strings.h"
|
||||
|
|
@ -1101,6 +1102,76 @@ START_TEST(lua_disable_wheel_debouncing)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(lua_remove_plugin_on_timeout)
|
||||
{
|
||||
enum when when = litest_test_param_get_i32(test_env->params, "when");
|
||||
_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
|
||||
_autofree_ char *lua = strdup_printf(
|
||||
"libinput:register({1})\n"
|
||||
"function infinite_loop(device)\n"
|
||||
" local i = 0\n"
|
||||
" while true do\n"
|
||||
" i = i + 1\n"
|
||||
" end\n"
|
||||
"end\n"
|
||||
"function new_device(device)\n"
|
||||
" device:connect(\"evdev-frame\", infinite_loop)\n"
|
||||
"end\n"
|
||||
"%s libinput:connect(\"new-evdev-device\", infinite_loop)\n"
|
||||
"%s libinput:connect(\"new-evdev-device\", new_device)\n",
|
||||
when == DEVICE_NEW ? "" : "--",
|
||||
when == FIRST_FRAME ? "" : "--");
|
||||
|
||||
_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
|
||||
_litest_context_destroy_ struct libinput *li =
|
||||
litest_create_context_with_plugindir(tmpdir->path);
|
||||
_destroy_(litest_device) *dev = NULL;
|
||||
|
||||
litest_with_logcapture(li, capture) {
|
||||
libinput_plugin_system_load_plugins(li,
|
||||
LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
|
||||
litest_drain_events(li);
|
||||
|
||||
usec_t before = usec_from_now();
|
||||
|
||||
dev = litest_add_device(li, LITEST_MOUSE);
|
||||
litest_drain_events(li);
|
||||
|
||||
if (when == FIRST_FRAME) {
|
||||
litest_event(dev, EV_KEY, BTN_LEFT, 1);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
litest_dispatch(li);
|
||||
litest_event(dev, EV_KEY, BTN_LEFT, 0);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
litest_dispatch(li);
|
||||
}
|
||||
|
||||
usec_t after = usec_from_now();
|
||||
usec_t elapsed = usec_delta(after, before);
|
||||
|
||||
/* verify the timeout kicked in around ~1 second (but less than 2s) */
|
||||
litest_assert_int_ge(usec_to_millis(elapsed), 1000U);
|
||||
if (!RUNNING_ON_VALGRIND)
|
||||
litest_assert_int_lt(usec_to_millis(elapsed), 2000U);
|
||||
|
||||
size_t index = 0;
|
||||
litest_assert(strv_find_substring(capture->errors,
|
||||
"Plugin execution timeout",
|
||||
&index));
|
||||
litest_assert_str_in("exceeded 1 second", capture->errors[index]);
|
||||
|
||||
litest_assert(strv_find_substring(capture->errors,
|
||||
"unloading after error",
|
||||
NULL));
|
||||
}
|
||||
/* device events should go through now */
|
||||
litest_event(dev, EV_KEY, BTN_LEFT, 1);
|
||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||
litest_dispatch(li);
|
||||
litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
TEST_COLLECTION(lua)
|
||||
{
|
||||
/* clang-format off */
|
||||
|
|
@ -1173,6 +1244,8 @@ TEST_COLLECTION(lua)
|
|||
litest_add_parametrized_no_device(lua_disable_button_debounce, params);
|
||||
litest_add_parametrized_no_device(lua_disable_touchpad_jump_detection, params);
|
||||
litest_add_parametrized_no_device(lua_disable_wheel_debouncing, params);
|
||||
|
||||
litest_add_parametrized_no_device(lua_remove_plugin_on_timeout, params);
|
||||
}
|
||||
/* clang-format on */
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue