Blob Blame History Raw
From 5680918c9e2cc8b2418a3809892532be0d10018a Mon Sep 17 00:00:00 2001
From: Eugene Syromyatnikov <evgsyr@gmail.com>
Date: Thu, 17 Oct 2019 15:29:50 +0200
Subject: [PATCH 70/76] evdev: decode struct input_absinfo regardless of
 in-kernel definitions

* evdev.c (struct_input_absinfo): New typedef.
(abs_ioctl): Add code argument. Add orig_sz, res_sz, sz, read_sz local
variables. Decode resolution field regardless of
HAVE_STRUCT_INPUT_ABSINFO_RESOLUTION.
(evdev_read_ioctl, evdev_write_ioctl): Pass code to abs_ioctl.
* tests/ioctl_evdev-success.c (print_input_absinfo): Update expected
output.
(main): Add absinfo_sz, absinfo_24, absinfo_32 local variables; add
additional checks for struct input_absinfo.

References: https://bugzilla.redhat.com/show_bug.cgi?id=1758201
---
 evdev.c                     | 78 ++++++++++++++++++++++++++++++---------------
 tests/ioctl_evdev-success.c | 51 ++++++++++++++++++++++++++---
 2 files changed, 99 insertions(+), 30 deletions(-)

Index: strace-5.1/evdev.c
===================================================================
--- strace-5.1.orig/evdev.c	2020-01-29 12:37:35.139765850 +0100
+++ strace-5.1/evdev.c	2020-01-29 12:39:10.344891943 +0100
@@ -37,6 +37,15 @@
 #  define SYN_MAX 0xf
 # endif
 
+typedef struct {
+	int32_t value;
+	int32_t minimum;
+	int32_t maximum;
+	int32_t fuzz;
+	int32_t flat;
+	int32_t resolution; /**< Added by Linux commit v2.6.31-rc1~100^2~1 */
+} struct_input_absinfo;
+
 /** Added by Linux commit v2.6.37-rc1~5^2~3^2~47 */
 typedef struct {
 	uint8_t  flags;
@@ -53,6 +62,9 @@
 	uint64_t codes_ptr;
 } struct_input_mask;
 
+static_assert(sizeof(struct input_absinfo) <= sizeof(struct_input_absinfo),
+	      "Unexpected struct input_absinfo size, please update "
+	      "the decoder");
 # ifdef HAVE_STRUCT_INPUT_KEYMAP_ENTRY
 static_assert(sizeof(struct input_keymap_entry)
 	      == sizeof(struct_input_keymap_entry),
@@ -85,36 +97,50 @@
 const size_t evdev_abs_size = ARRAY_SIZE(evdev_abs) - 1;
 
 static int
-abs_ioctl(struct tcb *const tcp, const kernel_ulong_t arg)
+abs_ioctl(struct tcb *const tcp, const unsigned int code,
+	  const kernel_ulong_t arg)
 {
+	static const size_t orig_sz = offsetofend(struct_input_absinfo, flat);
+	static const size_t res_sz = offsetofend(struct_input_absinfo,
+						 resolution);
+
+	struct_input_absinfo absinfo;
+	size_t sz = _IOC_SIZE(code);
+	size_t read_sz = MIN(sz, sizeof(absinfo));
+
+	if (sz < orig_sz)
+		return RVAL_DECODED;
+
 	tprints(", ");
 
-	struct input_absinfo absinfo;
+	if (umoven_or_printaddr(tcp, arg, read_sz, &absinfo))
+		return RVAL_IOCTL_DECODED;
 
-	if (!umove_or_printaddr(tcp, arg, &absinfo)) {
-		tprintf("{value=%u"
-			", minimum=%u, ",
-			absinfo.value,
-			absinfo.minimum);
-
-		if (!abbrev(tcp)) {
-			tprintf("maximum=%u"
-				", fuzz=%u"
-				", flat=%u",
-				absinfo.maximum,
-				absinfo.fuzz,
-				absinfo.flat);
-# ifdef HAVE_STRUCT_INPUT_ABSINFO_RESOLUTION
-			tprintf(", resolution=%u",
-				absinfo.resolution);
-# endif
-		} else {
-			tprints("...");
-		}
+	tprintf("{value=%u"
+		", minimum=%u, ",
+		absinfo.value,
+		absinfo.minimum);
 
-		tprints("}");
+	if (!abbrev(tcp)) {
+		tprintf("maximum=%u"
+			", fuzz=%u"
+			", flat=%u",
+			absinfo.maximum,
+			absinfo.fuzz,
+			absinfo.flat);
+		if (sz >= res_sz) {
+			tprintf(", resolution=%u%s",
+				absinfo.resolution,
+				sz > res_sz ? ", ..." : "");
+		} else if (sz > orig_sz) {
+			tprints(", ...");
+		}
+	} else {
+		tprints("...");
 	}
 
+	tprints("}");
+
 	return RVAL_IOCTL_DECODED;
 }
 
@@ -394,7 +420,7 @@
 
 	/* multi-number fixed-length commands */
 	if ((_IOC_NR(code) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0)))
-		return abs_ioctl(tcp, arg);
+		return abs_ioctl(tcp, code, arg);
 
 	/* multi-number variable-length commands */
 	if ((_IOC_NR(code) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
@@ -435,7 +461,7 @@
 
 	/* multi-number fixed-length commands */
 	if ((_IOC_NR(code) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0)))
-		return abs_ioctl(tcp, arg);
+		return abs_ioctl(tcp, code, arg);
 
 	return 0;
 }
Index: strace-5.1/tests/ioctl_evdev-success.c
===================================================================
--- strace-5.1.orig/tests/ioctl_evdev-success.c	2020-01-27 19:35:30.929507199 +0100
+++ strace-5.1/tests/ioctl_evdev-success.c	2020-01-29 12:39:10.345891934 +0100
@@ -56,6 +56,9 @@
 print_input_absinfo(long rc, const void *ptr, const void *arg)
 {
 	const struct input_absinfo *absinfo = ptr;
+# if VERBOSE
+	const uintptr_t sz = (uintptr_t) arg;
+# endif
 
 	if (rc < 0) {
 		printf("%p", absinfo);
@@ -67,9 +70,20 @@
 	PRINT_FIELD_U(", ", *absinfo, maximum);
 	PRINT_FIELD_U(", ", *absinfo, fuzz);
 	PRINT_FIELD_U(", ", *absinfo, flat);
+	if (sz > offsetofend(struct input_absinfo, flat)) {
+		if (sz >= 24) {
 #  ifdef HAVE_STRUCT_INPUT_ABSINFO_RESOLUTION
-	PRINT_FIELD_U(", ", *absinfo, resolution);
+			PRINT_FIELD_U(", ", *absinfo, resolution);
+#  else
+			printf(", resolution=%u", *((int *) ptr + 5));
 #  endif
+
+			if (sz > 24)
+				printf(", ...");
+		} else {
+			printf(", ...");
+		}
+	}
 # else
 	printf(", ...");
 # endif
@@ -176,10 +190,22 @@
 				   ", EVIOCGID, NULL) returning %lu",
 				   inject_retval);
 
+	static const void *absinfo_sz =
+		(const void *) (uintptr_t) sizeof(struct input_absinfo);
+
 	TAIL_ALLOC_OBJECT_CONST_PTR(struct input_id, id);
 	TAIL_ALLOC_OBJECT_CONST_PTR(struct input_absinfo, absinfo);
 	TAIL_ALLOC_OBJECT_CONST_PTR(int, bad_addr_slot);
 
+	struct input_absinfo *absinfo_24 = tail_alloc(MAX(sizeof(*absinfo_24),
+							  24));
+	struct input_absinfo *absinfo_32 = tail_alloc(MAX(sizeof(*absinfo_32),
+							  32));
+
+	fill_memory(absinfo, sizeof(struct input_absinfo));
+	fill_memory(absinfo_24, 24);
+	fill_memory(absinfo_32, 32);
+
 # ifdef EVIOCGMTSLOTS
 	int mtslots[] = { ABS_MT_SLOT, 1, 3 };
 	/* we use the second element to indicate the number of values */
@@ -236,9 +262,26 @@
 		const void *ptr;
 	} a[] = {
 		{ { ARG_STR(EVIOCGID), id, print_input_id }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 19), "EVIOCGABS(ABS_Y)",
+		    absinfo, NULL }, NULL },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 20),
+		    "EVIOCGABS(ABS_Y)", absinfo, print_input_absinfo },
+		  (const void *) (uintptr_t) 20 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 21),
+		    "EVIOCGABS(ABS_Y)", absinfo_24, print_input_absinfo },
+		  (const void *) (uintptr_t) 21 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 24),
+		    "EVIOCGABS(ABS_Y)", absinfo_24, print_input_absinfo },
+		  (const void *) (uintptr_t) 24 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 32),
+		    "EVIOCGABS(ABS_Y)", absinfo_32, print_input_absinfo },
+		  (const void *) (uintptr_t) 32 },
+		{ { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo },
+		  absinfo_sz},
+		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo },
+		  absinfo_sz },
+		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo },
+		  absinfo_sz },
 		{ { ARG_STR(EVIOCGBIT(0, 0)), ev_more, print_getbit },
 			inject_retval * 8 <= EV_LED
 				? (const void *) &ev_more_str_2
Index: strace-5.1/tests-m32/ioctl_evdev-success.c
===================================================================
--- strace-5.1.orig/tests-m32/ioctl_evdev-success.c	2020-01-27 19:35:30.932507172 +0100
+++ strace-5.1/tests-m32/ioctl_evdev-success.c	2020-01-29 12:39:23.992766667 +0100
@@ -56,6 +56,9 @@
 print_input_absinfo(long rc, const void *ptr, const void *arg)
 {
 	const struct input_absinfo *absinfo = ptr;
+# if VERBOSE
+	const uintptr_t sz = (uintptr_t) arg;
+# endif
 
 	if (rc < 0) {
 		printf("%p", absinfo);
@@ -67,9 +70,20 @@
 	PRINT_FIELD_U(", ", *absinfo, maximum);
 	PRINT_FIELD_U(", ", *absinfo, fuzz);
 	PRINT_FIELD_U(", ", *absinfo, flat);
+	if (sz > offsetofend(struct input_absinfo, flat)) {
+		if (sz >= 24) {
 #  ifdef HAVE_STRUCT_INPUT_ABSINFO_RESOLUTION
-	PRINT_FIELD_U(", ", *absinfo, resolution);
+			PRINT_FIELD_U(", ", *absinfo, resolution);
+#  else
+			printf(", resolution=%u", *((int *) ptr + 5));
 #  endif
+
+			if (sz > 24)
+				printf(", ...");
+		} else {
+			printf(", ...");
+		}
+	}
 # else
 	printf(", ...");
 # endif
@@ -176,10 +190,22 @@
 				   ", EVIOCGID, NULL) returning %lu",
 				   inject_retval);
 
+	static const void *absinfo_sz =
+		(const void *) (uintptr_t) sizeof(struct input_absinfo);
+
 	TAIL_ALLOC_OBJECT_CONST_PTR(struct input_id, id);
 	TAIL_ALLOC_OBJECT_CONST_PTR(struct input_absinfo, absinfo);
 	TAIL_ALLOC_OBJECT_CONST_PTR(int, bad_addr_slot);
 
+	struct input_absinfo *absinfo_24 = tail_alloc(MAX(sizeof(*absinfo_24),
+							  24));
+	struct input_absinfo *absinfo_32 = tail_alloc(MAX(sizeof(*absinfo_32),
+							  32));
+
+	fill_memory(absinfo, sizeof(struct input_absinfo));
+	fill_memory(absinfo_24, 24);
+	fill_memory(absinfo_32, 32);
+
 # ifdef EVIOCGMTSLOTS
 	int mtslots[] = { ABS_MT_SLOT, 1, 3 };
 	/* we use the second element to indicate the number of values */
@@ -236,9 +262,26 @@
 		const void *ptr;
 	} a[] = {
 		{ { ARG_STR(EVIOCGID), id, print_input_id }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 19), "EVIOCGABS(ABS_Y)",
+		    absinfo, NULL }, NULL },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 20),
+		    "EVIOCGABS(ABS_Y)", absinfo, print_input_absinfo },
+		  (const void *) (uintptr_t) 20 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 21),
+		    "EVIOCGABS(ABS_Y)", absinfo_24, print_input_absinfo },
+		  (const void *) (uintptr_t) 21 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 24),
+		    "EVIOCGABS(ABS_Y)", absinfo_24, print_input_absinfo },
+		  (const void *) (uintptr_t) 24 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 32),
+		    "EVIOCGABS(ABS_Y)", absinfo_32, print_input_absinfo },
+		  (const void *) (uintptr_t) 32 },
+		{ { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo },
+		  absinfo_sz},
+		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo },
+		  absinfo_sz },
+		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo },
+		  absinfo_sz },
 		{ { ARG_STR(EVIOCGBIT(0, 0)), ev_more, print_getbit },
 			inject_retval * 8 <= EV_LED
 				? (const void *) &ev_more_str_2
Index: strace-5.1/tests-mx32/ioctl_evdev-success.c
===================================================================
--- strace-5.1.orig/tests-mx32/ioctl_evdev-success.c	2020-01-27 19:35:30.933507163 +0100
+++ strace-5.1/tests-mx32/ioctl_evdev-success.c	2020-01-29 12:39:23.994766648 +0100
@@ -56,6 +56,9 @@
 print_input_absinfo(long rc, const void *ptr, const void *arg)
 {
 	const struct input_absinfo *absinfo = ptr;
+# if VERBOSE
+	const uintptr_t sz = (uintptr_t) arg;
+# endif
 
 	if (rc < 0) {
 		printf("%p", absinfo);
@@ -67,9 +70,20 @@
 	PRINT_FIELD_U(", ", *absinfo, maximum);
 	PRINT_FIELD_U(", ", *absinfo, fuzz);
 	PRINT_FIELD_U(", ", *absinfo, flat);
+	if (sz > offsetofend(struct input_absinfo, flat)) {
+		if (sz >= 24) {
 #  ifdef HAVE_STRUCT_INPUT_ABSINFO_RESOLUTION
-	PRINT_FIELD_U(", ", *absinfo, resolution);
+			PRINT_FIELD_U(", ", *absinfo, resolution);
+#  else
+			printf(", resolution=%u", *((int *) ptr + 5));
 #  endif
+
+			if (sz > 24)
+				printf(", ...");
+		} else {
+			printf(", ...");
+		}
+	}
 # else
 	printf(", ...");
 # endif
@@ -176,10 +190,22 @@
 				   ", EVIOCGID, NULL) returning %lu",
 				   inject_retval);
 
+	static const void *absinfo_sz =
+		(const void *) (uintptr_t) sizeof(struct input_absinfo);
+
 	TAIL_ALLOC_OBJECT_CONST_PTR(struct input_id, id);
 	TAIL_ALLOC_OBJECT_CONST_PTR(struct input_absinfo, absinfo);
 	TAIL_ALLOC_OBJECT_CONST_PTR(int, bad_addr_slot);
 
+	struct input_absinfo *absinfo_24 = tail_alloc(MAX(sizeof(*absinfo_24),
+							  24));
+	struct input_absinfo *absinfo_32 = tail_alloc(MAX(sizeof(*absinfo_32),
+							  32));
+
+	fill_memory(absinfo, sizeof(struct input_absinfo));
+	fill_memory(absinfo_24, 24);
+	fill_memory(absinfo_32, 32);
+
 # ifdef EVIOCGMTSLOTS
 	int mtslots[] = { ABS_MT_SLOT, 1, 3 };
 	/* we use the second element to indicate the number of values */
@@ -236,9 +262,26 @@
 		const void *ptr;
 	} a[] = {
 		{ { ARG_STR(EVIOCGID), id, print_input_id }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL },
-		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 19), "EVIOCGABS(ABS_Y)",
+		    absinfo, NULL }, NULL },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 20),
+		    "EVIOCGABS(ABS_Y)", absinfo, print_input_absinfo },
+		  (const void *) (uintptr_t) 20 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 21),
+		    "EVIOCGABS(ABS_Y)", absinfo_24, print_input_absinfo },
+		  (const void *) (uintptr_t) 21 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 24),
+		    "EVIOCGABS(ABS_Y)", absinfo_24, print_input_absinfo },
+		  (const void *) (uintptr_t) 24 },
+		{ { _IOC(_IOC_READ, 'E', 0x40 + ABS_Y, 32),
+		    "EVIOCGABS(ABS_Y)", absinfo_32, print_input_absinfo },
+		  (const void *) (uintptr_t) 32 },
+		{ { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo },
+		  absinfo_sz},
+		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo },
+		  absinfo_sz },
+		{ { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo },
+		  absinfo_sz },
 		{ { ARG_STR(EVIOCGBIT(0, 0)), ev_more, print_getbit },
 			inject_retval * 8 <= EV_LED
 				? (const void *) &ev_more_str_2