tsan: support synchronization by means of linux aio

http://llvm-reviews.chandlerc.com/D2269

llvm-svn: 195830
This commit is contained in:
Dmitry Vyukov 2013-11-27 09:10:47 +00:00
parent 3701f5465c
commit ee882ba4a1
5 changed files with 132 additions and 30 deletions

View File

@ -93,8 +93,8 @@ int main(int argc, char *argv[]) {
iocb[1].aio_lio_opcode = IOCB_CMD_PREAD;
iocb[1].aio_buf = (__u64)(&buf[kFortyTwo]);
iocb[1].aio_nbytes = kFortyTwo;
__sanitizer_syscall_post_io_submit(1, 0, 2, &iocbp);
assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo);
__sanitizer_syscall_pre_io_submit(0, 2, &iocbp);
assert(__msan_test_shadow(buf, sizeof(buf)) == 2 * kFortyTwo);
__msan_poison(buf, sizeof(buf));
char *p = buf;

View File

@ -25,8 +25,16 @@
// COMMON_SYSCALL_POST_WRITE_RANGE
// Called in posthook for regions that were written to by the kernel
// and are now initialized.
// COMMON_SYSCALL_ACQUIRE(addr)
// Acquire memory visibility from addr.
// COMMON_SYSCALL_RELEASE(addr)
// Release memory visibility to addr.
// COMMON_SYSCALL_FD_CLOSE(fd)
// Called before closing file descriptor fd.
// COMMON_SYSCALL_FD_ACQUIRE(fd)
// Acquire memory visibility from fd.
// COMMON_SYSCALL_FD_RELEASE(fd)
// Release memory visibility to fd.
// COMMON_SYSCALL_PRE_FORK()
// Called before fork syscall.
// COMMON_SYSCALL_POST_FORK(long res)
@ -48,16 +56,32 @@
#define POST_READ(p, s) COMMON_SYSCALL_POST_READ_RANGE(p, s)
#define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s)
#ifndef COMMON_SYSCALL_ACQUIRE
# define COMMON_SYSCALL_ACQUIRE(addr) ((void)(addr))
#endif
#ifndef COMMON_SYSCALL_RELEASE
# define COMMON_SYSCALL_RELEASE(addr) ((void)(addr))
#endif
#ifndef COMMON_SYSCALL_FD_CLOSE
# define COMMON_SYSCALL_FD_CLOSE(fd)
# define COMMON_SYSCALL_FD_CLOSE(fd) ((void)(fd))
#endif
#ifndef COMMON_SYSCALL_FD_ACQUIRE
# define COMMON_SYSCALL_FD_ACQUIRE(fd) ((void)(fd))
#endif
#ifndef COMMON_SYSCALL_FD_RELEASE
# define COMMON_SYSCALL_FD_RELEASE(fd) ((void)(fd))
#endif
#ifndef COMMON_SYSCALL_PRE_FORK
# define COMMON_SYSCALL_PRE_FORK()
# define COMMON_SYSCALL_PRE_FORK() {}
#endif
#ifndef COMMON_SYSCALL_POST_FORK
# define COMMON_SYSCALL_POST_FORK(res)
# define COMMON_SYSCALL_POST_FORK(res) {}
#endif
// FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such).
@ -1263,44 +1287,70 @@ PRE_SYSCALL(io_destroy)(long ctx) {}
POST_SYSCALL(io_destroy)(long res, long ctx) {}
PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr, void *events,
void *timeout) {
PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr,
__sanitizer_io_event *ioevpp, void *timeout) {
if (timeout) PRE_READ(timeout, struct_timespec_sz);
}
POST_SYSCALL(io_getevents)(long res, long ctx_id, long min_nr, long nr,
void *events, void *timeout) {
__sanitizer_io_event *ioevpp, void *timeout) {
if (res >= 0) {
if (events) POST_WRITE(events, res * struct_io_event_sz);
if (ioevpp) POST_WRITE(ioevpp, res * sizeof(*ioevpp));
if (timeout) POST_WRITE(timeout, struct_timespec_sz);
}
for (long i = 0; i < res; i++) {
// We synchronize io_submit -> io_getevents/io_cancel using the
// user-provided data context. Data is not necessary a pointer, it can be
// an int, 0 or whatever; acquire/release will correctly handle this.
// This scheme can lead to false negatives, e.g. when all operations
// synchronize on 0. But there does not seem to be a better solution
// (except wrapping all operations in own context, which is unreliable).
// We can not reliably extract fildes in io_getevents.
COMMON_SYSCALL_ACQUIRE((void*)ioevpp[i].data);
}
}
PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) {
for (long i = 0; i < nr; ++i) {
if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pwrite && iocbpp[i]->aio_buf &&
iocbpp[i]->aio_nbytes)
PRE_READ((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes);
uptr op = iocbpp[i]->aio_lio_opcode;
void *data = (void*)iocbpp[i]->aio_data;
void *buf = (void*)iocbpp[i]->aio_buf;
uptr len = (uptr)iocbpp[i]->aio_nbytes;
if (op == iocb_cmd_pwrite && buf && len) {
PRE_READ(buf, len);
} else if (op == iocb_cmd_pread && buf && len) {
POST_WRITE(buf, len);
} else if (op == iocb_cmd_pwritev) {
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
for (uptr v = 0; v < len; v++)
PRE_READ(iovec[i].iov_base, iovec[i].iov_len);
} else if (op == iocb_cmd_preadv) {
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
for (uptr v = 0; v < len; v++)
POST_WRITE(iovec[i].iov_base, iovec[i].iov_len);
}
// See comment in io_getevents.
COMMON_SYSCALL_RELEASE(data);
}
}
POST_SYSCALL(io_submit)(long res, long ctx_id, long nr,
__sanitizer_iocb **iocbpp) {
if (res > 0 && iocbpp) {
for (long i = 0; i < res; ++i) {
if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pread && iocbpp[i]->aio_buf &&
iocbpp[i]->aio_nbytes)
POST_WRITE((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes);
}
}
__sanitizer_iocb **iocbpp) {}
PRE_SYSCALL(io_cancel)(long ctx_id, __sanitizer_iocb *iocb,
__sanitizer_io_event *result) {
}
PRE_SYSCALL(io_cancel)(long ctx_id, void *iocb, void *result) {}
POST_SYSCALL(io_cancel)(long res, long ctx_id, void *iocb, void *result) {
if (res >= 0) {
if (iocb) POST_WRITE(iocb, sizeof(__sanitizer_iocb));
if (result) POST_WRITE(result, struct_io_event_sz);
POST_SYSCALL(io_cancel)(long res, long ctx_id, __sanitizer_iocb *iocb,
__sanitizer_io_event *result) {
if (res == 0) {
if (result) {
// See comment in io_getevents.
COMMON_SYSCALL_ACQUIRE((void*)result->data);
POST_WRITE(result, sizeof(*result));
}
if (iocb)
POST_WRITE(iocb, sizeof(*iocb));
}
}

View File

@ -70,7 +70,11 @@ COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat));
COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64));
#endif
COMPILER_CHECK(struct_io_event_sz == sizeof(struct io_event));
COMPILER_CHECK(sizeof(__sanitizer_io_event) == sizeof(struct io_event));
CHECK_SIZE_AND_OFFSET(io_event, data);
CHECK_SIZE_AND_OFFSET(io_event, obj);
CHECK_SIZE_AND_OFFSET(io_event, res);
CHECK_SIZE_AND_OFFSET(io_event, res2);
#if !SANITIZER_ANDROID
COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <=

View File

@ -70,7 +70,6 @@ namespace __sanitizer {
const unsigned struct_kernel_stat_sz = 144;
const unsigned struct_kernel_stat64_sz = 104;
#endif
const unsigned struct_io_event_sz = 32;
struct __sanitizer_perf_event_attr {
unsigned type;
unsigned size;
@ -109,8 +108,17 @@ namespace __sanitizer {
u64 aio_reserved3;
};
struct __sanitizer_io_event {
u64 data;
u64 obj;
u64 res;
u64 res2;
};
const unsigned iocb_cmd_pread = 0;
const unsigned iocb_cmd_pwrite = 1;
const unsigned iocb_cmd_preadv = 7;
const unsigned iocb_cmd_pwritev = 8;
struct __sanitizer___sysctl_args {
int *name;

View File

@ -1927,10 +1927,33 @@ static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
MemoryAccessRange(thr, pc, p, s, write);
}
static void syscall_acquire(uptr pc, uptr addr) {
TSAN_SYSCALL();
Acquire(thr, pc, addr);
Printf("syscall_acquire(%p)\n", addr);
}
static void syscall_release(uptr pc, uptr addr) {
TSAN_SYSCALL();
Printf("syscall_release(%p)\n", addr);
Release(thr, pc, addr);
}
static void syscall_fd_close(uptr pc, int fd) {
TSAN_SYSCALL();
if (fd >= 0)
FdClose(thr, pc, fd);
FdClose(thr, pc, fd);
}
static USED void syscall_fd_acquire(uptr pc, int fd) {
TSAN_SYSCALL();
FdAcquire(thr, pc, fd);
Printf("syscall_fd_acquire(%p)\n", fd);
}
static USED void syscall_fd_release(uptr pc, int fd) {
TSAN_SYSCALL();
Printf("syscall_fd_release(%p)\n", fd);
FdRelease(thr, pc, fd);
}
static void syscall_pre_fork(uptr pc) {
@ -1949,23 +1972,40 @@ static void syscall_post_fork(uptr pc, int res) {
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true)
#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
do { \
(void)(p); \
(void)(s); \
} while (false)
#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
do { \
(void)(p); \
(void)(s); \
} while (false)
#define COMMON_SYSCALL_ACQUIRE(addr) \
syscall_acquire(GET_CALLER_PC(), (uptr)(addr))
#define COMMON_SYSCALL_RELEASE(addr) \
syscall_release(GET_CALLER_PC(), (uptr)(addr))
#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd)
#define COMMON_SYSCALL_FD_ACQUIRE(fd) syscall_fd_acquire(GET_CALLER_PC(), fd)
#define COMMON_SYSCALL_FD_RELEASE(fd) syscall_fd_release(GET_CALLER_PC(), fd)
#define COMMON_SYSCALL_PRE_FORK() \
syscall_pre_fork(GET_CALLER_PC())
#define COMMON_SYSCALL_POST_FORK(res) \
syscall_post_fork(GET_CALLER_PC(), res)
#include "sanitizer_common/sanitizer_common_syscalls.inc"
namespace __tsan {