]> granicus.if.org Git - strace/blobdiff - evdev.c
net: fix off-by-one error in sorted xlat lookup
[strace] / evdev.c
diff --git a/evdev.c b/evdev.c
index 4619ecb11369bf041560d99fd18ffb6cdcea14da..8d22d08ea42310e554f95e49ebd1d08e1f481145 100644 (file)
--- a/evdev.c
+++ b/evdev.c
@@ -1,38 +1,22 @@
 /*
  * Copyright (c) 2015 Etienne Gemsa <etienne.gemsa@lse.epita.fr>
- * Copyright (c) 2015 Dmitry V. Levin <ldv@altlinux.org>
+ * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org>
+ * Copyright (c) 2015-2018 The strace developers.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #include "defs.h"
 
+#include "xlat/evdev_abs.h"
+#include "xlat/evdev_ev.h"
+
 #ifdef HAVE_LINUX_INPUT_H
 
 # include <linux/ioctl.h>
 # include <linux/input.h>
-# include "xlat/evdev_abs.h"
+
 # include "xlat/evdev_autorepeat.h"
 # include "xlat/evdev_ff_status.h"
 # include "xlat/evdev_ff_types.h"
 # include "xlat/evdev_relative_axes.h"
 # include "xlat/evdev_snd.h"
 # include "xlat/evdev_switch.h"
-# include "xlat/evdev_sync.h"
 
 # ifndef SYN_MAX
 #  define SYN_MAX 0xf
 # endif
 
-static void
-decode_envelope(const struct ff_envelope *envelope)
-{
-       tprintf(", envelope={attack_length=%" PRIu16
-               ", attack_level=%" PRIu16
-               ", fade_length=%" PRIu16
-               ", fade_level=%#x}",
-               envelope->attack_length,
-               envelope->attack_level,
-               envelope->fade_length,
-               envelope->fade_level);
-}
+const size_t evdev_abs_size = ARRAY_SIZE(evdev_abs) - 1;
 
 static int
-ff_effect_ioctl(struct tcb *tcp, long arg)
-{
-       tprints(", ");
-
-       struct ff_effect ffe;
-
-       if (umove_or_printaddr(tcp, arg, &ffe))
-               return 1;
-
-       tprints("{type=");
-       printxval(evdev_ff_types, ffe.type, "FF_???");
-       tprintf(", id=%" PRIu16
-               ", direction=%" PRIu16 ", ",
-               ffe.id,
-               ffe.direction);
-
-       if (abbrev(tcp)) {
-               tprints("...}");
-               return 1;
-       }
-
-       tprintf("trigger={button=%" PRIu16
-               ", interval=%" PRIu16 "}"
-               ", replay={length=%" PRIu16
-               ", delay=%" PRIu16 "}",
-               ffe.trigger.button,
-               ffe.trigger.interval,
-               ffe.replay.length,
-               ffe.replay.delay);
-
-       switch (ffe.type) {
-               case FF_CONSTANT:
-                       tprintf(", constant={level=%" PRId16,
-                               ffe.u.constant.level);
-                       decode_envelope(&ffe.u.constant.envelope);
-                       tprints("}");
-                       break;
-               case FF_RAMP:
-                       tprintf(", ramp={start_level=%" PRId16
-                               ", end_level=%" PRId16,
-                               ffe.u.ramp.start_level,
-                               ffe.u.ramp.end_level);
-                       decode_envelope(&ffe.u.ramp.envelope);
-                       tprints("}");
-                       break;
-               case FF_PERIODIC:
-                       tprintf(", periodic={waveform=%" PRIu16
-                               ", period=%" PRIu16
-                               ", magnitude=%" PRId16
-                               ", offset=%" PRId16
-                               ", phase=%" PRIu16,
-                               ffe.u.periodic.waveform,
-                               ffe.u.periodic.period,
-                               ffe.u.periodic.magnitude,
-                               ffe.u.periodic.offset,
-                               ffe.u.periodic.phase);
-                       decode_envelope(&ffe.u.periodic.envelope);
-                       tprintf(", custom_len=%u"
-                               ", custom_data=%#lx}",
-                               ffe.u.periodic.custom_len,
-                               (unsigned long) ffe.u.periodic.custom_data);
-                       break;
-               case FF_RUMBLE:
-                       tprintf(", rumble={strong_magnitude=%" PRIu16
-                               ", weak_magnitude=%" PRIu16 "}",
-                               ffe.u.rumble.strong_magnitude,
-                               ffe.u.rumble.weak_magnitude);
-                       break;
-               default:
-                       break;
-       }
-
-       tprints("}");
-
-       return 1;
-}
-
-static int
-abs_ioctl(struct tcb *tcp, long arg)
+abs_ioctl(struct tcb *const tcp, const kernel_ulong_t arg)
 {
        tprints(", ");
 
@@ -172,11 +66,11 @@ abs_ioctl(struct tcb *tcp, long arg)
                tprints("}");
        }
 
-       return 1;
+       return RVAL_IOCTL_DECODED;
 }
 
 static int
-keycode_ioctl(struct tcb *tcp, long arg)
+keycode_ioctl(struct tcb *const tcp, const kernel_ulong_t arg)
 {
        tprints(", ");
 
@@ -184,91 +78,96 @@ keycode_ioctl(struct tcb *tcp, long arg)
 
        if (!umove_or_printaddr(tcp, arg, &keycode)) {
                tprintf("[%u, ", keycode[0]);
-               printxval(evdev_keycode, keycode[1], "KEY_???");
+               printxval_index(evdev_keycode, keycode[1], "KEY_???");
                tprints("]");
        }
 
-       return 1;
+       return RVAL_IOCTL_DECODED;
 }
 
 # ifdef EVIOCGKEYCODE_V2
 static int
-keycode_V2_ioctl(struct tcb *tcp, long arg)
+keycode_V2_ioctl(struct tcb *const tcp, const kernel_ulong_t arg)
 {
+       tprints(", ");
+
        struct input_keymap_entry ike;
 
-       if (!arg) {
-               tprints(", NULL");
-               return 1;
-       }
+       if (umove_or_printaddr(tcp, arg, &ike))
+               return RVAL_IOCTL_DECODED;
 
-       if (!verbose(tcp) || umove(tcp, arg, &ike) < 0)
-               return 0;
+       tprintf("{flags=%" PRIu8
+               ", len=%" PRIu8 ", ",
+               ike.flags,
+               ike.len);
 
-       tprintf(", {flags=%" PRIu8 ", len=%" PRIu8, ike.flags, ike.len);
        if (!abbrev(tcp)) {
                unsigned int i;
 
-               tprintf("index=%" PRIu16 ", keycode=", ike.index);
-               printxval(evdev_keycode, ike.keycode, "KEY_???");
+               tprintf("index=%" PRIu16 ", keycode=", ike.index);
+               printxval_index(evdev_keycode, ike.keycode, "KEY_???");
                tprints(", scancode=[");
                for (i = 0; i < ARRAY_SIZE(ike.scancode); i++) {
                        if (i > 0)
                                tprints(", ");
                        tprintf("%" PRIx8, ike.scancode[i]);
                }
-               tprints("]}");
+               tprints("]");
        } else {
-               tprints(", ...}");
+               tprints("...");
        }
-       return 1;
+
+       tprints("}");
+
+       return RVAL_IOCTL_DECODED;
 }
 # endif /* EVIOCGKEYCODE_V2 */
 
 static int
-getid_ioctl(struct tcb *tcp, long arg)
+getid_ioctl(struct tcb *const tcp, const kernel_ulong_t arg)
 {
-       struct input_id id;
+       tprints(", ");
 
-       if (!verbose(tcp) || umove(tcp, arg, &id) < 0)
-               return 0;
+       struct input_id id;
 
-       tprintf(", {ID_BUS=%" PRIu16 ", ID_VENDOR=%" PRIu16,
-               id.bustype, id.vendor);
-       if (!abbrev(tcp)) {
-               tprintf(", ID_PRODUCT=%" PRIu16 ", ID_VERSION=%" PRIu16 "}",
-                       id.product, id.version);
-       } else {
-               tprints(", ...}");
-       }
-       return 1;
+       if (!umove_or_printaddr(tcp, arg, &id))
+               tprintf("{ID_BUS=%" PRIu16
+                       ", ID_VENDOR=%" PRIu16
+                       ", ID_PRODUCT=%" PRIu16
+                       ", ID_VERSION=%" PRIu16 "}",
+                       id.bustype,
+                       id.vendor,
+                       id.product,
+                       id.version);
+
+       return RVAL_IOCTL_DECODED;
 }
 
 static int
-decode_bitset(struct tcb *tcp, long arg, const struct xlat decode_nr[],
-             const unsigned int max_nr, const char *dflt)
+decode_bitset_(struct tcb *const tcp, const kernel_ulong_t arg,
+              const struct xlat decode_nr[], const unsigned int max_nr,
+              const char *const dflt, size_t decode_nr_size, enum xlat_type xt)
 {
-       if (!verbose(tcp))
-               return 0;
+       tprints(", ");
 
        unsigned int size;
-       if ((unsigned long) tcp->u_rval > max_nr)
+       if ((kernel_ulong_t) tcp->u_rval > max_nr / 8)
                size = max_nr;
        else
-               size = tcp->u_rval;
+               size = tcp->u_rval * 8;
        char decoded_arg[size];
 
-       if (umoven(tcp, arg, size, decoded_arg) < 0)
-               return 0;
+       if (umove_or_printaddr(tcp, arg, &decoded_arg))
+               return RVAL_IOCTL_DECODED;
 
-       tprints("[");
+       tprints("[");
 
        int bit_displayed = 0;
        int i = next_set_bit(decoded_arg, 0, size);
        if (i < 0) {
                tprints(" 0 ");
        } else {
-               printxval(decode_nr, i, dflt);
+               printxval_dispatch(decode_nr, decode_nr_size, i, dflt, xt);
 
                while ((i = next_set_bit(decoded_arg, i + 1, size)) > 0) {
                        if (abbrev(tcp) && bit_displayed >= 3) {
@@ -276,121 +175,133 @@ decode_bitset(struct tcb *tcp, long arg, const struct xlat decode_nr[],
                                break;
                        }
                        tprints(", ");
-                       printxval(decode_nr, i, dflt);
+                       printxval_dispatch(decode_nr, decode_nr_size, i, dflt,
+                                          xt);
                        bit_displayed++;
                }
        }
 
        tprints("]");
 
-       return 1;
+       return RVAL_IOCTL_DECODED;
 }
 
+#define decode_bitset(tcp_, arg_, decode_nr_, max_nr_, dflt_, xt_) \
+       decode_bitset_((tcp_), (arg_), (decode_nr_), (max_nr_), \
+                      (dflt_), ARRAY_SIZE(decode_nr_) - 1, (xt_))
+
 # ifdef EVIOCGMTSLOTS
 static int
-mtslots_ioctl(struct tcb *tcp, const unsigned int code, long arg)
+mtslots_ioctl(struct tcb *const tcp, const unsigned int code,
+             const kernel_ulong_t arg)
 {
-       const size_t size = _IOC_SIZE(code) / sizeof(int32_t);
-       if (!size)
-               return 0;
+       tprints(", ");
 
-       int32_t buffer[size];
+       const size_t size = _IOC_SIZE(code) / sizeof(int);
+       if (!size) {
+               printaddr(arg);
+               return RVAL_IOCTL_DECODED;
+       }
 
-       if (!verbose(tcp) || umove(tcp, arg, &buffer) < 0)
-               return 0;
+       int buffer[size];
 
-       tprints(", {code=");
+       if (umove_or_printaddr(tcp, arg, &buffer))
+               return RVAL_IOCTL_DECODED;
+
+       tprints("{code=");
        printxval(evdev_mtslots, buffer[0], "ABS_MT_???");
 
-       unsigned int i;
        tprints(", values=[");
 
+       unsigned int i;
        for (i = 1; i < ARRAY_SIZE(buffer); i++)
                tprintf("%s%d", i > 1 ? ", " : "", buffer[i]);
 
        tprints("]}");
-       return 1;
+
+       return RVAL_IOCTL_DECODED;
 }
 # endif /* EVIOCGMTSLOTS */
 
 # if defined EVIOCGREP || defined EVIOCSREP
 static int
-repeat_ioctl(struct tcb *tcp, long arg)
+repeat_ioctl(struct tcb *const tcp, const kernel_ulong_t arg)
 {
        tprints(", ");
        printpair_int(tcp, arg, "%u");
-       return 1;
+       return RVAL_IOCTL_DECODED;
 }
 # endif /* EVIOCGREP || EVIOCSREP */
 
 static int
-bit_ioctl(struct tcb *tcp, const unsigned int ev_nr, const long arg)
+bit_ioctl(struct tcb *const tcp, const unsigned int ev_nr,
+         const kernel_ulong_t arg)
 {
        switch (ev_nr) {
-               case EV_SYN:
-                       return decode_bitset(tcp, arg, evdev_sync,
-                                       SYN_MAX, "SYN_???");
+               case 0:
+                       return decode_bitset(tcp, arg, evdev_ev,
+                                            EV_MAX, "EV_???", XT_SORTED);
                case EV_KEY:
                        return decode_bitset(tcp, arg, evdev_keycode,
-                                       KEY_MAX, "KEY_???");
+                                            KEY_MAX, "KEY_???", XT_INDEXED);
                case EV_REL:
                        return decode_bitset(tcp, arg, evdev_relative_axes,
-                                       REL_MAX, "REL_???");
+                                            REL_MAX, "REL_???", XT_INDEXED);
                case EV_ABS:
-                       return decode_bitset(tcp, arg,
-                                       evdev_abs, ABS_MAX, "ABS_???");
+                       return decode_bitset(tcp, arg, evdev_abs,
+                                            ABS_MAX, "ABS_???", XT_INDEXED);
                case EV_MSC:
-                       return decode_bitset(tcp, arg,
-                                       evdev_misc, MSC_MAX, "MSC_???");
-# ifdef EV_SW
+                       return decode_bitset(tcp, arg, evdev_misc,
+                                            MSC_MAX, "MSC_???", XT_INDEXED);
                case EV_SW:
-                       return decode_bitset(tcp, arg,
-                                       evdev_switch, SW_MAX, "SW_???");
-# endif
+                       return decode_bitset(tcp, arg, evdev_switch,
+                                            SW_MAX, "SW_???", XT_INDEXED);
                case EV_LED:
-                       return decode_bitset(tcp, arg,
-                                       evdev_leds, LED_MAX, "LED_???");
+                       return decode_bitset(tcp, arg, evdev_leds,
+                                            LED_MAX, "LED_???", XT_INDEXED);
                case EV_SND:
-                       return decode_bitset(tcp, arg,
-                                       evdev_snd, SND_MAX, "SND_???");
+                       return decode_bitset(tcp, arg, evdev_snd,
+                                            SND_MAX, "SND_???", XT_INDEXED);
                case EV_REP:
                        return decode_bitset(tcp, arg, evdev_autorepeat,
-                                       REP_MAX, "REP_???");
+                                            REP_MAX, "REP_???", XT_INDEXED);
                case EV_FF:
                        return decode_bitset(tcp, arg, evdev_ff_types,
-                                       FF_MAX, "FF_???");
+                                            FF_MAX, "FF_???", XT_SORTED);
                case EV_PWR:
+                       tprints(", ");
                        printnum_int(tcp, arg, "%d");
-                       return 1;
+                       return RVAL_IOCTL_DECODED;
                case EV_FF_STATUS:
                        return decode_bitset(tcp, arg, evdev_ff_status,
-                                       FF_STATUS_MAX, "FF_STATUS_???");
+                                            FF_STATUS_MAX, "FF_STATUS_???",
+                                            XT_INDEXED);
                default:
-                       return 0;
+                       tprints(", ");
+                       printaddr(arg);
+                       return RVAL_IOCTL_DECODED;
        }
 }
 
 static int
-evdev_read_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
+evdev_read_ioctl(struct tcb *const tcp, const unsigned int code,
+                const kernel_ulong_t arg)
 {
-       if (syserror(tcp))
-               return 0;
-
        /* fixed-number fixed-length commands */
        switch (code) {
                case EVIOCGVERSION:
                        tprints(", ");
-                       printnum_int(tcp, arg, "%" PRIx32);
-                       return 1;
+                       printnum_int(tcp, arg, "%#x");
+                       return RVAL_IOCTL_DECODED;
                case EVIOCGEFFECTS:
                        tprints(", ");
-                       printnum_int(tcp, arg, "%" PRIu32);
-                       return 1;
+                       printnum_int(tcp, arg, "%u");
+                       return RVAL_IOCTL_DECODED;
                case EVIOCGID:
                        return getid_ioctl(tcp, arg);
 # ifdef EVIOCGREP
                case EVIOCGREP:
-                       return repeat_ioctl(tcp, arg);;
+                       return repeat_ioctl(tcp, arg);
 # endif
                case EVIOCGKEYCODE:
                        return keycode_ioctl(tcp, arg);
@@ -410,27 +321,31 @@ evdev_read_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
                case _IOC_NR(EVIOCGPHYS(0)):
                case _IOC_NR(EVIOCGUNIQ(0)):
                        tprints(", ");
-                       printstr(tcp, arg, tcp->u_rval - 1);
-                       return 1;
+                       if (syserror(tcp))
+                               printaddr(arg);
+                       else
+                               printstrn(tcp, arg, tcp->u_rval);
+                       return RVAL_IOCTL_DECODED;
 # ifdef EVIOCGPROP
                case _IOC_NR(EVIOCGPROP(0)):
-                       return decode_bitset(tcp, arg,
-                                       evdev_prop, INPUT_PROP_MAX, "PROP_???");
+                       return decode_bitset(tcp, arg, evdev_prop,
+                                            INPUT_PROP_MAX, "PROP_???",
+                                            XT_INDEXED);
 # endif
                case _IOC_NR(EVIOCGSND(0)):
-                       return decode_bitset(tcp, arg,
-                                       evdev_snd, SND_MAX, "SND_???");
+                       return decode_bitset(tcp, arg, evdev_snd,
+                                            SND_MAX, "SND_???", XT_INDEXED);
 # ifdef EVIOCGSW
                case _IOC_NR(EVIOCGSW(0)):
-                       return decode_bitset(tcp, arg,
-                                       evdev_switch, SW_MAX, "SW_???");
+                       return decode_bitset(tcp, arg, evdev_switch,
+                                            SW_MAX, "SW_???", XT_INDEXED);
 # endif
                case _IOC_NR(EVIOCGKEY(0)):
-                       return decode_bitset(tcp, arg,
-                                       evdev_keycode, KEY_MAX, "KEY_???");
+                       return decode_bitset(tcp, arg, evdev_keycode,
+                                            KEY_MAX, "KEY_???", XT_INDEXED);
                case _IOC_NR(EVIOCGLED(0)):
-                       return decode_bitset(tcp, arg,
-                                       evdev_leds, LED_MAX, "LED_???");
+                       return decode_bitset(tcp, arg, evdev_leds,
+                                            LED_MAX, "LED_???", XT_INDEXED);
        }
 
        /* multi-number fixed-length commands */
@@ -445,7 +360,8 @@ evdev_read_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
 }
 
 static int
-evdev_write_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
+evdev_write_ioctl(struct tcb *const tcp, const unsigned int code,
+                 const kernel_ulong_t arg)
 {
        /* fixed-number fixed-length commands */
        switch (code) {
@@ -459,23 +375,27 @@ evdev_write_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
                case EVIOCSKEYCODE_V2:
                        return keycode_V2_ioctl(tcp, arg);
 # endif
-               case EVIOCSFF:
-                       return ff_effect_ioctl(tcp, arg);
                case EVIOCRMFF:
                        tprintf(", %d", (int) arg);
-                       return 1;
+                       return RVAL_IOCTL_DECODED;
                case EVIOCGRAB:
 # ifdef EVIOCREVOKE
                case EVIOCREVOKE:
 # endif
-                       tprintf(", %lu", arg);
-                       return 1;
+                       tprintf(", %" PRI_klu, arg);
+                       return RVAL_IOCTL_DECODED;
 # ifdef EVIOCSCLOCKID
                case EVIOCSCLOCKID:
                        tprints(", ");
                        printnum_int(tcp, arg, "%u");
-                       return 1;
+                       return RVAL_IOCTL_DECODED;
 # endif
+               default: {
+                       int rc = evdev_write_ioctl_mpers(tcp, code, arg);
+
+                       if (rc != RVAL_DECODED)
+                               return rc;
+               }
        }
 
        /* multi-number fixed-length commands */
@@ -485,10 +405,17 @@ evdev_write_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
        return 0;
 }
 
+void
+print_evdev_ff_type(const kernel_ulong_t val)
+{
+       printxval(evdev_ff_types, val, "FF_???");
+}
+
 int
-evdev_ioctl(struct tcb *tcp, const unsigned int code, long arg)
+evdev_ioctl(struct tcb *const tcp,
+           const unsigned int code, const kernel_ulong_t arg)
 {
-       switch(_IOC_DIR(code)) {
+       switch (_IOC_DIR(code)) {
                case _IOC_READ:
                        if (entering(tcp))
                                return 0;