Index: sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- sanitizer_common/sanitizer_common_interceptors.inc +++ sanitizer_common/sanitizer_common_interceptors.inc @@ -40,7 +40,7 @@ #endif #ifndef COMMON_INTERCEPTOR_FD_ACCESS -#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) ((void)(fd)) #endif #ifndef COMMON_INTERCEPTOR_MUTEX_LOCK @@ -388,6 +388,77 @@ #define INIT_PWRITEV64 #endif +#if SANITIZER_INTERCEPT_AIO +INTERCEPTOR(int, io_submit, void *ctx1, long nr, __sanitizer_iocb **iocbpp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, io_submit, ctx1, nr, iocbpp); + for (long i = 0; i < nr; ++i) { + uptr op = iocbpp[i]->aio_lio_opcode; + int fd = iocbpp[i]->aio_fildes; + void *buf = (void*)iocbpp[i]->aio_buf; + uptr len = (uptr)iocbpp[i]->aio_nbytes; + if (buf && len) { + if (op == iocb_cmd_pwrite) + COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, len); + if (op == iocb_cmd_pread) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, len); + } + if (op == iocb_cmd_pwrite || op == iocb_cmd_pread) { + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + } + } + return REAL(io_submit)(ctx1, nr, iocbpp); +} + +INTERCEPTOR(int, io_cancel, void *ctx1, __sanitizer_iocb *iocb, void *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, io_cancel, ctx1, iocb, result); + int res = REAL(io_cancel)(ctx1, iocb, result); + if (res == 0) { + if (iocb) { + COMMON_INTERCEPTOR_FD_ACCESS(ctx, iocb->aio_fildes); + COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, iocb->aio_fildes); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iocb, sizeof(__sanitizer_iocb)); + } + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, struct_io_event_sz); + } + return res; +} + +INTERCEPTOR(int, io_getevents, void *ctx1, long nr, __sanitizer_iocb **iocbpp, + void *timeout) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, io_getevents, ctx1, nr, iocbpp, timeout); + if (timeout) + COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); + int res = REAL(io_getevents)(ctx1, nr, iocbpp, timeout); + if (res >= 0) { + if (iocbpp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iocbpp, + res * struct_io_event_sz); + if (timeout) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, timeout, + struct_timespec_sz); + } + for (int i = 0; i < res; i++) { + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pwrite || + iocbpp[i]->aio_lio_opcode == iocb_cmd_pread) { + COMMON_INTERCEPTOR_FD_ACCESS(ctx, iocbpp[i]->aio_fildes); + COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, iocbpp[i]->aio_fildes); + } + } + return res; +} + +#define INIT_AIO \ + COMMON_INTERCEPT_FUNCTION(io_submit); \ + COMMON_INTERCEPT_FUNCTION(io_cancel); \ + COMMON_INTERCEPT_FUNCTION(io_getevents) \ +/**/ + +#else +#define INIT_AIO +#endif + #if SANITIZER_INTERCEPT_PRCTL INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, // NOLINT @@ -2939,4 +3010,5 @@ INIT_LGAMMA_R; \ INIT_DRAND48_R; \ INIT_GETLINE; \ + INIT_AIO; \ /**/ Index: sanitizer_common/sanitizer_common_syscalls.inc =================================================================== --- sanitizer_common/sanitizer_common_syscalls.inc +++ sanitizer_common/sanitizer_common_syscalls.inc @@ -27,6 +27,10 @@ // and are now initialized. // 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) @@ -49,15 +53,23 @@ #define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s) #ifndef COMMON_SYSCALL_FD_CLOSE -# define COMMON_SYSCALL_FD_CLOSE(fd) +# define COMMON_SYSCALL_FD_CLOSE(fd) {} +#endif + +#ifndef COMMON_SYSCALL_FD_ACQUIRE +# define COMMON_SYSCALL_FD_ACQUIRE(fd) {} +#endif + +#ifndef COMMON_SYSCALL_FD_RELEASE +# define COMMON_SYSCALL_FD_RELEASE(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,17 +1275,22 @@ 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_iocb **iocbpp, 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_iocb **iocbpp, void *timeout) { if (res >= 0) { - if (events) POST_WRITE(events, res * struct_io_event_sz); + if (iocbpp) POST_WRITE(iocbpp, res * struct_io_event_sz); if (timeout) POST_WRITE(timeout, struct_timespec_sz); } + for (long i = 0; i < res; i++) { + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pwrite || + iocbpp[i]->aio_lio_opcode == iocb_cmd_pread) + COMMON_SYSCALL_FD_ACQUIRE(iocbpp[i]->aio_fildes); + } } PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) { @@ -1281,6 +1298,9 @@ 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); + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pwrite || + iocbpp[i]->aio_lio_opcode == iocb_cmd_pread) + COMMON_SYSCALL_FD_RELEASE(iocbpp[i]->aio_fildes); } } @@ -1290,15 +1310,20 @@ for (long i = 0; i < res; ++i) { if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pread && iocbpp[i]->aio_buf && iocbpp[i]->aio_nbytes) + // FIXME(dvyukov): from tsan point of view, this write must be before + // FD_RELEASE in PRE_SYSCALL(io_submit). Otherwise we can report + // false positive between io_submit and user write after io_getevents. POST_WRITE((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes); } } } -PRE_SYSCALL(io_cancel)(long ctx_id, void *iocb, void *result) {} +PRE_SYSCALL(io_cancel)(long ctx_id, __sanitizer_iocb *iocb, void *result) {} -POST_SYSCALL(io_cancel)(long res, long ctx_id, void *iocb, void *result) { +POST_SYSCALL(io_cancel)(long res, long ctx_id, __sanitizer_iocb *iocb, + void *result) { if (res >= 0) { + if (iocb) COMMON_SYSCALL_FD_ACQUIRE(iocb->aio_fildes); if (iocb) POST_WRITE(iocb, sizeof(__sanitizer_iocb)); if (result) POST_WRITE(result, struct_io_event_sz); } Index: sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- sanitizer_common/sanitizer_platform_interceptors.h +++ sanitizer_common/sanitizer_platform_interceptors.h @@ -66,6 +66,8 @@ #define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_AIO SI_LINUX_NOT_ANDROID + # define SANITIZER_INTERCEPT_PRCTL SI_LINUX # define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS Index: tsan/rtl/tsan_interceptors.cc =================================================================== --- tsan/rtl/tsan_interceptors.cc +++ tsan/rtl/tsan_interceptors.cc @@ -1929,8 +1929,17 @@ static void syscall_fd_close(uptr pc, int fd) { TSAN_SYSCALL(); - if (fd >= 0) - FdClose(thr, pc, fd); + FdClose(thr, pc, fd); +} + +static void syscall_fd_acquire(uptr pc, int fd) { + TSAN_SYSCALL(); + FdAcquire(thr, pc, fd); +} + +static void syscall_fd_release(uptr pc, int fd) { + TSAN_SYSCALL(); + FdRelease(thr, pc, fd); } static void syscall_pre_fork(uptr pc) { @@ -1949,23 +1958,34 @@ #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_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 { Index: tsan/rtl/tsan_stat.h =================================================================== --- tsan/rtl/tsan_stat.h +++ tsan/rtl/tsan_stat.h @@ -410,6 +410,9 @@ StatInt_lrand48_r, StatInt_getline, StatInt_getdelim, + StatInt_io_submit, + StatInt_io_cancel, + StatInt_io_getevents, StatInt_pthread_attr_getdetachstate, StatInt_pthread_attr_getguardsize, Index: tsan/rtl/tsan_stat.cc =================================================================== --- tsan/rtl/tsan_stat.cc +++ tsan/rtl/tsan_stat.cc @@ -415,6 +415,9 @@ name[StatInt_lrand48_r] = " lrand48_r "; name[StatInt_getline] = " getline "; name[StatInt_getdelim] = " getdelim "; + name[StatInt_io_submit] = " io_submit "; + name[StatInt_io_cancel] = " io_cancel "; + name[StatInt_io_getevents] = " io_getevents "; name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT