[sanitizer] Implement ioctl decoding.

When an unknown ioctl is encountered, try to guess the parameter size from the
request id.

llvm-svn: 200872
This commit is contained in:
Sergey Matveev 2014-02-05 19:35:24 +00:00
parent f697a1ef94
commit c5c84a1d86
5 changed files with 127 additions and 6 deletions

View File

@ -0,0 +1,29 @@
// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t
// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t
#include <assert.h>
#include <fcntl.h>
#include <sound/asound.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sanitizer/msan_interface.h>
int main(int argc, char **argv) {
int fd = open("/dev/snd/controlC0", O_RDONLY);
if (fd < 0) {
printf("Unable to open sound device.");
return 0;
}
const unsigned sz = sizeof(snd_ctl_card_info);
void *info = malloc(sz + 1);
assert(__msan_test_shadow(info, sz) == 0);
assert(ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, info) >= 0);
assert(__msan_test_shadow(info, sz + 1) == sz);
close(fd);
free(info);
return 0;
}

View File

@ -844,7 +844,14 @@ INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) {
if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg);
const ioctl_desc *desc = ioctl_lookup(request);
if (!desc) Printf("WARNING: unknown ioctl %x\n", request);
ioctl_desc decoded_desc;
if (!desc) {
VPrintf(2, "Decoding unknown ioctl 0x%x\n", request);
if (!ioctl_decode(request, &decoded_desc))
Printf("WARNING: failed decoding unknown ioctl 0x%x\n", request);
else
desc = &decoded_desc;
}
if (desc) ioctl_common_pre(ctx, desc, d, request, arg);
int res = REAL(ioctl)(d, request, arg);

View File

@ -493,11 +493,15 @@ static void ioctl_init() {
// Handle the most evil ioctls that encode argument value as part of request id.
static unsigned ioctl_request_fixup(unsigned req) {
#if SANITIZER_LINUX
if ((req & ~0x3fff001fU) == IOCTL_EVIOCGBIT)
// Strip size and event number.
const unsigned kEviocgbitMask =
(IOC_SIZEMASK << IOC_SIZESHIFT) | EVIOC_EV_MAX;
if ((req & ~kEviocgbitMask) == IOCTL_EVIOCGBIT)
return IOCTL_EVIOCGBIT;
if ((req & ~0x3fU) == IOCTL_EVIOCGABS)
// Strip absolute axis number.
if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCGABS)
return IOCTL_EVIOCGABS;
if ((req & ~0x3fU) == IOCTL_EVIOCSABS)
if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCSABS)
return IOCTL_EVIOCSABS;
#endif
return req;
@ -519,13 +523,44 @@ static const ioctl_desc *ioctl_table_lookup(unsigned req) {
return 0;
}
static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
CHECK(desc);
desc->req = req;
desc->name = "<DECODED_IOCTL>";
desc->size = IOC_SIZE(req);
// Sanity check.
if (desc->size > 1024) return false;
unsigned dir = IOC_DIR(req);
switch (dir) {
case IOC_NONE:
desc->type = ioctl_desc::NONE;
break;
case IOC_READ | IOC_WRITE:
desc->type = ioctl_desc::READWRITE;
break;
case IOC_READ:
desc->type = ioctl_desc::WRITE;
break;
case IOC_WRITE:
desc->type = ioctl_desc::READ;
break;
default:
return false;
}
if (desc->type != IOC_NONE && desc->size == 0) return false;
char id = IOC_TYPE(req);
// Sanity check.
if (!(id >= 'a' && id <= 'z') && !(id >= 'A' && id <= 'Z')) return false;
return true;
}
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);
desc = ioctl_table_lookup(req & ~(IOC_SIZEMASK << IOC_SIZESHIFT));
// 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 &&

View File

@ -787,7 +787,28 @@ 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);
// FIXME: We define those on Linux and Mac, but only check on Linux.
COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS);
COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS);
COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS);
COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS);
COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK);
COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK);
COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK);
COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK);
COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT);
COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT);
COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT);
COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT);
COMPILER_CHECK(IOC_NONE == _IOC_NONE);
COMPILER_CHECK(IOC_WRITE == _IOC_WRITE);
COMPILER_CHECK(IOC_READ == _IOC_READ);
COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX);
COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX);
COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678));
COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678));
COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678));
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID

View File

@ -517,7 +517,36 @@ namespace __sanitizer {
};
#endif
#define IOC_SIZE(nr) (((nr) >> 16) & 0x3fff)
#define IOC_NRBITS 8
#define IOC_TYPEBITS 8
#if defined(__powerpc__) || defined(__powerpc64__)
#define IOC_SIZEBITS 13
#define IOC_DIRBITS 3
#define IOC_NONE 1U
#define IOC_WRITE 4U
#define IOC_READ 2U
#else
#define IOC_SIZEBITS 14
#define IOC_DIRBITS 2
#define IOC_NONE 0U
#define IOC_WRITE 1U
#define IOC_READ 2U
#endif
#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
#define IOC_NRSHIFT 0
#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
#define EVIOC_EV_MAX 0x1f
#define EVIOC_ABS_MAX 0x3f
#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
extern unsigned struct_arpreq_sz;
extern unsigned struct_ifreq_sz;