[Git][xorg-team/lib/libinput][debian-unstable] 7 commits: lua: separate the API from the metatables
"Timo Aaltonen (@tjaalton)" <[email protected]>
| Newsgroups | gmane.linux.debian.devel.x |
|---|---|
| Message-ID | <[email protected]> |
Timo Aaltonen pushed to branch debian-unstable at X Strike Force / lib / libinput Commits: 45506c7b by Peter Hutterer at 2026-04-02T10:17:52+10: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 <[email protected]> (cherry picked from commit 45dfd0f0301af855f068df27b2e40cc9f5713acd) Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1461> - - - - - 49f9a815 by Peter Hutterer at 2026-04-02T10:18:00+10:00 lua: force text mode for loading plugins luaL_loadfile() by default allows for both text files and precompiled lua files. Precompiled files are not verified on load allowing for a sandbox escape. CVE-2026-35093 Fixes: #1271 Found-by: Koen Tange <[email protected]> (cherry picked from commit 356c498fd4ba25ec99f6866fc96847ec3d1f16bf) Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1461> - - - - - 19206869 by Peter Hutterer at 2026-04-02T11:04:12+10:00 libinput 1.31.1 Signed-off-by: Peter Hutterer <[email protected]> - - - - - 3b10f1fa by Timo Aaltonen at 2026-04-02T22:19:35+03:00 Merge branch 'upstream-unstable' into debian-unstable - - - - - 3f108ac7 by Timo Aaltonen at 2026-04-02T22:20:38+03:00 version bump - - - - - 14966aa8 by Timo Aaltonen at 2026-04-02T22:24:42+03:00 Add quirks support for Goodix touchpad 27C6:0F96 and 27C6:0F90 - - - - - dcb3df27 by Timo Aaltonen at 2026-04-02T22:25:03+03:00 releasing package libinput version 1.31.1-1 - - - - - 7 changed files: - debian/changelog - + debian/patches/0001-Add-Goodix-haptic-touchpad-27C6-0F90-support.patch - + debian/patches/0002-quirks-add-support-for-Goodix-touchpad-27C6-0F96.patch - debian/patches/series - meson.build - src/libinput-plugin-lua.c - test/test-plugins-lua.c Changes: ===================================== debian/changelog ===================================== @@ -1,3 +1,14 @@ +libinput (1.31.1-1) unstable; urgency=medium + + * New upstream release. (Closes: #1132550) + - CVE-2026-35093 + - CVE-2026-35094 + * Add quirks support for Goodix touchpad 27C6:0F96 and 27C6:0F90 (LP: #2146518) + d/p/0001-Add-Goodix-haptic-touchpad-27C6-0F90-support.patch + d/p/0002-quirks-add-support-for-Goodix-touchpad-27C6-0F96.patch + + -- Timo Aaltonen <[email protected]> Thu, 02 Apr 2026 22:24:51 +0300 + libinput (1.31.0-1) unstable; urgency=medium * New upstream release. ===================================== debian/patches/0001-Add-Goodix-haptic-touchpad-27C6-0F90-support.patch ===================================== @@ -0,0 +1,29 @@ +From: Freeman Zhang <[email protected]> +Date: Sun, 22 Mar 2026 16:19:47 +0800 +Subject: [PATCH] Add Goodix haptic touchpad 27C6:0F90 support + +Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1451> + +Origin: upstream, https://gitlab.freedesktop.org/libinput/libinput/-/commit/a1c5f35a69493a16354391b3dfbac4ad35e3bcab +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2146518 +Signed-off-by: Bin Li <[email protected]> +--- + quirks/30-vendor-goodix.quirks | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/quirks/30-vendor-goodix.quirks b/quirks/30-vendor-goodix.quirks +index b7aab62..e294156 100644 +--- a/quirks/30-vendor-goodix.quirks ++++ b/quirks/30-vendor-goodix.quirks +@@ -17,3 +17,11 @@ MatchVendor=0x27C6 + MatchProduct=0x0F95 + MatchUdevType=touchpad + AttrInputProp=+INPUT_PROP_PRESSUREPAD ++ ++# "GXTP5420 Touchpad": pressure touchpad mostly used in Lenovo laptops. ++[Goodix Haptic Touchpad (27C6:0F90)] ++MatchBus=i2c ++MatchVendor=0x27C6 ++MatchProduct=0x0F90 ++MatchUdevType=touchpad ++AttrInputProp=+INPUT_PROP_PRESSUREPAD ===================================== debian/patches/0002-quirks-add-support-for-Goodix-touchpad-27C6-0F96.patch ===================================== @@ -0,0 +1,42 @@ +From: Richie Roy Jayme <[email protected]> +Date: Wed, 25 Mar 2026 18:33:21 +0900 +Subject: [PATCH] quirks: add support for Goodix touchpad GXTP5400:00 + 27C6:0F96 + +This commit adds a specific vendor HWID for Goodix Haptic Touchpad to +improve detection and handling. + +Signed-off-by: Richie Roy Jayme <[email protected]> +Signed-off-by: Richie Roy Jayme <[email protected]> +Reviewed-by: Vishnu Sankar <[email protected]> +Reviewed-by: Vishnu Sankar <[email protected]> +Tested-by: Ameer Ivan Julkarnain <[email protected]> +Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1455> + +Origin: backport, https://gitlab.freedesktop.org/libinput/libinput/-/commit/baddf1e2b6db43d4e726d58a940f7720d9368c57 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2146518 +Signed-off-by: Bin Li <[email protected]> +--- + quirks/30-vendor-goodix.quirks | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/quirks/30-vendor-goodix.quirks b/quirks/30-vendor-goodix.quirks +index e294156..53b0391 100644 +--- a/quirks/30-vendor-goodix.quirks ++++ b/quirks/30-vendor-goodix.quirks +@@ -9,6 +9,15 @@ MatchProduct=0x01E8 + MatchUdevType=touchpad + AttrInputProp=+INPUT_PROP_PRESSUREPAD + ++# "GXTP5400 Touchpad": pressure touchpad mostly used in Lenovo laptops. ++# GXTP5400:00 27C6:0F96 Touchpad ++[Goodix Haptic Touchpad (27C6:0F96)] ++MatchBus=i2c ++MatchVendor=0x27C6 ++MatchProduct=0x0F96 ++MatchUdevType=touchpad ++AttrInputProp=+INPUT_PROP_PRESSUREPAD ++ + # "GXTP5420 Touchpad": pressure touchpad mostly used in Lenovo laptops. + # GXTP5420:00 27C6:0F95 Touchpad + [Goodix Haptic Touchpad (27C6:0F95)] ===================================== debian/patches/series ===================================== @@ -1,2 +1,4 @@ #placeholder tools-remove-references-to-libinput_quir.patch +0001-Add-Goodix-haptic-touchpad-27C6-0F90-support.patch +0002-quirks-add-support-for-Goodix-touchpad-27C6-0F96.patch ===================================== meson.build ===================================== @@ -1,5 +1,5 @@ project('libinput', 'c', - version : '1.31.0', + version : '1.31.1', license : 'MIT/Expat', default_options : [ 'c_std=gnu99', 'warning_level=2' ], meson_version : '>= 0.64.0') ===================================== src/libinput-plugin-lua.c ===================================== @@ -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 @@ -1336,7 +1362,7 @@ libinput_lua_plugin_new_from_path(struct libinput *libinput, const char *path) return NULL; } - int ret = luaL_loadfile(L, path); + int ret = luaL_loadfilex(L, path, "t"); if (ret == LUA_OK) { plugin->L = steal(&L); ===================================== test/test-plugins-lua.c ===================================== @@ -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); @@ -1172,10 +1204,79 @@ START_TEST(lua_remove_plugin_on_timeout) } END_TEST +/* Pre-compiled Lua 5.4 bytecode for the following source: + * + * libinput:register({1}) + * libinput:connect("new-evdev-device", function(device) + * libinput:log_info("loaded from binary lua file") + * end) + * + * To regenerate: + * luac5.4 -o /dev/stdout /tmp/plugin.lua | xxd -i + */ +static const unsigned char binary_lua_plugin[] = { + 0x1b, 0x4c, 0x75, 0x61, 0x54, 0x00, 0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a, 0x04, + 0x08, 0x08, 0x78, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x77, 0x40, 0x01, 0x9b, 0x40, 0x74, 0x65, 0x73, 0x74, 0x2f, + 0x31, 0x30, 0x2d, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x2d, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x6c, 0x75, 0x61, 0x80, 0x80, 0x00, 0x01, 0x04, 0x8e, + 0x51, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x01, 0x13, + 0x01, 0x00, 0x01, 0x52, 0x00, 0x00, 0x00, 0x81, 0x01, 0x00, 0x80, 0x4e, 0x01, + 0x01, 0x00, 0x44, 0x00, 0x03, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, + 0x02, 0x03, 0x81, 0x01, 0x00, 0xcf, 0x01, 0x00, 0x00, 0x44, 0x00, 0x04, 0x01, + 0x46, 0x00, 0x01, 0x01, 0x84, 0x04, 0x89, 0x6c, 0x69, 0x62, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x04, 0x89, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x04, + 0x88, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x04, 0x91, 0x6e, 0x65, 0x77, + 0x2d, 0x65, 0x76, 0x64, 0x65, 0x76, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x81, 0x01, 0x00, 0x00, 0x81, 0x80, 0x8d, 0x8f, 0x01, 0x00, 0x04, 0x85, 0x8b, + 0x00, 0x00, 0x00, 0x94, 0x80, 0x01, 0x01, 0x83, 0x01, 0x01, 0x00, 0xc4, 0x00, + 0x03, 0x01, 0xc7, 0x00, 0x01, 0x00, 0x83, 0x04, 0x89, 0x6c, 0x69, 0x62, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x04, 0x89, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x04, 0x9c, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x6c, 0x75, 0x61, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x81, 0x00, 0x00, 0x00, 0x80, 0x85, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x80, 0x81, 0x87, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x80, 0x85, + 0x81, 0x85, 0x5f, 0x45, 0x4e, 0x56, 0x8e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xfe, 0x02, 0x80, 0x80, 0x81, 0x85, 0x5f, + 0x45, 0x4e, 0x56, +}; + +START_TEST(lua_reject_precompiled_files) +{ + _destroy_(tmpdir) *tmpdir = tmpdir_create(NULL); + + /* Write the binary bytecode to a .lua file in the tmpdir. + * Binary (pre-compiled) Lua files must be rejected by the + * plugin loader for security reasons. */ + _autofree_ char *path = strdup_printf("%s/10-binary-plugin.lua", tmpdir->path); + _autoclose_ int fd = open(path, O_WRONLY | O_CREAT, 0644); + litest_assert_errno_success(fd); + + ssize_t written = write(fd, binary_lua_plugin, sizeof(binary_lua_plugin)); + litest_assert_int_eq((int)written, (int)sizeof(binary_lua_plugin)); + fsync(fd); + + _litest_context_destroy_ struct libinput *li = + litest_create_context_with_plugindir(tmpdir->path); + + litest_with_logcapture(li, capture) { + libinput_plugin_system_load_plugins(li, + LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE); + litest_drain_events(li); + + size_t index = 0; + litest_assert( + strv_find_substring(capture->errors, "Failed to load", &index)); + litest_assert_str_in(path, capture->errors[index]); + } +} +END_TEST + TEST_COLLECTION(lua) { /* clang-format off */ litest_add_no_device(lua_load_failure); + litest_add_no_device(lua_reject_precompiled_files); litest_with_parameters(params, "content", 'I', 6, litest_named_i32(EMPTY), @@ -1219,6 +1320,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); View it on GitLab: https://salsa.debian.org/xorg-team/lib/libinput/-/compare/c264d27be4ecd2c69555a96da6236bf7e5900548...dcb3df2705bbc80b3d30b1aa315851aa897fa677 -- View it on GitLab: https://salsa.debian.org/xorg-team/lib/libinput/-/compare/c264d27be4ecd2c69555a96da6236bf7e5900548...dcb3df2705bbc80b3d30b1aa315851aa897fa677 You're receiving this email because of your account on salsa.debian.org.