[sanitizer] Handle EVIOxxxx ioctls.


git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@184405 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index d41f871..55c3ad1 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -40,6 +40,7 @@
   sanitizer_atomic.h
   sanitizer_common.h
   sanitizer_common_interceptors.inc
+  sanitizer_common_interceptors_ioctl.inc
   sanitizer_common_interceptors_scanf.inc
   sanitizer_common_syscalls.inc
   sanitizer_flags.h
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
index a5dbf0c..880644b 100755
--- a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
@@ -130,6 +130,26 @@
   _(CDROMVOLCTRL, READ, struct_cdrom_volctrl_sz);
   _(CDROMVOLREAD, WRITE, struct_cdrom_volctrl_sz);
   _(CDROM_GET_UPC, WRITE, 8);
+  _(EVIOCGABS, WRITE, struct_input_absinfo_sz); // fixup
+  _(EVIOCGBIT, WRITE, struct_input_id_sz); // fixup
+  _(EVIOCGEFFECTS, WRITE, sizeof(int));
+  _(EVIOCGID, WRITE, struct_input_id_sz);
+  _(EVIOCGKEY, WRITE, 0);
+  _(EVIOCGKEYCODE, WRITE, sizeof(int) * 2);
+  _(EVIOCGLED, WRITE, 0);
+  _(EVIOCGNAME, WRITE, 0);
+  _(EVIOCGPHYS, WRITE, 0);
+  _(EVIOCGRAB, READ, sizeof(int));
+  _(EVIOCGREP, WRITE, sizeof(int) * 2);
+  _(EVIOCGSND, WRITE, 0);
+  _(EVIOCGSW, WRITE, 0);
+  _(EVIOCGUNIQ, WRITE, 0);
+  _(EVIOCGVERSION, WRITE, sizeof(int));
+  _(EVIOCRMFF, READ, sizeof(int));
+  _(EVIOCSABS, READ, struct_input_absinfo_sz); // fixup
+  _(EVIOCSFF, READ, struct_ff_effect_sz);
+  _(EVIOCSKEYCODE, READ, sizeof(int) * 2);
+  _(EVIOCSREP, READ, sizeof(int) * 2);
   _(FDCLRPRM, NONE, 0);
   _(FDDEFPRM, READ, struct_floppy_struct_sz);
   _(FDFLUSH, NONE, 0);
@@ -350,6 +370,9 @@
   _(EQL_GETSLAVECFG, WRITE, struct_ifreq_sz);
   _(EQL_SETMASTRCFG, WRITE, struct_ifreq_sz);
   _(EQL_SETSLAVECFG, WRITE, struct_ifreq_sz);
+  _(EVIOCGKEYCODE_V2, WRITE, struct_input_keymap_entry_sz);
+  _(EVIOCGPROP, WRITE, 0);
+  _(EVIOCSKEYCODE_V2, READ, struct_input_keymap_entry_sz);
   _(FS_IOC_GETFLAGS, WRITE, sizeof(int));
   _(FS_IOC_GETVERSION, WRITE, sizeof(int));
   _(FS_IOC_SETFLAGS, READ, sizeof(int));
@@ -463,7 +486,18 @@
   ioctl_initialized = true;
 }
 
-static const ioctl_desc *ioctl_lookup(unsigned req) {
+// Handle the most evil ioctls that encode argument value as part of request id.
+static unsigned ioctl_request_fixup(unsigned req) {
+  if ((req & ~0x3fff001fU) == IOCTL_EVIOCGBIT)
+    return IOCTL_EVIOCGBIT;
+  if ((req & ~0x3fU) == IOCTL_EVIOCGABS)
+    return IOCTL_EVIOCGABS;
+  if ((req & ~0x3fU) == IOCTL_EVIOCSABS)
+    return IOCTL_EVIOCSABS;
+  return req;
+}
+
+static const ioctl_desc *ioctl_table_lookup(unsigned req) {
   int left = 0;
   int right = ioctl_table_size;
   while (left < right) {
@@ -479,10 +513,27 @@
     return 0;
 }
 
+static const ioctl_desc *ioctl_lookup(unsigned req) {
+  req = ioctl_request_fixup(req);
+  const ioctl_desc *desc = ioctl_table_lookup(req);
+  if (desc) return desc;
+
+  // Try stripping access size from the request id.
+  desc = ioctl_table_lookup(req & ~0x3fff0000U);
+  // Sanity check: requests that encode access size are either read or write and
+  // have size of 0 in the table.
+  if (desc && desc->size == 0 &&
+      (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READ))
+    return desc;
+  return 0;
+}
+
 static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
                              unsigned request, void *arg) {
-  if (desc->type == ioctl_desc::READ)
-    COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, desc->size);
+  if (desc->type == ioctl_desc::READ) {
+    unsigned size = desc->size ? desc->size : IOC_SIZE(request);
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, size);
+  }
   if (desc->type != ioctl_desc::CUSTOM)
     return;
   switch (request) {
@@ -499,7 +550,8 @@
                               unsigned request, void *arg) {
   if (desc->type == ioctl_desc::WRITE) {
     // FIXME: add verbose output
-    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, desc->size);
+    unsigned size = desc->size ? desc->size : IOC_SIZE(request);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, size);
   }
   if (desc->type != ioctl_desc::CUSTOM)
     return;
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index 9264e61..7ae0c65 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -46,6 +46,8 @@
 #include <linux/fd.h>
 #include <linux/fs.h>
 #include <linux/hdreg.h>
+#include <linux/input.h>
+#include <linux/ioctl.h>
 #include <linux/soundcard.h>
 #endif
 
@@ -173,6 +175,7 @@
   unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
   unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
   unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
+  unsigned struct_ff_effect_sz = sizeof(struct ff_effect);
   unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params);
   unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct);
   unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state);
@@ -183,6 +186,8 @@
   unsigned struct_format_descr_sz = sizeof(struct format_descr);
   unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid);
   unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry);
+  unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo);
+  unsigned struct_input_id_sz = sizeof(struct input_id);
   unsigned struct_midi_info_sz = sizeof(struct midi_info);
   unsigned struct_mtget_sz = sizeof(struct mtget);
   unsigned struct_mtop_sz = sizeof(struct mtop);
@@ -199,15 +204,16 @@
 #endif
 
 #if SANITIZER_LINUX && !SANITIZER_ANDROID
+  unsigned mpu_command_rec_sz = sizeof(mpu_command_rec);
   unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
   unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
   unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor);
+  unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry);
   unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data);
   unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs);
   unsigned struct_kbentry_sz = sizeof(struct kbentry);
   unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode);
   unsigned struct_kbsentry_sz = sizeof(struct kbsentry);
-  unsigned mpu_command_rec_sz = sizeof(mpu_command_rec);
   unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo);
   unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct);
   unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
@@ -275,6 +281,26 @@
   unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
 #endif
 #if SANITIZER_LINUX
+  unsigned IOCTL_EVIOCGABS = EVIOCGABS(0);
+  unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0);
+  unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS;
+  unsigned IOCTL_EVIOCGID = EVIOCGID;
+  unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0);
+  unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE;
+  unsigned IOCTL_EVIOCGLED = EVIOCGLED(0);
+  unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0);
+  unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0);
+  unsigned IOCTL_EVIOCGRAB = EVIOCGRAB;
+  unsigned IOCTL_EVIOCGREP = EVIOCGREP;
+  unsigned IOCTL_EVIOCGSND = EVIOCGSND(0);
+  unsigned IOCTL_EVIOCGSW = EVIOCGSW(0);
+  unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0);
+  unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION;
+  unsigned IOCTL_EVIOCRMFF = EVIOCRMFF;
+  unsigned IOCTL_EVIOCSABS = EVIOCSABS(0);
+  unsigned IOCTL_EVIOCSFF = EVIOCSFF;
+  unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE;
+  unsigned IOCTL_EVIOCSREP = EVIOCSREP;
   unsigned IOCTL_BLKFLSBUF = BLKFLSBUF;
   unsigned IOCTL_BLKGETSIZE = BLKGETSIZE;
   unsigned IOCTL_BLKRAGET = BLKRAGET;
@@ -530,6 +556,9 @@
   unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG;
   unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG;
   unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG;
+  unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2;
+  unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0);
+  unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2;
   unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS;
   unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION;
   unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS;
@@ -635,6 +664,8 @@
 CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
 CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
 CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+
+COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
 #endif
 
 CHECK_TYPE_SIZE(addrinfo);
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index 077badf..cf55d36 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -160,6 +160,8 @@
   };
 #endif
 
+#define IOC_SIZE(nr) (((nr) >> 16) & 0x3fff)
+
   extern unsigned struct_arpreq_sz;
   extern unsigned struct_ifreq_sz;
   extern unsigned struct_termios_sz;
@@ -177,6 +179,7 @@
   extern unsigned struct_copr_buffer_sz;
   extern unsigned struct_copr_debug_buf_sz;
   extern unsigned struct_copr_msg_sz;
+  extern unsigned struct_ff_effect_sz;
   extern unsigned struct_floppy_drive_params_sz;
   extern unsigned struct_floppy_drive_struct_sz;
   extern unsigned struct_floppy_fdc_state_sz;
@@ -187,6 +190,8 @@
   extern unsigned struct_format_descr_sz;
   extern unsigned struct_hd_driveid_sz;
   extern unsigned struct_hd_geometry_sz;
+  extern unsigned struct_input_absinfo_sz;
+  extern unsigned struct_input_id_sz;
   extern unsigned struct_midi_info_sz;
   extern unsigned struct_mtget_sz;
   extern unsigned struct_mtop_sz;
@@ -203,15 +208,16 @@
 #endif
 
 #if SANITIZER_LINUX && !SANITIZER_ANDROID
+  extern unsigned mpu_command_rec_sz;
   extern unsigned struct_audio_buf_info_sz;
   extern unsigned struct_ax25_parms_struct_sz;
   extern unsigned struct_cyclades_monitor_sz;
+  extern unsigned struct_input_keymap_entry_sz;
   extern unsigned struct_ipx_config_data_sz;
   extern unsigned struct_kbdiacrs_sz;
   extern unsigned struct_kbentry_sz;
   extern unsigned struct_kbkeycode_sz;
   extern unsigned struct_kbsentry_sz;
-  extern unsigned mpu_command_rec_sz;
   extern unsigned struct_mtconfiginfo_sz;
   extern unsigned struct_nr_parms_struct_sz;
   extern unsigned struct_ppp_stats_sz;
@@ -279,6 +285,26 @@
   extern unsigned IOCTL_SIOCGETVIFCNT;
 #endif
 #if SANITIZER_LINUX
+  extern unsigned IOCTL_EVIOCGABS;
+  extern unsigned IOCTL_EVIOCGBIT;
+  extern unsigned IOCTL_EVIOCGEFFECTS;
+  extern unsigned IOCTL_EVIOCGID;
+  extern unsigned IOCTL_EVIOCGKEY;
+  extern unsigned IOCTL_EVIOCGKEYCODE;
+  extern unsigned IOCTL_EVIOCGLED;
+  extern unsigned IOCTL_EVIOCGNAME;
+  extern unsigned IOCTL_EVIOCGPHYS;
+  extern unsigned IOCTL_EVIOCGRAB;
+  extern unsigned IOCTL_EVIOCGREP;
+  extern unsigned IOCTL_EVIOCGSND;
+  extern unsigned IOCTL_EVIOCGSW;
+  extern unsigned IOCTL_EVIOCGUNIQ;
+  extern unsigned IOCTL_EVIOCGVERSION;
+  extern unsigned IOCTL_EVIOCRMFF;
+  extern unsigned IOCTL_EVIOCSABS;
+  extern unsigned IOCTL_EVIOCSFF;
+  extern unsigned IOCTL_EVIOCSKEYCODE;
+  extern unsigned IOCTL_EVIOCSREP;
   extern unsigned IOCTL_BLKFLSBUF;
   extern unsigned IOCTL_BLKGETSIZE;
   extern unsigned IOCTL_BLKRAGET;
@@ -534,6 +560,9 @@
   extern unsigned IOCTL_EQL_GETSLAVECFG;
   extern unsigned IOCTL_EQL_SETMASTRCFG;
   extern unsigned IOCTL_EQL_SETSLAVECFG;
+  extern unsigned IOCTL_EVIOCGKEYCODE_V2;
+  extern unsigned IOCTL_EVIOCGPROP;
+  extern unsigned IOCTL_EVIOCSKEYCODE_V2;
   extern unsigned IOCTL_FS_IOC_GETFLAGS;
   extern unsigned IOCTL_FS_IOC_GETVERSION;
   extern unsigned IOCTL_FS_IOC_SETFLAGS;
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index 9e820e5..8f871a5 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -5,6 +5,7 @@
   sanitizer_atomic_test.cc
   sanitizer_common_test.cc
   sanitizer_flags_test.cc
+  sanitizer_ioctl_test.cc
   sanitizer_libc_test.cc
   sanitizer_linux_test.cc
   sanitizer_list_test.cc
@@ -31,7 +32,8 @@
   -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
   -DGTEST_HAS_RTTI=0
   -O2 -g -fno-rtti
-  -Wall -Werror -Werror=sign-compare)
+  -Wall -Werror -Werror=sign-compare
+  -Wno-unused-function)
 
 set(SANITIZER_TEST_LINK_FLAGS_COMMON
   -lstdc++ -ldl)
diff --git a/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc b/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc
new file mode 100644
index 0000000..1ecd6cb
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc
@@ -0,0 +1,72 @@
+//===-- sanitizer_ioctl_test.cc -------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for ioctl interceptor implementation in sanitizer_common.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include <linux/input.h>
+#include <vector>
+
+#include "interception/interception.h"
+#include "sanitizer_test_utils.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "gtest/gtest.h"
+
+
+using namespace __sanitizer;
+
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, sz) \
+  do {                                              \
+    (void) ctx;                                     \
+    (void) ptr;                                     \
+    (void) sz;                                      \
+  } while (0)
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sz) \
+  do {                                               \
+    (void) ctx;                                      \
+    (void) ptr;                                      \
+    (void) sz;                                       \
+  } while (0)
+
+#include "sanitizer_common/sanitizer_common_interceptors_ioctl.inc"
+
+static struct IoctlInit {
+  IoctlInit() { ioctl_init(); }
+} ioctl_static_initializer;
+
+TEST(SanitizerIoctl, Fixup) {
+  EXPECT_EQ((unsigned)FIONBIO, ioctl_request_fixup(FIONBIO));
+
+  EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(0, 16)));
+  EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 16)));
+  EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 17)));
+  EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(31, 16)));
+  EXPECT_NE(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(32, 16)));
+
+  EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(0)));
+  EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(5)));
+  EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(63)));
+  EXPECT_NE(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(64)));
+
+  EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(0)));
+  EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(5)));
+  EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(63)));
+  EXPECT_NE(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(64)));
+
+  const ioctl_desc *desc = ioctl_lookup(EVIOCGKEY(16));
+  EXPECT_NE((void *)0, desc);
+  EXPECT_EQ(EVIOCGKEY(0), desc->req);
+}
+
+#endif // SANITIZER_LINUX