Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libinput for openSUSE:Factory checked in at 2026-04-04 19:05:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libinput (Old) and /work/SRC/openSUSE:Factory/.libinput.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libinput" Sat Apr 4 19:05:27 2026 rev:130 rq:1344309 version:1.31.1 Changes: -------- --- /work/SRC/openSUSE:Factory/libinput/libinput.changes 2026-02-16 13:22:58.896203081 +0100 +++ /work/SRC/openSUSE:Factory/.libinput.new.21863/libinput.changes 2026-04-04 19:06:45.567506483 +0200 @@ -1,0 +2,8 @@ +Thu Apr 2 07:22:08 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 1.31.1 + * Fixed sandbox escape in libinput plugins [CVE-2026-35093] + * Use after free allowing information leak in libinput plugins + [CVE-2026-35094] + +------------------------------------------------------------------- Old: ---- libinput-1.31.0.tar.gz New: ---- libinput-1.31.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libinput.spec ++++++ --- /var/tmp/diff_new_pack.7x0hJ0/_old 2026-04-04 19:06:48.203614564 +0200 +++ /var/tmp/diff_new_pack.7x0hJ0/_new 2026-04-04 19:06:48.239616039 +0200 @@ -37,7 +37,7 @@ %define lname libinput10 %define pname libinput Name: libinput%{?xsuffix} -Version: 1.31.0 +Version: 1.31.1 Release: 0 Summary: Input device and event processing library License: MIT ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.7x0hJ0/_old 2026-04-04 19:06:48.591630472 +0200 +++ /var/tmp/diff_new_pack.7x0hJ0/_new 2026-04-04 19:06:48.619631620 +0200 @@ -1,5 +1,5 @@ -mtime: 1771003500 -commit: 53dd0d605bbfab5b31b2f4d9e405601ad6561edf1e13b236b083ae58295b0915 +mtime: 1775114676 +commit: 6cfb5d9611a339e3f447be0287373aed1776c9a947e0c8c8296b75ee548aa3f1 url: https://src.opensuse.org/jengelh/libinput revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-04-02 09:24:44.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ libinput-1.31.0.tar.gz -> libinput-1.31.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libinput-1.31.0/meson.build new/libinput-1.31.1/meson.build --- old/libinput-1.31.0/meson.build 2026-02-13 00:25:46.000000000 +0100 +++ new/libinput-1.31.1/meson.build 2026-04-02 03:04:12.000000000 +0200 @@ -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') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libinput-1.31.0/src/libinput-plugin-lua.c new/libinput-1.31.1/src/libinput-plugin-lua.c --- old/libinput-1.31.0/src/libinput-plugin-lua.c 2026-02-13 00:25:46.000000000 +0100 +++ new/libinput-1.31.1/src/libinput-plugin-lua.c 2026-04-02 03:04:12.000000000 +0200 @@ -563,6 +563,12 @@ } 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) { LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE); @@ -673,7 +679,28 @@ 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 @@ { "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 @@ 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 @@ { "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 @@ return NULL; } - int ret = luaL_loadfile(L, path); + int ret = luaL_loadfilex(L, path, "t"); if (ret == LUA_OK) { plugin->L = steal(&L); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libinput-1.31.0/test/test-plugins-lua.c new/libinput-1.31.1/test/test-plugins-lua.c --- old/libinput-1.31.0/test/test-plugins-lua.c 2026-02-13 00:25:46.000000000 +0100 +++ new/libinput-1.31.1/test/test-plugins-lua.c 2026-04-02 03:04:12.000000000 +0200 @@ -526,6 +526,38 @@ } 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 @@ } 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 @@ 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);
