summaryrefslogtreecommitdiff
path: root/0001-evdev-strip-the-device-name-of-format-directives.patch
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2024-08-06 02:27:05 +0000
committerCoprDistGit <infra@openeuler.org>2024-08-06 02:27:05 +0000
commitf60bce8d08e3f94364f8a5f3c9d0c37615a3fbda (patch)
treee779f43cedda0a31c8980fe0644f8472e36b3cb1 /0001-evdev-strip-the-device-name-of-format-directives.patch
parentfc33180046264bd1f3abf086ffc74fde2c30a9f6 (diff)
automatic import of libinputopeneuler24.03_LTS
Diffstat (limited to '0001-evdev-strip-the-device-name-of-format-directives.patch')
-rw-r--r--0001-evdev-strip-the-device-name-of-format-directives.patch359
1 files changed, 359 insertions, 0 deletions
diff --git a/0001-evdev-strip-the-device-name-of-format-directives.patch b/0001-evdev-strip-the-device-name-of-format-directives.patch
new file mode 100644
index 0000000..f72ca08
--- /dev/null
+++ b/0001-evdev-strip-the-device-name-of-format-directives.patch
@@ -0,0 +1,359 @@
+From 762da0ec8832a2b8dd0fd11ccb34603391c2893c 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, and independently by Lukas Lamster.
+
+Fixes #752
+
+Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
+(cherry picked from commit a423d7d3269dc32a87384f79e29bb5ac021c83d1)
+(cherry picked from commit 04f22107e1a2ead05401d9169fa4306e8c7eefad)
+---
+ meson.build | 1 +
+ src/evdev.c | 31 +++++++++++------
+ src/evdev.h | 6 ++--
+ src/util-strings.h | 30 ++++++++++++++++
+ test/litest-device-format-string.c | 56 ++++++++++++++++++++++++++++++
+ test/litest.h | 1 +
+ test/test-utils.c | 26 ++++++++++++++
+ 7 files changed, 139 insertions(+), 12 deletions(-)
+ create mode 100644 test/litest-device-format-string.c
+
+diff --git a/meson.build b/meson.build
+index d6b06cca..a909ff19 100644
+--- a/meson.build
++++ b/meson.build
+@@ -732,6 +732,7 @@ if get_option('tests')
+ 'test/litest-device-dell-canvas-totem-touch.c',
+ 'test/litest-device-elantech-touchpad.c',
+ 'test/litest-device-elan-tablet.c',
++ 'test/litest-device-format-string.c',
+ 'test/litest-device-generic-pressurepad.c',
+ 'test/litest-device-generic-singletouch.c',
+ 'test/litest-device-gpio-keys.c',
+diff --git a/src/evdev.c b/src/evdev.c
+index d8dfdadd..0b4b3590 100644
+--- a/src/evdev.c
++++ b/src/evdev.c
+@@ -2291,19 +2291,19 @@ evdev_device_create(struct libinput_seat *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
+@@ -2317,13 +2317,15 @@ evdev_device_create(struct libinput_seat *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);
+@@ -2346,6 +2348,9 @@ evdev_device_create(struct libinput_seat *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;
+@@ -2386,12 +2391,16 @@ evdev_device_create(struct libinput_seat *seat,
+ return device;
+
+ err:
+- close_restricted(libinput, fd);
+- if (device) {
+- unhandled_device = device->seat_caps == 0;
+- evdev_device_destroy(device);
++ if (fd >= 0) {
++ close_restricted(libinput, fd);
++ if (device) {
++ unhandled_device = device->seat_caps == 0;
++ evdev_device_destroy(device);
++ }
+ }
+
++ free(sysname);
++
+ return unhandled_device ? EVDEV_UNHANDLED_DEVICE : NULL;
+ }
+
+@@ -2404,7 +2413,7 @@ evdev_device_get_output(struct evdev_device *device)
+ const char *
+ evdev_device_get_sysname(struct evdev_device *device)
+ {
+- return udev_device_get_sysname(device->udev_device);
++ return device->sysname;
+ }
+
+ const char *
+@@ -3001,6 +3010,8 @@ evdev_device_destroy(struct evdev_device *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 --git a/src/evdev.h b/src/evdev.h
+index c7d130f8..980c5943 100644
+--- a/src/evdev.h
++++ b/src/evdev.h
+@@ -169,6 +169,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;
+@@ -786,7 +788,7 @@ evdev_log_msg(struct evdev_device *device,
+ 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);
+
+@@ -824,7 +826,7 @@ evdev_log_msg_ratelimit(struct evdev_device *device,
+ 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 --git a/src/util-strings.h b/src/util-strings.h
+index 2a15fab3..d5a84146 100644
+--- a/src/util-strings.h
++++ b/src/util-strings.h
+@@ -43,6 +43,8 @@
+ #include <xlocale.h>
+ #endif
+
++#include "util-macros.h"
++
+ static inline bool
+ streq(const char *str1, const char *str2)
+ {
+@@ -398,3 +400,31 @@ safe_basename(const char *filename);
+
+ char *
+ trunkname(const char *filename);
++
++/**
++ * Return a copy of str with all % converted to %% to make the string
++ * acceptable as printf format.
++ */
++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;
++}
+diff --git a/test/litest-device-format-string.c b/test/litest-device-format-string.c
+new file mode 100644
+index 00000000..aed15db4
+--- /dev/null
++++ b/test/litest-device-format-string.c
+@@ -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 --git a/test/litest.h b/test/litest.h
+index b6ffbf4e..c16670e7 100644
+--- a/test/litest.h
++++ b/test/litest.h
+@@ -320,6 +320,7 @@ enum litest_device_type {
+ LITEST_KEYBOARD_QUIRKED,
+ LITEST_SYNAPTICS_PRESSUREPAD,
+ LITEST_GENERIC_PRESSUREPAD,
++ LITEST_MOUSE_FORMAT_STRING,
+ };
+
+ #define LITEST_DEVICELESS -2
+diff --git a/test/test-utils.c b/test/test-utils.c
+index 989adecd..e80754be 100644
+--- a/test/test-utils.c
++++ b/test/test-utils.c
+@@ -1267,6 +1267,31 @@ START_TEST(strstartswith_test)
+ }
+ END_TEST
+
++START_TEST(strsanitize_test)
++{
++ struct strsanitize_test {
++ const char *string;
++ const char *expected;
++ } tests[] = {
++ { "foobar", "foobar" },
++ { "", "" },
++ { "%", "%%" },
++ { "%%%%", "%%%%%%%%" },
++ { "x %s", "x %%s" },
++ { "x %", "x %%" },
++ { "%sx", "%%sx" },
++ { "%s%s", "%%s%%s" },
++ { NULL, NULL },
++ };
++
++ for (struct strsanitize_test *t = tests; t->string; t++) {
++ char *sanitized = str_sanitize(t->string);
++ ck_assert_str_eq(sanitized, t->expected);
++ free(sanitized);
++ }
++}
++END_TEST
++
+ START_TEST(list_test_insert)
+ {
+ struct list_test {
+@@ -1489,6 +1514,7 @@ litest_utils_suite(void)
+ tcase_add_test(tc, strstrip_test);
+ tcase_add_test(tc, strendswith_test);
+ tcase_add_test(tc, strstartswith_test);
++ tcase_add_test(tc, strsanitize_test);
+ tcase_add_test(tc, time_conversion);
+ tcase_add_test(tc, human_time);
+
+--
+2.36.0
+