Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP2:Update
libinput
libinput-CVE-2022-1215.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File libinput-CVE-2022-1215.patch of Package libinput
From 0fd73ca67649b359abc68ee7f6c6778c97681f76 Mon Sep 17 00:00:00 2001 From: Peter Hutterer <peter.hutterer@who-t.net> Date: Wed, 30 Mar 2022 09:25:22 +1000 Subject: [PATCH] evdev: strip the device name of format directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a format string vulnerabilty. evdev_log_message() composes a format string consisting of a fixed prefix (including the rendered device name) and the passed-in format buffer. This format string is then passed with the arguments to the actual log handler, which usually and eventually ends up being printf. If the device name contains a printf-style format directive, these ended up in the format string and thus get interpreted correctly, e.g. for a device "Foo%sBar" the log message vs printf invocation ends up being: evdev_log_message(device, "some message %s", "some argument"); printf("event9 - Foo%sBar: some message %s", "some argument"); This can enable an attacker to execute malicious code with the privileges of the process using libinput. To exploit this, an attacker needs to be able to create a kernel device with a malicious name, e.g. through /dev/uinput or a Bluetooth device. To fix this, convert any potential format directives in the device name by duplicating percentages. Pre-rendering the device to avoid the issue altogether would be nicer but the current log level hooks do not easily allow for this. The device name is the only user-controlled part of the format string. A second potential issue is the sysname of the device which is also sanitized. This issue was found by Albin Eldstål-Ahrens and Benjamin Svensson from Assured AB. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Backported by Mike Gorse <mgorse@suse.com> --- diff -urpN libinput-1.10.5.orig/meson.build libinput-1.10.5/meson.build --- libinput-1.10.5.orig/meson.build 2018-04-18 21:02:13.000000000 -0500 +++ libinput-1.10.5/meson.build 2022-04-14 14:18:04.401410487 -0500 @@ -543,6 +543,7 @@ if get_option('tests') 'test/litest-device-calibrated-touchscreen.c', 'test/litest-device-cyborg-rat-5.c', 'test/litest-device-elantech-touchpad.c', + 'test/litest-device-format-string.c', 'test/litest-device-generic-singletouch.c', 'test/litest-device-gpio-keys.c', 'test/litest-device-huion-pentablet.c', diff -urpN libinput-1.10.5.orig/src/evdev.c libinput-1.10.5/src/evdev.c --- libinput-1.10.5.orig/src/evdev.c 2018-04-18 21:02:13.000000000 -0500 +++ libinput-1.10.5/src/evdev.c 2022-04-14 16:36:52.661941282 -0500 @@ -1913,6 +1913,30 @@ udev_device_should_be_ignored(struct ude return value && !streq(value, "0"); } +static inline char * +str_sanitize(const char *str) +{ + if (!str) + return NULL; + + if (!strchr(str, '%')) + return strdup(str); + + size_t slen = min(strlen(str), 512); + char *sanitized = zalloc(2 * slen + 1); + const char *src = str; + char *dst = sanitized; + + for (size_t i = 0; i < slen; i++) { + if (*src == '%') + *dst++ = '%'; + *dst++ = *src++; + } + *dst = '\0'; + + return sanitized; +} + struct evdev_device * evdev_device_create(struct libinput_seat *seat, struct udev_device *udev_device) @@ -1920,19 +1944,19 @@ evdev_device_create(struct libinput_seat struct libinput *libinput = seat->libinput; struct evdev_device *device = NULL; int rc; - int fd; + int fd = -1; int unhandled_device = 0; const char *devnode = udev_device_get_devnode(udev_device); - const char *sysname = udev_device_get_sysname(udev_device); + char *sysname = str_sanitize(udev_device_get_sysname(udev_device)); if (!devnode) { log_info(libinput, "%s: no device node associated\n", sysname); - return NULL; + goto err; } if (udev_device_should_be_ignored(udev_device)) { log_debug(libinput, "%s: device is ignored\n", sysname); - return NULL; + goto err; } /* Use non-blocking mode so that we can loop on read on @@ -1946,13 +1970,15 @@ evdev_device_create(struct libinput_seat sysname, devnode, strerror(-fd)); - return NULL; + goto err; } if (!evdev_device_have_same_syspath(udev_device, fd)) goto err; device = zalloc(sizeof *device); + device->sysname = sysname; + sysname = NULL; libinput_device_init(&device->base, seat); libinput_seat_ref(seat); @@ -1975,6 +2001,9 @@ evdev_device_create(struct libinput_seat device->dispatch = NULL; device->fd = fd; device->devname = libevdev_get_name(device->evdev); + /* the log_prefix_name is used as part of a printf format string and + * must not contain % directives, see evdev_log_msg */ + device->log_prefix_name = str_sanitize(device->devname); device->scroll.threshold = 5.0; /* Default may be overridden */ device->scroll.direction_lock_threshold = 5.0; /* Default may be overridden */ device->scroll.direction = 0; @@ -2017,10 +2046,15 @@ evdev_device_create(struct libinput_seat return device; err: - if (fd >= 0) + if (fd >= 0) { close_restricted(libinput, fd); - if (device) - evdev_device_destroy(device); + if (device) { + unhandled_device = device->seat_caps == 0; + evdev_device_destroy(device); + } + } + + free(sysname); return unhandled_device ? EVDEV_UNHANDLED_DEVICE : NULL; } @@ -2034,7 +2068,7 @@ evdev_device_get_output(struct evdev_dev const char * evdev_device_get_sysname(struct evdev_device *device) { - return udev_device_get_sysname(device->udev_device); + return device->sysname; } const char * @@ -2540,6 +2574,8 @@ evdev_device_destroy(struct evdev_device if (device->base.group) libinput_device_group_unref(device->base.group); + free(device->log_prefix_name); + free(device->sysname); free(device->output_name); filter_destroy(device->pointer.filter); libinput_timer_destroy(&device->scroll.timer); diff -urpN libinput-1.10.5.orig/src/evdev.h libinput-1.10.5/src/evdev.h --- libinput-1.10.5.orig/src/evdev.h 2018-04-18 21:02:13.000000000 -0500 +++ libinput-1.10.5/src/evdev.h 2022-04-14 14:13:07.599823534 -0500 @@ -179,6 +179,8 @@ struct evdev_device { struct udev_device *udev_device; char *output_name; const char *devname; + char *log_prefix_name; + char *sysname; bool was_removed; int fd; enum evdev_device_seat_capability seat_caps; @@ -705,7 +707,7 @@ evdev_log_msg_va(struct evdev_device *de sizeof(buf), "%-7s - %s%s%s", evdev_device_get_sysname(device), - (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? device->devname : "", + (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? device->log_prefix_name : "", (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? ": " : "", format); diff -urpN libinput-1.10.5.orig/test/litest-device-format-string.c libinput-1.10.5/test/litest-device-format-string.c --- libinput-1.10.5.orig/test/litest-device-format-string.c 1969-12-31 18:00:00.000000000 -0600 +++ libinput-1.10.5/test/litest-device-format-string.c 2022-04-14 14:13:13.439854762 -0500 @@ -0,0 +1,56 @@ + +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "litest.h" +#include "litest-int.h" + +static struct input_id input_id = { + .bustype = 0x3, + .vendor = 0x0123, + .product = 0x0456, +}; + +static int events[] = { + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + EV_KEY, BTN_MIDDLE, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_REL, REL_WHEEL_HI_RES, + -1 , -1, +}; + +TEST_DEVICE("mouse-format-string", + .type = LITEST_MOUSE_FORMAT_STRING, + .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL, + .interface = NULL, + + .name = "Evil %s %d %x Mouse %p %", + .id = &input_id, + .absinfo = NULL, + .events = events, +) diff -urpN libinput-1.10.5.orig/test/litest.h libinput-1.10.5/test/litest.h --- libinput-1.10.5.orig/test/litest.h 2018-04-18 21:02:13.000000000 -0500 +++ libinput-1.10.5/test/litest.h 2022-04-14 14:52:06.952331912 -0500 @@ -271,6 +271,7 @@ enum litest_device_type { LITEST_WACOM_BAMBOO_2FG_FINGER, LITEST_HP_WMI_HOTKEYS, LITEST_MS_NANO_TRANSCEIVER_MOUSE, + LITEST_MOUSE_FORMAT_STRING, }; enum litest_device_feature {
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor