]> granicus.if.org Git - strace/commitdiff
evdev: decode struct input_absinfo regardless of in-kernel definitions
authorEugene Syromyatnikov <evgsyr@gmail.com>
Thu, 17 Oct 2019 13:29:50 +0000 (15:29 +0200)
committerEugene Syromyatnikov <evgsyr@gmail.com>
Tue, 22 Oct 2019 17:44:08 +0000 (19:44 +0200)
* 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
tests/ioctl_evdev-success.c

diff --git a/evdev.c b/evdev.c
index 62a2226335321c6ebf342cf7c97074db95f1e693..72addff6c8a80338c2587bdfc31b8521b796ca0e 100644 (file)
--- a/evdev.c
+++ b/evdev.c
 #  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 @@ typedef struct {
        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),
@@ -83,36 +95,50 @@ static_assert(sizeof(struct input_mask) == sizeof(struct_input_mask),
 # endif
 
 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 (!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("...");
-               }
+       if (umoven_or_printaddr(tcp, arg, read_sz, &absinfo))
+               return RVAL_IOCTL_DECODED;
+
+       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;
 }
 
@@ -402,7 +428,7 @@ evdev_read_ioctl(struct tcb *const tcp, const unsigned int code,
 
        /* 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)))
@@ -443,7 +469,7 @@ evdev_write_ioctl(struct tcb *const tcp, const unsigned int code,
 
        /* 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 cb9ed5f069e04e383e6622594bfc16694c7a4b8c..b8d07337f3299697b711752570c095b804be570f 100644 (file)
@@ -56,6 +56,9 @@ static void
 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_input_absinfo(long rc, const void *ptr, const void *arg)
        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
@@ -201,10 +215,22 @@ main(int argc, char **argv)
                                   ", 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
        static const unsigned int mtslots[] = { ABS_MT_SLOT, 1, 3 };
        static const char * const mtslots_str[] = {
@@ -282,9 +308,26 @@ main(int argc, char **argv)
                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