[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:
parent
f697a1ef94
commit
c5c84a1d86
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue