Skip to content

Commit 9c3665c

Browse files
authored
[rtsan] Add I/O multiplexing interceptors (#115227)
Intercepts in the family of `poll`, `select` and modern equivalents `epoll` (linux only) and `kqueue` bsd family only. These calls mirror the names of the system calls they call, which have been verified on mac at least (e.g. kevent calls the system call kevent).
1 parent 5d33010 commit 9c3665c

File tree

3 files changed

+281
-0
lines changed

3 files changed

+281
-0
lines changed

compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ void OSSpinLockLock(volatile OSSpinLock *__lock);
4242
#endif
4343

4444
#include <fcntl.h>
45+
#include <poll.h>
4546
#include <pthread.h>
4647
#include <stdarg.h>
4748
#include <stdio.h>
@@ -612,6 +613,104 @@ INTERCEPTOR(int, shutdown, int socket, int how) {
612613
return REAL(shutdown)(socket, how);
613614
}
614615

616+
// I/O Multiplexing
617+
618+
INTERCEPTOR(int, poll, struct pollfd *fds, nfds_t nfds, int timeout) {
619+
__rtsan_notify_intercepted_call("poll");
620+
return REAL(poll)(fds, nfds, timeout);
621+
}
622+
623+
#if !SANITIZER_APPLE
624+
// FIXME: This should work on all unix systems, even Mac, but currently
625+
// it is showing some weird error while linking
626+
// error: declaration of 'select' has a different language linkage
627+
INTERCEPTOR(int, select, int nfds, fd_set *readfds, fd_set *writefds,
628+
fd_set *exceptfds, struct timeval *timeout) {
629+
__rtsan_notify_intercepted_call("select");
630+
return REAL(select)(nfds, readfds, writefds, exceptfds, timeout);
631+
}
632+
#define RTSAN_MAYBE_INTERCEPT_SELECT INTERCEPT_FUNCTION(select)
633+
#else
634+
#define RTSAN_MAYBE_INTERCEPT_SELECT
635+
#endif // !SANITIZER_APPLE
636+
637+
INTERCEPTOR(int, pselect, int nfds, fd_set *readfds, fd_set *writefds,
638+
fd_set *exceptfds, const struct timespec *timeout,
639+
const sigset_t *sigmask) {
640+
__rtsan_notify_intercepted_call("pselect");
641+
return REAL(pselect)(nfds, readfds, writefds, exceptfds, timeout, sigmask);
642+
}
643+
644+
#if SANITIZER_INTERCEPT_EPOLL
645+
INTERCEPTOR(int, epoll_create, int size) {
646+
__rtsan_notify_intercepted_call("epoll_create");
647+
return REAL(epoll_create)(size);
648+
}
649+
650+
INTERCEPTOR(int, epoll_create1, int flags) {
651+
__rtsan_notify_intercepted_call("epoll_create1");
652+
return REAL(epoll_create1)(flags);
653+
}
654+
655+
INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd,
656+
struct epoll_event *event) {
657+
__rtsan_notify_intercepted_call("epoll_ctl");
658+
return REAL(epoll_ctl)(epfd, op, fd, event);
659+
}
660+
661+
INTERCEPTOR(int, epoll_wait, int epfd, struct epoll_event *events,
662+
int maxevents, int timeout) {
663+
__rtsan_notify_intercepted_call("epoll_wait");
664+
return REAL(epoll_wait)(epfd, events, maxevents, timeout);
665+
}
666+
667+
INTERCEPTOR(int, epoll_pwait, int epfd, struct epoll_event *events,
668+
int maxevents, int timeout, const sigset_t *sigmask) {
669+
__rtsan_notify_intercepted_call("epoll_pwait");
670+
return REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask);
671+
}
672+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE INTERCEPT_FUNCTION(epoll_create)
673+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 INTERCEPT_FUNCTION(epoll_create1)
674+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_CTL INTERCEPT_FUNCTION(epoll_ctl)
675+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT INTERCEPT_FUNCTION(epoll_wait)
676+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT INTERCEPT_FUNCTION(epoll_pwait)
677+
#else
678+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE
679+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1
680+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_CTL
681+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT
682+
#define RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT
683+
#endif // SANITIZER_INTERCEPT_EPOLL
684+
685+
#if SANITIZER_INTERCEPT_KQUEUE
686+
INTERCEPTOR(int, kqueue, void) {
687+
__rtsan_notify_intercepted_call("kqueue");
688+
return REAL(kqueue)();
689+
}
690+
691+
INTERCEPTOR(int, kevent, int kq, const struct kevent *changelist, int nchanges,
692+
struct kevent *eventlist, int nevents,
693+
const struct timespec *timeout) {
694+
__rtsan_notify_intercepted_call("kevent");
695+
return REAL(kevent)(kq, changelist, nchanges, eventlist, nevents, timeout);
696+
}
697+
698+
INTERCEPTOR(int, kevent64, int kq, const struct kevent64_s *changelist,
699+
int nchanges, struct kevent64_s *eventlist, int nevents,
700+
unsigned int flags, const struct timespec *timeout) {
701+
__rtsan_notify_intercepted_call("kevent64");
702+
return REAL(kevent64)(kq, changelist, nchanges, eventlist, nevents, flags,
703+
timeout);
704+
}
705+
#define RTSAN_MAYBE_INTERCEPT_KQUEUE INTERCEPT_FUNCTION(kqueue)
706+
#define RTSAN_MAYBE_INTERCEPT_KEVENT INTERCEPT_FUNCTION(kevent)
707+
#define RTSAN_MAYBE_INTERCEPT_KEVENT64 INTERCEPT_FUNCTION(kevent64)
708+
#else
709+
#define RTSAN_MAYBE_INTERCEPT_KQUEUE
710+
#define RTSAN_MAYBE_INTERCEPT_KEVENT
711+
#define RTSAN_MAYBE_INTERCEPT_KEVENT64
712+
#endif // SANITIZER_INTERCEPT_KQUEUE
713+
615714
// Preinit
616715
void __rtsan::InitializeInterceptors() {
617716
INTERCEPT_FUNCTION(calloc);
@@ -696,6 +795,18 @@ void __rtsan::InitializeInterceptors() {
696795
INTERCEPT_FUNCTION(sendto);
697796
INTERCEPT_FUNCTION(shutdown);
698797
INTERCEPT_FUNCTION(socket);
798+
799+
RTSAN_MAYBE_INTERCEPT_SELECT;
800+
INTERCEPT_FUNCTION(pselect);
801+
INTERCEPT_FUNCTION(poll);
802+
RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE;
803+
RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1;
804+
RTSAN_MAYBE_INTERCEPT_EPOLL_CTL;
805+
RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT;
806+
RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT;
807+
RTSAN_MAYBE_INTERCEPT_KQUEUE;
808+
RTSAN_MAYBE_INTERCEPT_KEVENT;
809+
RTSAN_MAYBE_INTERCEPT_KEVENT64;
699810
}
700811

701812
#endif // SANITIZER_POSIX

compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,18 @@
2828
#include <malloc.h>
2929
#endif
3030

31+
#if SANITIZER_INTERCEPT_EPOLL
32+
#include <sys/epoll.h>
33+
#endif
34+
35+
#if SANITIZER_INTERCEPT_KQUEUE
36+
#include <sys/event.h>
37+
#include <sys/time.h>
38+
#endif
39+
3140
#include <fcntl.h>
3241
#include <netdb.h>
42+
#include <poll.h>
3343
#include <pthread.h>
3444
#include <stdio.h>
3545
#include <sys/mman.h>
@@ -779,4 +789,162 @@ TEST(TestRtsanInterceptors, ShutdownOnASocketDiesWhenRealtime) {
779789
ExpectNonRealtimeSurvival(Func);
780790
}
781791

792+
/*
793+
I/O Multiplexing
794+
*/
795+
796+
TEST(TestRtsanInterceptors, PollDiesWhenRealtime) {
797+
struct pollfd fds[1];
798+
fds[0].fd = 0;
799+
fds[0].events = POLLIN;
800+
801+
auto Func = [&fds]() { poll(fds, 1, 0); };
802+
803+
ExpectRealtimeDeath(Func, "poll");
804+
ExpectNonRealtimeSurvival(Func);
805+
}
806+
807+
#if !SANITIZER_APPLE
808+
// FIXME: This should work on Darwin as well
809+
// see the comment near the interceptor
810+
TEST(TestRtsanInterceptors, SelectDiesWhenRealtime) {
811+
fd_set readfds;
812+
FD_ZERO(&readfds);
813+
FD_SET(0, &readfds);
814+
struct timeval timeout = {0, 0};
815+
816+
auto Func = [&readfds, &timeout]() {
817+
select(1, &readfds, nullptr, nullptr, &timeout);
818+
};
819+
ExpectRealtimeDeath(Func, "select");
820+
ExpectNonRealtimeSurvival(Func);
821+
}
822+
#endif
823+
824+
TEST(TestRtsanInterceptors, PSelectDiesWhenRealtime) {
825+
fd_set readfds;
826+
FD_ZERO(&readfds);
827+
FD_SET(0, &readfds);
828+
struct timespec timeout = {0, 0};
829+
830+
auto Func = [&]() {
831+
pselect(1, &readfds, nullptr, nullptr, &timeout, nullptr);
832+
};
833+
ExpectRealtimeDeath(Func, "pselect");
834+
ExpectNonRealtimeSurvival(Func);
835+
}
836+
837+
#if SANITIZER_INTERCEPT_EPOLL
838+
TEST(TestRtsanInterceptors, EpollCreateDiesWhenRealtime) {
839+
auto Func = []() { epoll_create(1); };
840+
ExpectRealtimeDeath(Func, "epoll_create");
841+
ExpectNonRealtimeSurvival(Func);
842+
}
843+
844+
TEST(TestRtsanInterceptors, EpollCreate1DiesWhenRealtime) {
845+
auto Func = []() { epoll_create1(EPOLL_CLOEXEC); };
846+
ExpectRealtimeDeath(Func, "epoll_create1");
847+
ExpectNonRealtimeSurvival(Func);
848+
}
849+
850+
class EpollTest : public ::testing::Test {
851+
protected:
852+
void SetUp() override {
853+
epfd = epoll_create1(EPOLL_CLOEXEC);
854+
ASSERT_GE(epfd, 0);
855+
}
856+
857+
void TearDown() override {
858+
if (epfd >= 0)
859+
close(epfd);
860+
}
861+
862+
int GetEpollFd() { return epfd; }
863+
864+
private:
865+
int epfd = -1;
866+
};
867+
868+
TEST_F(EpollTest, EpollCtlDiesWhenRealtime) {
869+
auto Func = [this]() {
870+
struct epoll_event event = {.events = EPOLLIN, .data = {.fd = 0}};
871+
epoll_ctl(GetEpollFd(), EPOLL_CTL_ADD, 0, &event);
872+
};
873+
ExpectRealtimeDeath(Func, "epoll_ctl");
874+
ExpectNonRealtimeSurvival(Func);
875+
}
876+
877+
TEST_F(EpollTest, EpollWaitDiesWhenRealtime) {
878+
auto Func = [this]() {
879+
struct epoll_event events[1];
880+
epoll_wait(GetEpollFd(), events, 1, 0);
881+
};
882+
883+
ExpectRealtimeDeath(Func, "epoll_wait");
884+
ExpectNonRealtimeSurvival(Func);
885+
}
886+
887+
TEST_F(EpollTest, EpollPWaitDiesWhenRealtime) {
888+
auto Func = [this]() {
889+
struct epoll_event events[1];
890+
epoll_pwait(GetEpollFd(), events, 1, 0, nullptr);
891+
};
892+
893+
ExpectRealtimeDeath(Func, "epoll_pwait");
894+
ExpectNonRealtimeSurvival(Func);
895+
}
896+
#endif // SANITIZER_INTERCEPT_EPOLL
897+
898+
#if SANITIZER_INTERCEPT_KQUEUE
899+
TEST(TestRtsanInterceptors, KqueueDiesWhenRealtime) {
900+
auto Func = []() { kqueue(); };
901+
ExpectRealtimeDeath(Func, "kqueue");
902+
ExpectNonRealtimeSurvival(Func);
903+
}
904+
905+
class KqueueTest : public ::testing::Test {
906+
protected:
907+
void SetUp() override {
908+
kq = kqueue();
909+
ASSERT_GE(kq, 0);
910+
}
911+
912+
void TearDown() override {
913+
if (kq >= 0)
914+
close(kq);
915+
}
916+
917+
int GetKqueueFd() { return kq; }
918+
919+
private:
920+
int kq = -1;
921+
};
922+
923+
TEST_F(KqueueTest, KeventDiesWhenRealtime) {
924+
struct kevent event;
925+
EV_SET(&event, 0, EVFILT_READ, EV_ADD, 0, 0, nullptr);
926+
struct timespec timeout = {0, 0};
927+
928+
auto Func = [this, event, timeout]() {
929+
kevent(GetKqueueFd(), &event, 1, nullptr, 0, &timeout);
930+
};
931+
932+
ExpectRealtimeDeath(Func, "kevent");
933+
ExpectNonRealtimeSurvival(Func);
934+
}
935+
936+
TEST_F(KqueueTest, Kevent64DiesWhenRealtime) {
937+
struct kevent64_s event;
938+
EV_SET64(&event, 0, EVFILT_READ, EV_ADD, 0, 0, 0, 0, 0);
939+
struct timespec timeout = {0, 0};
940+
941+
auto Func = [this, event, timeout]() {
942+
kevent64(GetKqueueFd(), &event, 1, nullptr, 0, 0, &timeout);
943+
};
944+
945+
ExpectRealtimeDeath(Func, "kevent64");
946+
ExpectNonRealtimeSurvival(Func);
947+
}
948+
#endif // SANITIZER_INTERCEPT_KQUEUE
949+
782950
#endif // SANITIZER_POSIX

compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
339339
#define SANITIZER_INTERCEPT_GETGROUPS SI_POSIX
340340
#define SANITIZER_INTERCEPT_POLL SI_POSIX
341341
#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID || SI_SOLARIS
342+
#define SANITIZER_INTERCEPT_EPOLL (SI_LINUX)
343+
#define SANITIZER_INTERCEPT_KQUEUE (SI_FREEBSD || SI_NETBSD || SI_MAC)
342344
#define SANITIZER_INTERCEPT_WORDEXP \
343345
(SI_FREEBSD || SI_NETBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID || \
344346
SI_SOLARIS)

0 commit comments

Comments
 (0)