diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp index 91d023e858ba3..5debf13ab9815 100644 --- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp @@ -190,6 +190,23 @@ INTERCEPTOR(int, fcntl, int filedes, int cmd, ...) { return REAL(fcntl)(filedes, cmd, arg); } +INTERCEPTOR(int, ioctl, int filedes, unsigned long request, ...) { + __rtsan_notify_intercepted_call("ioctl"); + + // See fcntl for discussion on why we use intptr_t + // And why we read from va_args on all request types + using arg_type = intptr_t; + static_assert(sizeof(arg_type) >= sizeof(struct ifreq *)); + static_assert(sizeof(arg_type) >= sizeof(int)); + + va_list args; + va_start(args, request); + arg_type arg = va_arg(args, arg_type); + va_end(args); + + return REAL(ioctl)(filedes, request, arg); +} + #if SANITIZER_INTERCEPT_FCNTL64 INTERCEPTOR(int, fcntl64, int filedes, int cmd, ...) { __rtsan_notify_intercepted_call("fcntl64"); @@ -801,6 +818,7 @@ void __rtsan::InitializeInterceptors() { RTSAN_MAYBE_INTERCEPT_LSEEK64; INTERCEPT_FUNCTION(dup); INTERCEPT_FUNCTION(dup2); + INTERCEPT_FUNCTION(ioctl); #if SANITIZER_APPLE INTERCEPT_FUNCTION(OSSpinLockLock); 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 bf6a3a895bd3d..8551424717de6 100644 --- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp @@ -20,7 +20,6 @@ #if SANITIZER_APPLE #include #include -#include #include #endif @@ -38,12 +37,16 @@ #endif #include +#include +#include #include #include #include #include +#include #include #include +#include #include #if _FILE_OFFSET_BITS == 64 && SANITIZER_GLIBC @@ -373,6 +376,54 @@ class RtsanOpenedFileTest : public RtsanFileTest { int fd = -1; }; +TEST(TestRtsanInterceptors, IoctlDiesWhenRealtime) { + auto Func = []() { ioctl(0, FIONREAD); }; + ExpectRealtimeDeath(Func, "ioctl"); + ExpectNonRealtimeSurvival(Func); +} + +TEST_F(RtsanOpenedFileTest, IoctlBehavesWithOutputArg) { + int arg{}; + ioctl(GetOpenFd(), FIONREAD, &arg); + + EXPECT_THAT(arg, Ge(0)); +} + +TEST(TestRtsanInterceptors, IoctlBehavesWithOutputPointer) { + // These initial checks just see if we CAN run these tests. + // If we can't (can't open a socket, or can't find an interface, just + // gracefully skip. + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + perror("socket"); + GTEST_SKIP(); + } + + struct ifaddrs *ifaddr = nullptr; + if (getifaddrs(&ifaddr) == -1 || ifaddr == nullptr) { + perror("getifaddrs"); + close(sock); + GTEST_SKIP(); + } + + struct ifreq ifr {}; + strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1); + + int retval = ioctl(sock, SIOCGIFADDR, &ifr); + if (retval == -1) { + perror("ioctl"); + close(sock); + freeifaddrs(ifaddr); + FAIL(); + } + + freeifaddrs(ifaddr); + close(sock); + + ASSERT_THAT(ifr.ifr_addr.sa_data, NotNull()); + ASSERT_THAT(ifr.ifr_addr.sa_family, Eq(AF_INET)); +} + TEST_F(RtsanOpenedFileTest, LseekDiesWhenRealtime) { auto Func = [this]() { lseek(GetOpenFd(), 0, SEEK_SET); }; ExpectRealtimeDeath(Func, MAYBE_APPEND_64("lseek"));