diff --git a/0001-util-sanitize-control-characters-in-str_sanitize.patch b/0001-util-sanitize-control-characters-in-str_sanitize.patch new file mode 100644 index 0000000..599e169 --- /dev/null +++ b/0001-util-sanitize-control-characters-in-str_sanitize.patch @@ -0,0 +1,124 @@ +From fc2262e1c1847021239065e84f39f15492ef05cc Mon Sep 17 00:00:00 2001 +From: Peter Hutterer +Date: Mon, 1 Jun 2026 10:12:29 +1000 +Subject: [PATCH] util: sanitize control characters in str_sanitize() + +str_sanitize() only escaped '%' characters for format string safety. +Device names from uinput devices can contain arbitrary bytes including +ANSI escape sequences (ESC, 0x1b) and other control characters. When +these strings are included in log messages and printed to a terminal, +the escape sequences are interpreted by the terminal emulator. This +could allow an attacker to manipulate terminal output (change colors, +set window title, clear screen) when an administrator views libinput +logs. + +Replace all control characters (0x00-0x1f and 0x7f) with '?' in +addition to the existing '%' escaping. This prevents terminal escape +sequence injection through device names in log output. + +Assisted-by: Claude:claude-opus-4-6 +(cherry picked from commit 71a2c5cae2a80a1e3bb29e3f3a07ccc3f3de5acb) + +Part-of: +--- + src/util-strings.h | 30 ++++++++++++++++++++++++------ + test/test-utils.c | 10 ++++++++++ + 2 files changed, 34 insertions(+), 6 deletions(-) + +diff --git a/src/util-strings.h b/src/util-strings.h +index 94236c481706..3c6626331a91 100644 +--- a/src/util-strings.h ++++ b/src/util-strings.h +@@ -538,34 +538,52 @@ strstartswith(const char *str, const char *prefix) + const char * + 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. ++ * acceptable as printf format, and all non-NUL control characters ++ * (bytes 0x01-0x1f, 0x7f) replaced with '?' to prevent terminal ++ * escape sequence injection. NUL bytes are excluded implicitly ++ * because the string is null-terminated. + */ + static inline char * + str_sanitize(const char *str) + { + if (!str) + return NULL; + +- if (!strchr(str, '%')) +- return strdup(str); +- + size_t slen = strlen(str); + slen = min(slen, 512); ++ ++ bool needs_sanitization = false; ++ for (size_t i = 0; i < slen; i++) { ++ unsigned char c = str[i]; ++ if (c == '%' || c < 0x20 || c == 0x7f) { ++ needs_sanitization = true; ++ break; ++ } ++ } ++ if (!needs_sanitization) ++ return strdup(str); ++ + char *sanitized = zalloc(2 * slen + 1); + const char *src = str; + char *dst = sanitized; + + for (size_t i = 0; i < slen; i++) { +- if (*src == '%') ++ unsigned char c = *src++; ++ if (c == '%') { + *dst++ = '%'; +- *dst++ = *src++; ++ *dst++ = '%'; ++ } else if (c < 0x20 || c == 0x7f) { ++ *dst++ = '?'; ++ } else { ++ *dst++ = c; ++ } + } + *dst = '\0'; + + return sanitized; + } +diff --git a/test/test-utils.c b/test/test-utils.c +index 7c938b0af8ac..ee61ba13cc9c 100644 +--- a/test/test-utils.c ++++ b/test/test-utils.c +@@ -2081,16 +2081,26 @@ START_TEST(strsanitize_test) + { "foobar", "foobar" }, + { "", "" }, + { "%", "%%" }, + { "%%%%", "%%%%%%%%" }, + { "x %s", "x %%s" }, + { "x %", "x %%" }, + { "%sx", "%%sx" }, + { "%s%s", "%%s%%s" }, ++ { "\t", "?" }, ++ { "\n", "?" }, ++ { "\r", "?" }, ++ { "\x1b[31m", "?[31m" }, ++ { "foo\tbar", "foo?bar" }, ++ { "foo\nbar", "foo?bar" }, ++ { "\x01\x1f\x7f", "???" }, ++ { "clean", "clean" }, ++ { "a\x1b[0mb", "a?[0mb" }, ++ { "%\n", "%%?" }, + { NULL, NULL }, + }; + /* clang-format on */ + + for (struct strsanitize_test *t = tests; t->string; t++) { + char *sanitized = str_sanitize(t->string); + litest_assert_str_eq(sanitized, t->expected); + free(sanitized); +-- +2.54.0 + diff --git a/0002-libinput-device-group-sanitize-phys-before-printing-.patch b/0002-libinput-device-group-sanitize-phys-before-printing-.patch new file mode 100644 index 0000000..e73558e --- /dev/null +++ b/0002-libinput-device-group-sanitize-phys-before-printing-.patch @@ -0,0 +1,116 @@ +From b2bde9504d42a5976d76e1f27c640dc561fbd99b Mon Sep 17 00:00:00 2001 +From: Peter Hutterer +Date: Mon, 1 Jun 2026 10:48:24 +1000 +Subject: [PATCH] libinput-device-group: sanitize phys before printing it + +A malicious uinput device could set the phys value (via UI_SET_PHYS) +to contain a '\n'. When the value is printed as part of the device group +the udev rules will interpret it as separate property. + +Depending on the property this can cause local privilege escalation. + +Closes #1296 + +Found-by: Csome +(cherry picked from commit 76f0d8a7f57e2868882864b4611281f12f704b55) + +Part-of: +--- + udev/libinput-device-group.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/udev/libinput-device-group.c b/udev/libinput-device-group.c +index cdb38c0beb98..f9188406d4de 100644 +--- a/udev/libinput-device-group.c ++++ b/udev/libinput-device-group.c +@@ -102,61 +102,63 @@ wacom_handle_ekr(struct udev_device *device, + udev = udev_device_get_udev(device); + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "input"); + udev_enumerate_add_match_sysname(e, "input*"); + udev_enumerate_scan_devices(e); + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + struct udev_device *d; +- const char *path, *phys; ++ _autofree_ char *phys = NULL; ++ const char *path; + const char *pidstr, *vidstr; + int pid, vid, dist; + + /* Find and use the closest Wacom device on the system, + * relying on wacom_handle_paired() to fix our ID later + * if needed. + */ + path = udev_list_entry_get_name(entry); + d = udev_device_new_from_syspath(udev, path); + if (!d) + continue; + + vidstr = udev_device_get_property_value(d, "ID_VENDOR_ID"); + pidstr = udev_device_get_property_value(d, "ID_MODEL_ID"); +- phys = udev_device_get_sysattr_value(d, "phys"); ++ phys = str_sanitize(udev_device_get_sysattr_value(d, "phys")); + + if (vidstr && pidstr && phys && safe_atoi_base(vidstr, &vid, 16) && + safe_atoi_base(pidstr, &pid, 16) && vid == VENDOR_ID_WACOM && + pid != PRODUCT_ID_WACOM_EKR) { + dist = find_tree_distance(device, d); + if (dist > 0 && (dist < best_dist || best_dist < 0)) { + *vendor_id = vid; + *product_id = pid; + best_dist = dist; + + free(*phys_attr); +- *phys_attr = safe_strdup(phys); ++ *phys_attr = steal(&phys); + } + } + + udev_device_unref(d); + } + + udev_enumerate_unref(e); + } + #endif + + int + main(int argc, char **argv) + { + int rc = 1; + struct udev *udev = NULL; + struct udev_device *device = NULL; +- const char *syspath, *phys = NULL; ++ _autofree_ char *phys = NULL; ++ const char *syspath = NULL; + const char *product; + int bustype, vendor_id, product_id, version; + char group[1024]; + char *str; + + if (argc != 2) + return 1; + +@@ -170,18 +172,17 @@ main(int argc, char **argv) + if (!device) + goto out; + + /* Find the first parent with ATTRS{phys} set. For tablets that + * value looks like usb-0000:00:14.0-1/input1. Drop the /input1 + * bit and use the remainder as device group identifier */ + while (device != NULL) { + struct udev_device *parent; +- +- phys = udev_device_get_sysattr_value(device, "phys"); ++ phys = str_sanitize(udev_device_get_sysattr_value(device, "phys")); + if (phys) + break; + + parent = udev_device_get_parent(device); + udev_device_ref(parent); + udev_device_unref(device); + device = parent; + } +-- +2.54.0 + diff --git a/0003-util-fix-possible-return-of-unsanitized-input-in-str.patch b/0003-util-fix-possible-return-of-unsanitized-input-in-str.patch new file mode 100644 index 0000000..8e71678 --- /dev/null +++ b/0003-util-fix-possible-return-of-unsanitized-input-in-str.patch @@ -0,0 +1,38 @@ +From a487280e095e376a40354e69c01593153d61f126 Mon Sep 17 00:00:00 2001 +From: Josiah Vehrs +Date: Wed, 10 Jun 2026 18:17:53 -0700 +Subject: [PATCH] util: fix possible return of unsanitized input in + str_sanitize + +Fixes: 71a2c5cae2a8 ("util: sanitize control characters in str_sanitize()") +Part-of: +--- + src/util-strings.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/util-strings.h b/src/util-strings.h +index 6186c16784d5..a6ff440bd24b 100644 +--- a/src/util-strings.h ++++ b/src/util-strings.h +@@ -567,17 +567,17 @@ str_sanitize(const char *str) + for (size_t i = 0; i < slen; i++) { + unsigned char c = str[i]; + if (c == '%' || c < 0x20 || c == 0x7f) { + needs_sanitization = true; + break; + } + } + if (!needs_sanitization) +- return strdup(str); ++ return strndup(str, slen); + + char *sanitized = zalloc(2 * slen + 1); + const char *src = str; + char *dst = sanitized; + + for (size_t i = 0; i < slen; i++) { + unsigned char c = *src++; + if (c == '%') { +-- +2.54.0 + diff --git a/libinput.spec b/libinput.spec index 0046da7..7e018aa 100644 --- a/libinput.spec +++ b/libinput.spec @@ -5,7 +5,7 @@ Name: libinput Version: 1.30.1 -Release: 1%{?gitdate:.%{gitdate}git%{gitversion}}%{?dist} +Release: 2%{?gitdate:.%{gitdate}git%{gitversion}}%{?dist} Summary: Input device library # SPDX @@ -20,6 +20,10 @@ Source0: https://gitlab.freedesktop.org/libinput/libinput/-/archive/%{ver %endif Patch0002: 0001-RHEL-map-dials-to-rings-on-the-Intuos-Pro-3rd-Gen-de.patch +# CVE-2026-50292 (RHEL-182371) +Patch0003: 0001-util-sanitize-control-characters-in-str_sanitize.patch +Patch0004: 0002-libinput-device-group-sanitize-phys-before-printing-.patch +Patch0005: 0003-util-fix-possible-return-of-unsanitized-input-in-str.patch BuildRequires: git-core BuildRequires: gcc @@ -162,6 +166,10 @@ intended to be run by users. %changelog +* Fri Jun 12 2026 Peter Hutterer - 1.30.1-2 +- CVE-2026-50292: sanitize phys before printing it in libinput-device-group + Resolves: RHEL-182371 + * Wed Dec 17 2025 Peter Hutterer - 1.30.1-1 - libinput 1.30.1 Resolves: RHEL-136390