[compiler-rt] 9c3665c - [rtsan] Add I/O multiplexing interceptors (#115227)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 18 15:29:52 PST 2024
Author: Chris Apple
Date: 2024-11-18T15:29:49-08:00
New Revision: 9c3665c8d26ba041a6e582e83cc2de0a1f63be48
URL: https://github.com/llvm/llvm-project/commit/9c3665c8d26ba041a6e582e83cc2de0a1f63be48
DIFF: https://github.com/llvm/llvm-project/commit/9c3665c8d26ba041a6e582e83cc2de0a1f63be48.diff
LOG: [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).
Added:
Modified:
compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
Removed:
################################################################################
diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
index 3a1b1f6524745f..497db4ecc6ef4c 100644
--- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
@@ -42,6 +42,7 @@ void OSSpinLockLock(volatile OSSpinLock *__lock);
#endif
#include <fcntl.h>
+#include <poll.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
@@ -612,6 +613,104 @@ INTERCEPTOR(int, shutdown, int socket, int how) {
return REAL(shutdown)(socket, how);
}
+// I/O Multiplexing
+
+INTERCEPTOR(int, poll, struct pollfd *fds, nfds_t nfds, int timeout) {
+ __rtsan_notify_intercepted_call("poll");
+ return REAL(poll)(fds, nfds, timeout);
+}
+
+#if !SANITIZER_APPLE
+// FIXME: This should work on all unix systems, even Mac, but currently
+// it is showing some weird error while linking
+// error: declaration of 'select' has a
diff erent language linkage
+INTERCEPTOR(int, select, int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout) {
+ __rtsan_notify_intercepted_call("select");
+ return REAL(select)(nfds, readfds, writefds, exceptfds, timeout);
+}
+#define RTSAN_MAYBE_INTERCEPT_SELECT INTERCEPT_FUNCTION(select)
+#else
+#define RTSAN_MAYBE_INTERCEPT_SELECT
+#endif // !SANITIZER_APPLE
+
+INTERCEPTOR(int, pselect, int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, const struct timespec *timeout,
+ const sigset_t *sigmask) {
+ __rtsan_notify_intercepted_call("pselect");
+ return REAL(pselect)(nfds, readfds, writefds, exceptfds, timeout, sigmask);
+}
+
+#if SANITIZER_INTERCEPT_EPOLL
+INTERCEPTOR(int, epoll_create, int size) {
+ __rtsan_notify_intercepted_call("epoll_create");
+ return REAL(epoll_create)(size);
+}
+
+INTERCEPTOR(int, epoll_create1, int flags) {
+ __rtsan_notify_intercepted_call("epoll_create1");
+ return REAL(epoll_create1)(flags);
+}
+
+INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd,
+ struct epoll_event *event) {
+ __rtsan_notify_intercepted_call("epoll_ctl");
+ return REAL(epoll_ctl)(epfd, op, fd, event);
+}
+
+INTERCEPTOR(int, epoll_wait, int epfd, struct epoll_event *events,
+ int maxevents, int timeout) {
+ __rtsan_notify_intercepted_call("epoll_wait");
+ return REAL(epoll_wait)(epfd, events, maxevents, timeout);
+}
+
+INTERCEPTOR(int, epoll_pwait, int epfd, struct epoll_event *events,
+ int maxevents, int timeout, const sigset_t *sigmask) {
+ __rtsan_notify_intercepted_call("epoll_pwait");
+ return REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask);
+}
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE INTERCEPT_FUNCTION(epoll_create)
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 INTERCEPT_FUNCTION(epoll_create1)
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_CTL INTERCEPT_FUNCTION(epoll_ctl)
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT INTERCEPT_FUNCTION(epoll_wait)
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT INTERCEPT_FUNCTION(epoll_pwait)
+#else
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_CTL
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT
+#define RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT
+#endif // SANITIZER_INTERCEPT_EPOLL
+
+#if SANITIZER_INTERCEPT_KQUEUE
+INTERCEPTOR(int, kqueue, void) {
+ __rtsan_notify_intercepted_call("kqueue");
+ return REAL(kqueue)();
+}
+
+INTERCEPTOR(int, kevent, int kq, const struct kevent *changelist, int nchanges,
+ struct kevent *eventlist, int nevents,
+ const struct timespec *timeout) {
+ __rtsan_notify_intercepted_call("kevent");
+ return REAL(kevent)(kq, changelist, nchanges, eventlist, nevents, timeout);
+}
+
+INTERCEPTOR(int, kevent64, int kq, const struct kevent64_s *changelist,
+ int nchanges, struct kevent64_s *eventlist, int nevents,
+ unsigned int flags, const struct timespec *timeout) {
+ __rtsan_notify_intercepted_call("kevent64");
+ return REAL(kevent64)(kq, changelist, nchanges, eventlist, nevents, flags,
+ timeout);
+}
+#define RTSAN_MAYBE_INTERCEPT_KQUEUE INTERCEPT_FUNCTION(kqueue)
+#define RTSAN_MAYBE_INTERCEPT_KEVENT INTERCEPT_FUNCTION(kevent)
+#define RTSAN_MAYBE_INTERCEPT_KEVENT64 INTERCEPT_FUNCTION(kevent64)
+#else
+#define RTSAN_MAYBE_INTERCEPT_KQUEUE
+#define RTSAN_MAYBE_INTERCEPT_KEVENT
+#define RTSAN_MAYBE_INTERCEPT_KEVENT64
+#endif // SANITIZER_INTERCEPT_KQUEUE
+
// Preinit
void __rtsan::InitializeInterceptors() {
INTERCEPT_FUNCTION(calloc);
@@ -696,6 +795,18 @@ void __rtsan::InitializeInterceptors() {
INTERCEPT_FUNCTION(sendto);
INTERCEPT_FUNCTION(shutdown);
INTERCEPT_FUNCTION(socket);
+
+ RTSAN_MAYBE_INTERCEPT_SELECT;
+ INTERCEPT_FUNCTION(pselect);
+ INTERCEPT_FUNCTION(poll);
+ RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE;
+ RTSAN_MAYBE_INTERCEPT_EPOLL_CREATE1;
+ RTSAN_MAYBE_INTERCEPT_EPOLL_CTL;
+ RTSAN_MAYBE_INTERCEPT_EPOLL_WAIT;
+ RTSAN_MAYBE_INTERCEPT_EPOLL_PWAIT;
+ RTSAN_MAYBE_INTERCEPT_KQUEUE;
+ RTSAN_MAYBE_INTERCEPT_KEVENT;
+ RTSAN_MAYBE_INTERCEPT_KEVENT64;
}
#endif // SANITIZER_POSIX
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
index d0ae12c9bea447..5be62b97906383 100644
--- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
@@ -28,8 +28,18 @@
#include <malloc.h>
#endif
+#if SANITIZER_INTERCEPT_EPOLL
+#include <sys/epoll.h>
+#endif
+
+#if SANITIZER_INTERCEPT_KQUEUE
+#include <sys/event.h>
+#include <sys/time.h>
+#endif
+
#include <fcntl.h>
#include <netdb.h>
+#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/mman.h>
@@ -779,4 +789,162 @@ TEST(TestRtsanInterceptors, ShutdownOnASocketDiesWhenRealtime) {
ExpectNonRealtimeSurvival(Func);
}
+/*
+ I/O Multiplexing
+*/
+
+TEST(TestRtsanInterceptors, PollDiesWhenRealtime) {
+ struct pollfd fds[1];
+ fds[0].fd = 0;
+ fds[0].events = POLLIN;
+
+ auto Func = [&fds]() { poll(fds, 1, 0); };
+
+ ExpectRealtimeDeath(Func, "poll");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if !SANITIZER_APPLE
+// FIXME: This should work on Darwin as well
+// see the comment near the interceptor
+TEST(TestRtsanInterceptors, SelectDiesWhenRealtime) {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ struct timeval timeout = {0, 0};
+
+ auto Func = [&readfds, &timeout]() {
+ select(1, &readfds, nullptr, nullptr, &timeout);
+ };
+ ExpectRealtimeDeath(Func, "select");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+TEST(TestRtsanInterceptors, PSelectDiesWhenRealtime) {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ struct timespec timeout = {0, 0};
+
+ auto Func = [&]() {
+ pselect(1, &readfds, nullptr, nullptr, &timeout, nullptr);
+ };
+ ExpectRealtimeDeath(Func, "pselect");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if SANITIZER_INTERCEPT_EPOLL
+TEST(TestRtsanInterceptors, EpollCreateDiesWhenRealtime) {
+ auto Func = []() { epoll_create(1); };
+ ExpectRealtimeDeath(Func, "epoll_create");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, EpollCreate1DiesWhenRealtime) {
+ auto Func = []() { epoll_create1(EPOLL_CLOEXEC); };
+ ExpectRealtimeDeath(Func, "epoll_create1");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+class EpollTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ epfd = epoll_create1(EPOLL_CLOEXEC);
+ ASSERT_GE(epfd, 0);
+ }
+
+ void TearDown() override {
+ if (epfd >= 0)
+ close(epfd);
+ }
+
+ int GetEpollFd() { return epfd; }
+
+private:
+ int epfd = -1;
+};
+
+TEST_F(EpollTest, EpollCtlDiesWhenRealtime) {
+ auto Func = [this]() {
+ struct epoll_event event = {.events = EPOLLIN, .data = {.fd = 0}};
+ epoll_ctl(GetEpollFd(), EPOLL_CTL_ADD, 0, &event);
+ };
+ ExpectRealtimeDeath(Func, "epoll_ctl");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST_F(EpollTest, EpollWaitDiesWhenRealtime) {
+ auto Func = [this]() {
+ struct epoll_event events[1];
+ epoll_wait(GetEpollFd(), events, 1, 0);
+ };
+
+ ExpectRealtimeDeath(Func, "epoll_wait");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST_F(EpollTest, EpollPWaitDiesWhenRealtime) {
+ auto Func = [this]() {
+ struct epoll_event events[1];
+ epoll_pwait(GetEpollFd(), events, 1, 0, nullptr);
+ };
+
+ ExpectRealtimeDeath(Func, "epoll_pwait");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif // SANITIZER_INTERCEPT_EPOLL
+
+#if SANITIZER_INTERCEPT_KQUEUE
+TEST(TestRtsanInterceptors, KqueueDiesWhenRealtime) {
+ auto Func = []() { kqueue(); };
+ ExpectRealtimeDeath(Func, "kqueue");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+class KqueueTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ kq = kqueue();
+ ASSERT_GE(kq, 0);
+ }
+
+ void TearDown() override {
+ if (kq >= 0)
+ close(kq);
+ }
+
+ int GetKqueueFd() { return kq; }
+
+private:
+ int kq = -1;
+};
+
+TEST_F(KqueueTest, KeventDiesWhenRealtime) {
+ struct kevent event;
+ EV_SET(&event, 0, EVFILT_READ, EV_ADD, 0, 0, nullptr);
+ struct timespec timeout = {0, 0};
+
+ auto Func = [this, event, timeout]() {
+ kevent(GetKqueueFd(), &event, 1, nullptr, 0, &timeout);
+ };
+
+ ExpectRealtimeDeath(Func, "kevent");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST_F(KqueueTest, Kevent64DiesWhenRealtime) {
+ struct kevent64_s event;
+ EV_SET64(&event, 0, EVFILT_READ, EV_ADD, 0, 0, 0, 0, 0);
+ struct timespec timeout = {0, 0};
+
+ auto Func = [this, event, timeout]() {
+ kevent64(GetKqueueFd(), &event, 1, nullptr, 0, 0, &timeout);
+ };
+
+ ExpectRealtimeDeath(Func, "kevent64");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif // SANITIZER_INTERCEPT_KQUEUE
+
#endif // SANITIZER_POSIX
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 3fd6b595ef197f..7f9d4998bf757c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -339,6 +339,8 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
#define SANITIZER_INTERCEPT_GETGROUPS SI_POSIX
#define SANITIZER_INTERCEPT_POLL SI_POSIX
#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID || SI_SOLARIS
+#define SANITIZER_INTERCEPT_EPOLL (SI_LINUX)
+#define SANITIZER_INTERCEPT_KQUEUE (SI_FREEBSD || SI_NETBSD || SI_MAC)
#define SANITIZER_INTERCEPT_WORDEXP \
(SI_FREEBSD || SI_NETBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID || \
SI_SOLARIS)
More information about the llvm-commits
mailing list