Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
53 changes: 52 additions & 1 deletion compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#if SANITIZER_APPLE
#include <libkern/OSAtomic.h>
#include <os/lock.h>
#include <sys/types.h>
#include <unistd.h>
#endif

Expand All @@ -38,12 +37,16 @@
#endif

#include <fcntl.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netdb.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>

#if _FILE_OFFSET_BITS == 64 && SANITIZER_GLIBC
Expand Down Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test (and the one above) exists to ensure we pass on the arg correctly. It's just a simple "lights on" test of ioctl getting info from a socket

// 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"));
Expand Down