mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-04-02 05:10:42 +02:00
lua: separate the API from the metatables
Previously we had one vtable for the libinputplugin and EvdevDevice objects. This allowed plugins to call __gc(), a decidedly internal method. This fixes a use-after-free: A plugin that called EvdevDevice::__gc() frees the plugin's copy of device->name but leaves the pointer in-place, a subsequent call will thus cause a UAF read. Fix this by separating what is the object's metatable from the public methods that are accessible to a plugin. CVE-2026-35094 Fixes: #1272 Found-by: Koen Tange <koen@monokles.eu> Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1459>
This commit is contained in:
parent
7face63bd5
commit
45dfd0f030
2 changed files with 73 additions and 14 deletions
|
|
@ -562,6 +562,12 @@ libinputplugin_unregister(lua_State *L)
|
|||
return luaL_error(L, "@@unregistering@@");
|
||||
}
|
||||
|
||||
static int
|
||||
readonly_newindex(lua_State *L)
|
||||
{
|
||||
return luaL_error(L, "attempt to modify a read-only table");
|
||||
}
|
||||
|
||||
static int
|
||||
libinputplugin_gc(lua_State *L)
|
||||
{
|
||||
|
|
@ -673,7 +679,28 @@ libinputplugin_log_error(lua_State *L)
|
|||
return libinputplugin_log(L, LIBINPUT_LOG_PRIORITY_ERROR);
|
||||
}
|
||||
|
||||
static const struct luaL_Reg libinputplugin_vtable[] = {
|
||||
static void
|
||||
setup_vfuncs(lua_State *L,
|
||||
const char *metatable_name,
|
||||
const struct luaL_Reg *vfuncs,
|
||||
const struct luaL_Reg *public_methods)
|
||||
{
|
||||
luaL_newmetatable(L, metatable_name);
|
||||
luaL_setfuncs(L, vfuncs, 0);
|
||||
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, public_methods, 0);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
/* set metatable.__metatable = false to prevent a script from getmetatable(),
|
||||
which is blocked anyway but safe and sorry and whatnot */
|
||||
lua_pushboolean(L, 0);
|
||||
lua_setfield(L, -2, "__metatable");
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static const struct luaL_Reg libinputplugin_methods[] = {
|
||||
{ "now", libinputplugin_now },
|
||||
{ "version", libinputplugin_version },
|
||||
{ "connect", libinputplugin_connect },
|
||||
|
|
@ -685,18 +712,18 @@ static const struct luaL_Reg libinputplugin_vtable[] = {
|
|||
{ "log_debug", libinputplugin_log_debug },
|
||||
{ "log_info", libinputplugin_log_info },
|
||||
{ "log_error", libinputplugin_log_error },
|
||||
{ "__gc", libinputplugin_gc },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct luaL_Reg libinputplugin_meta[] = { { "__gc", libinputplugin_gc },
|
||||
{ "__newindex",
|
||||
readonly_newindex },
|
||||
{ NULL, NULL } };
|
||||
|
||||
static void
|
||||
libinputplugin_init(lua_State *L)
|
||||
{
|
||||
luaL_newmetatable(L, PLUGIN_METATABLE);
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2); /* push metatable */
|
||||
lua_settable(L, -3); /* metatable.__index = metatable */
|
||||
luaL_setfuncs(L, libinputplugin_vtable, 0);
|
||||
setup_vfuncs(L, PLUGIN_METATABLE, libinputplugin_meta, libinputplugin_methods);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -1073,7 +1100,7 @@ evdevdevice_gc(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg evdevdevice_vtable[] = {
|
||||
static const struct luaL_Reg evdevdevice_methods[] = {
|
||||
{ "info", evdevdevice_info },
|
||||
{ "name", evdevdevice_name },
|
||||
{ "usages", evdevdevice_usages },
|
||||
|
|
@ -1087,18 +1114,17 @@ static const struct luaL_Reg evdevdevice_vtable[] = {
|
|||
{ "prepend_frame", evdevdevice_prepend_frame },
|
||||
{ "append_frame", evdevdevice_append_frame },
|
||||
{ "disable_feature", evdevdevice_disable_feature },
|
||||
{ "__gc", evdevdevice_gc },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct luaL_Reg evdevdevice_meta[] = { { "__gc", evdevdevice_gc },
|
||||
{ "__newindex", readonly_newindex },
|
||||
{ NULL, NULL } };
|
||||
|
||||
static void
|
||||
evdevdevice_init(lua_State *L)
|
||||
{
|
||||
luaL_newmetatable(L, EVDEV_DEVICE_METATABLE);
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2); /* push metatable */
|
||||
lua_settable(L, -3); /* metatable.__index = metatable */
|
||||
luaL_setfuncs(L, evdevdevice_vtable, 0);
|
||||
setup_vfuncs(L, EVDEV_DEVICE_METATABLE, evdevdevice_meta, evdevdevice_methods);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -526,6 +526,38 @@ START_TEST(lua_disallowed_functions)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(lua_gc_not_accessible)
|
||||
{
|
||||
_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
|
||||
const char *lua =
|
||||
"libinput:register({1})\n"
|
||||
"assert(libinput.__gc == nil)\n"
|
||||
"function check_device_gc(device)\n"
|
||||
" assert(device.__gc == nil)\n"
|
||||
" libinput:log_info(\"gc_not_accessible: ok\")\n"
|
||||
"end\n"
|
||||
"libinput:connect(\"new-evdev-device\", check_device_gc)\n";
|
||||
|
||||
_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
|
||||
_litest_context_destroy_ struct libinput *li =
|
||||
litest_create_context_with_plugindir(tmpdir->path);
|
||||
if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
|
||||
libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
|
||||
|
||||
litest_with_logcapture(li, capture) {
|
||||
libinput_plugin_system_load_plugins(li,
|
||||
LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
|
||||
litest_drain_events(li);
|
||||
|
||||
_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
|
||||
litest_drain_events(li);
|
||||
|
||||
litest_assert_logcapture_no_errors(capture);
|
||||
litest_assert_strv_substring(capture->infos, "gc_not_accessible: ok");
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(lua_frame_handler)
|
||||
{
|
||||
_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
|
||||
|
|
@ -1219,6 +1251,7 @@ TEST_COLLECTION(lua)
|
|||
litest_add_no_device(lua_register_multiversions);
|
||||
litest_add_no_device(lua_allowed_functions);
|
||||
litest_add_no_device(lua_disallowed_functions);
|
||||
litest_add_no_device(lua_gc_not_accessible);
|
||||
|
||||
litest_add_no_device(lua_frame_handler);
|
||||
litest_add_no_device(lua_device_info);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue