diff options
Diffstat (limited to 'contrib/llvm-project/compiler-rt/lib/rtsan')
14 files changed, 1632 insertions, 0 deletions
diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan.cpp new file mode 100644 index 000000000000..cf7fbddd9eb9 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan.cpp @@ -0,0 +1,50 @@ +//===--- rtsan.cpp - Realtime Sanitizer -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include <rtsan/rtsan.h> +#include <rtsan/rtsan_context.h> +#include <rtsan/rtsan_interceptors.h> + +using namespace __rtsan; + +bool __rtsan::rtsan_initialized; +bool __rtsan::rtsan_init_is_running; + +extern "C" { + +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() { + CHECK(!rtsan_init_is_running); + if (rtsan_initialized) + return; + rtsan_init_is_running = true; + + InitializeInterceptors(); + + rtsan_init_is_running = false; + rtsan_initialized = true; +} + +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() { + __rtsan::GetContextForThisThread().RealtimePush(); +} + +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() { + __rtsan::GetContextForThisThread().RealtimePop(); +} + +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_off() { + __rtsan::GetContextForThisThread().BypassPush(); +} + +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_on() { + __rtsan::GetContextForThisThread().BypassPop(); +} + +} // extern "C" diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan.h b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan.h new file mode 100644 index 000000000000..ccddaf2c893e --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan.h @@ -0,0 +1,47 @@ +//===--- rtsan.h - Realtime Sanitizer ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "sanitizer_common/sanitizer_internal_defs.h" + +extern "C" { + +namespace __rtsan { + +extern bool rtsan_initialized; +extern bool rtsan_init_is_running; + +} // namespace __rtsan + +// Initialise rtsan interceptors. +// A call to this method is added to the preinit array on Linux systems. +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init(); + +// Enter real-time context. +// When in a real-time context, RTSan interceptors will error if realtime +// violations are detected. Calls to this method are injected at the code +// generation stage when RTSan is enabled. +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter(); + +// Exit the real-time context. +// When not in a real-time context, RTSan interceptors will simply forward +// intercepted method calls to the real methods. +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit(); + +// Disable all RTSan error reporting. +// Injected into the code if "nosanitize(realtime)" is on a function. +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_off(); + +// Re-enable all RTSan error reporting. +// The counterpart to `__rtsan_off`. +SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_on(); + +} // extern "C" diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_context.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_context.cpp new file mode 100644 index 000000000000..f761ddedce67 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_context.cpp @@ -0,0 +1,98 @@ +//===--- rtsan_context.cpp - Realtime Sanitizer -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include <rtsan/rtsan_context.h> + +#include <rtsan/rtsan_stack.h> + +#include <sanitizer_common/sanitizer_allocator_internal.h> +#include <sanitizer_common/sanitizer_stacktrace.h> + +#include <new> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +static pthread_key_t context_key; +static pthread_once_t key_once = PTHREAD_ONCE_INIT; + +// InternalFree cannot be passed directly to pthread_key_create +// because it expects a signature with only one arg +static void InternalFreeWrapper(void *ptr) { __sanitizer::InternalFree(ptr); } + +static __rtsan::Context &GetContextForThisThreadImpl() { + auto make_thread_local_context_key = []() { + CHECK_EQ(pthread_key_create(&context_key, InternalFreeWrapper), 0); + }; + + pthread_once(&key_once, make_thread_local_context_key); + __rtsan::Context *current_thread_context = + static_cast<__rtsan::Context *>(pthread_getspecific(context_key)); + if (current_thread_context == nullptr) { + current_thread_context = static_cast<__rtsan::Context *>( + __sanitizer::InternalAlloc(sizeof(__rtsan::Context))); + new (current_thread_context) __rtsan::Context(); + pthread_setspecific(context_key, current_thread_context); + } + + return *current_thread_context; +} + +/* + This is a placeholder stub for a future feature that will allow + a user to configure RTSan's behaviour when a real-time safety + violation is detected. The RTSan developers intend for the + following choices to be made available, via a RTSAN_OPTIONS + environment variable, in a future PR: + + i) exit, + ii) continue, or + iii) wait for user input from stdin. + + Until then, and to keep the first PRs small, only the exit mode + is available. +*/ +static void InvokeViolationDetectedAction() { exit(EXIT_FAILURE); } + +__rtsan::Context::Context() = default; + +void __rtsan::Context::RealtimePush() { realtime_depth++; } + +void __rtsan::Context::RealtimePop() { realtime_depth--; } + +void __rtsan::Context::BypassPush() { bypass_depth++; } + +void __rtsan::Context::BypassPop() { bypass_depth--; } + +void __rtsan::Context::ExpectNotRealtime( + const char *intercepted_function_name) { + if (InRealtimeContext() && !IsBypassed()) { + BypassPush(); + PrintDiagnostics(intercepted_function_name); + InvokeViolationDetectedAction(); + BypassPop(); + } +} + +bool __rtsan::Context::InRealtimeContext() const { return realtime_depth > 0; } + +bool __rtsan::Context::IsBypassed() const { return bypass_depth > 0; } + +void __rtsan::Context::PrintDiagnostics(const char *intercepted_function_name) { + fprintf(stderr, + "Real-time violation: intercepted call to real-time unsafe function " + "`%s` in real-time context! Stack trace:\n", + intercepted_function_name); + __rtsan::PrintStackTrace(); +} + +__rtsan::Context &__rtsan::GetContextForThisThread() { + return GetContextForThisThreadImpl(); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_context.h b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_context.h new file mode 100644 index 000000000000..515bb8ade1eb --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_context.h @@ -0,0 +1,38 @@ +//===--- rtsan_context.h - Realtime Sanitizer -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace __rtsan { + +class Context { +public: + Context(); + + void RealtimePush(); + void RealtimePop(); + + void BypassPush(); + void BypassPop(); + + void ExpectNotRealtime(const char *intercepted_function_name); + +private: + bool InRealtimeContext() const; + bool IsBypassed() const; + void PrintDiagnostics(const char *intercepted_function_name); + + int realtime_depth{0}; + int bypass_depth{0}; +}; + +Context &GetContextForThisThread(); + +} // namespace __rtsan diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors.cpp new file mode 100644 index 000000000000..b63040446e53 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors.cpp @@ -0,0 +1,442 @@ +//===--- rtsan_interceptors.cpp - Realtime Sanitizer ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "rtsan/rtsan_interceptors.h" + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator_dlsym.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_platform.h" +#include "sanitizer_common/sanitizer_platform_interceptors.h" + +#include "interception/interception.h" +#include "rtsan/rtsan.h" +#include "rtsan/rtsan_context.h" + +#if SANITIZER_APPLE + +#if TARGET_OS_MAC +// On MacOS OSSpinLockLock is deprecated and no longer present in the headers, +// but the symbol still exists on the system. Forward declare here so we +// don't get compilation errors. +#include <stdint.h> +extern "C" { +typedef int32_t OSSpinLock; +void OSSpinLockLock(volatile OSSpinLock *__lock); +} +#endif + +#include <libkern/OSAtomic.h> +#include <os/lock.h> +#endif + +#if SANITIZER_INTERCEPT_MEMALIGN || SANITIZER_INTERCEPT_PVALLOC +#include <malloc.h> +#endif + +#include <fcntl.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/socket.h> +#include <time.h> +#include <unistd.h> + +using namespace __sanitizer; + +using __rtsan::rtsan_init_is_running; +using __rtsan::rtsan_initialized; + +namespace { +struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> { + static bool UseImpl() { return !rtsan_initialized; } +}; +} // namespace + +void ExpectNotRealtime(const char *intercepted_function_name) { + __rtsan::GetContextForThisThread().ExpectNotRealtime( + intercepted_function_name); +} + +// Filesystem + +INTERCEPTOR(int, open, const char *path, int oflag, ...) { + // TODO Establish whether we should intercept here if the flag contains + // O_NONBLOCK + ExpectNotRealtime("open"); + + va_list args; + va_start(args, oflag); + const mode_t mode = va_arg(args, int); + va_end(args); + + const int result = REAL(open)(path, oflag, mode); + return result; +} + +INTERCEPTOR(int, openat, int fd, const char *path, int oflag, ...) { + // TODO Establish whether we should intercept here if the flag contains + // O_NONBLOCK + ExpectNotRealtime("openat"); + + va_list args; + va_start(args, oflag); + mode_t mode = va_arg(args, int); + va_end(args); + + const int result = REAL(openat)(fd, path, oflag, mode); + return result; +} + +INTERCEPTOR(int, creat, const char *path, mode_t mode) { + // TODO Establish whether we should intercept here if the flag contains + // O_NONBLOCK + ExpectNotRealtime("creat"); + const int result = REAL(creat)(path, mode); + return result; +} + +INTERCEPTOR(int, fcntl, int filedes, int cmd, ...) { + ExpectNotRealtime("fcntl"); + + va_list args; + va_start(args, cmd); + + // Following precedent here. The linux source (fcntl.c, do_fcntl) accepts the + // final argument in a variable that will hold the largest of the possible + // argument types (pointers and ints are typical in fcntl) It is then assumed + // that the implementation of fcntl will cast it properly depending on cmd. + // + // This is also similar to what is done in + // sanitizer_common/sanitizer_common_syscalls.inc + const unsigned long arg = va_arg(args, unsigned long); + int result = REAL(fcntl)(filedes, cmd, arg); + + va_end(args); + + return result; +} + +INTERCEPTOR(int, close, int filedes) { + ExpectNotRealtime("close"); + return REAL(close)(filedes); +} + +INTERCEPTOR(FILE *, fopen, const char *path, const char *mode) { + ExpectNotRealtime("fopen"); + return REAL(fopen)(path, mode); +} + +INTERCEPTOR(size_t, fread, void *ptr, size_t size, size_t nitems, + FILE *stream) { + ExpectNotRealtime("fread"); + return REAL(fread)(ptr, size, nitems, stream); +} + +INTERCEPTOR(size_t, fwrite, const void *ptr, size_t size, size_t nitems, + FILE *stream) { + ExpectNotRealtime("fwrite"); + return REAL(fwrite)(ptr, size, nitems, stream); +} + +INTERCEPTOR(int, fclose, FILE *stream) { + ExpectNotRealtime("fclose"); + return REAL(fclose)(stream); +} + +INTERCEPTOR(int, fputs, const char *s, FILE *stream) { + ExpectNotRealtime("fputs"); + return REAL(fputs)(s, stream); +} + +// Streams +INTERCEPTOR(int, puts, const char *s) { + ExpectNotRealtime("puts"); + return REAL(puts)(s); +} + +// Concurrency +#if SANITIZER_APPLE +#pragma clang diagnostic push +// OSSpinLockLock is deprecated, but still in use in libc++ +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) { + ExpectNotRealtime("OSSpinLockLock"); + return REAL(OSSpinLockLock)(lock); +} +#pragma clang diagnostic pop + +INTERCEPTOR(void, os_unfair_lock_lock, os_unfair_lock_t lock) { + ExpectNotRealtime("os_unfair_lock_lock"); + return REAL(os_unfair_lock_lock)(lock); +} +#elif SANITIZER_LINUX +INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *spinlock) { + ExpectNotRealtime("pthread_spin_lock"); + return REAL(pthread_spin_lock)(spinlock); +} +#endif + +INTERCEPTOR(int, pthread_create, pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) { + ExpectNotRealtime("pthread_create"); + return REAL(pthread_create)(thread, attr, start_routine, arg); +} + +INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *mutex) { + ExpectNotRealtime("pthread_mutex_lock"); + return REAL(pthread_mutex_lock)(mutex); +} + +INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *mutex) { + ExpectNotRealtime("pthread_mutex_unlock"); + return REAL(pthread_mutex_unlock)(mutex); +} + +INTERCEPTOR(int, pthread_join, pthread_t thread, void **value_ptr) { + ExpectNotRealtime("pthread_join"); + return REAL(pthread_join)(thread, value_ptr); +} + +INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *cond) { + ExpectNotRealtime("pthread_cond_signal"); + return REAL(pthread_cond_signal)(cond); +} + +INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *cond) { + ExpectNotRealtime("pthread_cond_broadcast"); + return REAL(pthread_cond_broadcast)(cond); +} + +INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *cond, + pthread_mutex_t *mutex) { + ExpectNotRealtime("pthread_cond_wait"); + return REAL(pthread_cond_wait)(cond, mutex); +} + +INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *cond, + pthread_mutex_t *mutex, const timespec *ts) { + ExpectNotRealtime("pthread_cond_timedwait"); + return REAL(pthread_cond_timedwait)(cond, mutex, ts); +} + +INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *lock) { + ExpectNotRealtime("pthread_rwlock_rdlock"); + return REAL(pthread_rwlock_rdlock)(lock); +} + +INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *lock) { + ExpectNotRealtime("pthread_rwlock_unlock"); + return REAL(pthread_rwlock_unlock)(lock); +} + +INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *lock) { + ExpectNotRealtime("pthread_rwlock_wrlock"); + return REAL(pthread_rwlock_wrlock)(lock); +} + +// Sleeping + +INTERCEPTOR(unsigned int, sleep, unsigned int s) { + ExpectNotRealtime("sleep"); + return REAL(sleep)(s); +} + +INTERCEPTOR(int, usleep, useconds_t u) { + ExpectNotRealtime("usleep"); + return REAL(usleep)(u); +} + +INTERCEPTOR(int, nanosleep, const struct timespec *rqtp, + struct timespec *rmtp) { + ExpectNotRealtime("nanosleep"); + return REAL(nanosleep)(rqtp, rmtp); +} + +// Memory + +INTERCEPTOR(void *, calloc, SIZE_T num, SIZE_T size) { + if (DlsymAlloc::Use()) + return DlsymAlloc::Callocate(num, size); + + ExpectNotRealtime("calloc"); + return REAL(calloc)(num, size); +} + +INTERCEPTOR(void, free, void *ptr) { + if (DlsymAlloc::PointerIsMine(ptr)) + return DlsymAlloc::Free(ptr); + + if (ptr != NULL) { + ExpectNotRealtime("free"); + } + return REAL(free)(ptr); +} + +INTERCEPTOR(void *, malloc, SIZE_T size) { + if (DlsymAlloc::Use()) + return DlsymAlloc::Allocate(size); + + ExpectNotRealtime("malloc"); + return REAL(malloc)(size); +} + +INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { + if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr)) + return DlsymAlloc::Realloc(ptr, size); + + ExpectNotRealtime("realloc"); + return REAL(realloc)(ptr, size); +} + +INTERCEPTOR(void *, reallocf, void *ptr, SIZE_T size) { + ExpectNotRealtime("reallocf"); + return REAL(reallocf)(ptr, size); +} + +INTERCEPTOR(void *, valloc, SIZE_T size) { + ExpectNotRealtime("valloc"); + return REAL(valloc)(size); +} + +#if SANITIZER_INTERCEPT_ALIGNED_ALLOC +INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) { + ExpectNotRealtime("aligned_alloc"); + return REAL(aligned_alloc)(alignment, size); +} +#define RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc) +#else +#define RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC +#endif + +INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { + ExpectNotRealtime("posix_memalign"); + return REAL(posix_memalign)(memptr, alignment, size); +} + +#if SANITIZER_INTERCEPT_MEMALIGN +INTERCEPTOR(void *, memalign, size_t alignment, size_t size) { + ExpectNotRealtime("memalign"); + return REAL(memalign)(alignment, size); +} +#endif + +#if SANITIZER_INTERCEPT_PVALLOC +INTERCEPTOR(void *, pvalloc, size_t size) { + ExpectNotRealtime("pvalloc"); + return REAL(pvalloc)(size); +} +#endif + +// Sockets +INTERCEPTOR(int, socket, int domain, int type, int protocol) { + ExpectNotRealtime("socket"); + return REAL(socket)(domain, type, protocol); +} + +INTERCEPTOR(ssize_t, send, int sockfd, const void *buf, size_t len, int flags) { + ExpectNotRealtime("send"); + return REAL(send)(sockfd, buf, len, flags); +} + +INTERCEPTOR(ssize_t, sendmsg, int socket, const struct msghdr *message, + int flags) { + ExpectNotRealtime("sendmsg"); + return REAL(sendmsg)(socket, message, flags); +} + +INTERCEPTOR(ssize_t, sendto, int socket, const void *buffer, size_t length, + int flags, const struct sockaddr *dest_addr, socklen_t dest_len) { + ExpectNotRealtime("sendto"); + return REAL(sendto)(socket, buffer, length, flags, dest_addr, dest_len); +} + +INTERCEPTOR(ssize_t, recv, int socket, void *buffer, size_t length, int flags) { + ExpectNotRealtime("recv"); + return REAL(recv)(socket, buffer, length, flags); +} + +INTERCEPTOR(ssize_t, recvfrom, int socket, void *buffer, size_t length, + int flags, struct sockaddr *address, socklen_t *address_len) { + ExpectNotRealtime("recvfrom"); + return REAL(recvfrom)(socket, buffer, length, flags, address, address_len); +} + +INTERCEPTOR(ssize_t, recvmsg, int socket, struct msghdr *message, int flags) { + ExpectNotRealtime("recvmsg"); + return REAL(recvmsg)(socket, message, flags); +} + +INTERCEPTOR(int, shutdown, int socket, int how) { + ExpectNotRealtime("shutdown"); + return REAL(shutdown)(socket, how); +} + +// Preinit +void __rtsan::InitializeInterceptors() { + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(realloc); + INTERCEPT_FUNCTION(reallocf); + INTERCEPT_FUNCTION(valloc); + RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC; + INTERCEPT_FUNCTION(posix_memalign); +#if SANITIZER_INTERCEPT_MEMALIGN + INTERCEPT_FUNCTION(memalign); +#endif +#if SANITIZER_INTERCEPT_PVALLOC + INTERCEPT_FUNCTION(pvalloc); +#endif + + INTERCEPT_FUNCTION(open); + INTERCEPT_FUNCTION(openat); + INTERCEPT_FUNCTION(close); + INTERCEPT_FUNCTION(fopen); + INTERCEPT_FUNCTION(fread); + INTERCEPT_FUNCTION(fwrite); + INTERCEPT_FUNCTION(fclose); + INTERCEPT_FUNCTION(fcntl); + INTERCEPT_FUNCTION(creat); + INTERCEPT_FUNCTION(puts); + INTERCEPT_FUNCTION(fputs); + +#if SANITIZER_APPLE + INTERCEPT_FUNCTION(OSSpinLockLock); + INTERCEPT_FUNCTION(os_unfair_lock_lock); +#elif SANITIZER_LINUX + INTERCEPT_FUNCTION(pthread_spin_lock); +#endif + + INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_mutex_lock); + INTERCEPT_FUNCTION(pthread_mutex_unlock); + INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(pthread_cond_signal); + INTERCEPT_FUNCTION(pthread_cond_broadcast); + INTERCEPT_FUNCTION(pthread_cond_wait); + INTERCEPT_FUNCTION(pthread_cond_timedwait); + INTERCEPT_FUNCTION(pthread_rwlock_rdlock); + INTERCEPT_FUNCTION(pthread_rwlock_unlock); + INTERCEPT_FUNCTION(pthread_rwlock_wrlock); + + INTERCEPT_FUNCTION(sleep); + INTERCEPT_FUNCTION(usleep); + INTERCEPT_FUNCTION(nanosleep); + + INTERCEPT_FUNCTION(socket); + INTERCEPT_FUNCTION(send); + INTERCEPT_FUNCTION(sendmsg); + INTERCEPT_FUNCTION(sendto); + INTERCEPT_FUNCTION(recv); + INTERCEPT_FUNCTION(recvmsg); + INTERCEPT_FUNCTION(recvfrom); + INTERCEPT_FUNCTION(shutdown); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors.h b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors.h new file mode 100644 index 000000000000..a3dac27544c6 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_interceptors.h @@ -0,0 +1,15 @@ +//===--- rtsan_interceptors.h - Realtime Sanitizer --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace __rtsan { +void InitializeInterceptors(); +} // namespace __rtsan diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_preinit.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_preinit.cpp new file mode 100644 index 000000000000..1307268951fb --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_preinit.cpp @@ -0,0 +1,21 @@ +//===--- rtsan_preinit.cpp - Realtime Sanitizer -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include <rtsan/rtsan.h> + +#if SANITIZER_CAN_USE_PREINIT_ARRAY + +// This section is linked into the main executable when -fsanitize=realtime is +// specified to perform initialization at a very early stage. +__attribute__((section(".preinit_array"), used)) static auto preinit = + __rtsan_init; + +#endif diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_stack.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_stack.cpp new file mode 100644 index 000000000000..43fd5fbf0550 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_stack.cpp @@ -0,0 +1,50 @@ +//===--- rtsan_stack.cpp - Realtime Sanitizer -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "rtsan_stack.h" + +#include <sanitizer_common/sanitizer_flags.h> +#include <sanitizer_common/sanitizer_stacktrace.h> + +using namespace __sanitizer; +using namespace __rtsan; + +// We must define our own implementation of this method for our runtime. +// This one is just copied from UBSan. +namespace __sanitizer { +void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, + bool request_fast, u32 max_depth) { + uptr top = 0; + uptr bottom = 0; + GetThreadStackTopAndBottom(false, &top, &bottom); + bool fast = StackTrace::WillUseFastUnwind(request_fast); + Unwind(max_depth, pc, bp, context, top, bottom, fast); +} +} // namespace __sanitizer + +static void SetGlobalStackTraceFormat() { + SetCommonFlagsDefaults(); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.stack_trace_format = "DEFAULT"; + cf.external_symbolizer_path = GetEnv("RTSAN_SYMBOLIZER_PATH"); + OverrideCommonFlags(cf); +} + +void __rtsan::PrintStackTrace() { + + BufferedStackTrace stack{}; + + GET_CURRENT_PC_BP; + stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); + + SetGlobalStackTraceFormat(); + stack.Print(); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_stack.h b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_stack.h new file mode 100644 index 000000000000..cecdd43045db --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/rtsan_stack.h @@ -0,0 +1,15 @@ +//===--- rtsan_stack.h - Realtime Sanitizer ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace __rtsan { +void PrintStackTrace(); +} // namespace __rtsan diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp new file mode 100644 index 000000000000..b7e73236a14c --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp @@ -0,0 +1,69 @@ +//===--- rtsan_test_context.cpp - Realtime Sanitizer ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "rtsan_test_utilities.h" + +#include "rtsan_context.h" + +TEST(TestRtsanContext, CanCreateContext) { __rtsan::Context context{}; } + +TEST(TestRtsanContext, ExpectNotRealtimeDoesNotDieBeforeRealtimePush) { + __rtsan::Context context{}; + context.ExpectNotRealtime("do_some_stuff"); +} + +TEST(TestRtsanContext, ExpectNotRealtimeDoesNotDieAfterPushAndPop) { + __rtsan::Context context{}; + context.RealtimePush(); + context.RealtimePop(); + context.ExpectNotRealtime("do_some_stuff"); +} + +TEST(TestRtsanContext, ExpectNotRealtimeDiesAfterRealtimePush) { + __rtsan::Context context{}; + + context.RealtimePush(); + EXPECT_DEATH(context.ExpectNotRealtime("do_some_stuff"), ""); +} + +TEST(TestRtsanContext, + ExpectNotRealtimeDiesAfterRealtimeAfterMorePushesThanPops) { + __rtsan::Context context{}; + + context.RealtimePush(); + context.RealtimePush(); + context.RealtimePush(); + context.RealtimePop(); + context.RealtimePop(); + EXPECT_DEATH(context.ExpectNotRealtime("do_some_stuff"), ""); +} + +TEST(TestRtsanContext, ExpectNotRealtimeDoesNotDieAfterBypassPush) { + __rtsan::Context context{}; + + context.RealtimePush(); + context.BypassPush(); + context.ExpectNotRealtime("do_some_stuff"); +} + +TEST(TestRtsanContext, + ExpectNotRealtimeDoesNotDieIfBypassDepthIsGreaterThanZero) { + __rtsan::Context context{}; + + context.RealtimePush(); + context.BypassPush(); + context.BypassPush(); + context.BypassPush(); + context.BypassPop(); + context.BypassPop(); + context.ExpectNotRealtime("do_some_stuff"); + context.BypassPop(); + EXPECT_DEATH(context.ExpectNotRealtime("do_some_stuff"), ""); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp new file mode 100644 index 000000000000..97afb1eefb64 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp @@ -0,0 +1,207 @@ +//===--- rtsan_test.cpp - Realtime Sanitizer --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Introduces basic functional tests for the realtime sanitizer. +// Not meant to be exhaustive, testing all interceptors, please see +// test_rtsan_interceptors.cpp for those tests. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "rtsan_test_utilities.h" +#include <rtsan.h> +#include <sanitizer_common/sanitizer_platform.h> +#include <sanitizer_common/sanitizer_platform_interceptors.h> + +#include <array> +#include <atomic> +#include <chrono> +#include <fstream> +#include <mutex> +#include <shared_mutex> +#include <thread> + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200 +#define SI_MAC_DEPLOYMENT_AT_LEAST_10_12 1 +#else +#define SI_MAC_DEPLOYMENT_AT_LEAST_10_12 0 +#endif + +#define RTSAN_TEST_SHARED_MUTEX (!(SI_MAC) || SI_MAC_DEPLOYMENT_AT_LEAST_10_12) + +using namespace testing; +using namespace rtsan_testing; +using namespace std::chrono_literals; + +TEST(TestRtsan, VectorPushBackAllocationDiesWhenRealtime) { + std::vector<float> vec; + auto Func = [&vec]() { vec.push_back(0.4f); }; + ExpectRealtimeDeath(Func); + ASSERT_EQ(0u, vec.size()); + ExpectNonRealtimeSurvival(Func); + ASSERT_EQ(1u, vec.size()); +} + +TEST(TestRtsan, DestructionOfObjectOnHeapDiesWhenRealtime) { + auto allocated_ptr = std::make_unique<std::array<float, 256>>(); + auto Func = [&allocated_ptr]() { allocated_ptr.reset(); }; + ExpectRealtimeDeath(Func); + ASSERT_NE(nullptr, allocated_ptr.get()); + ExpectNonRealtimeSurvival(Func); + ASSERT_EQ(nullptr, allocated_ptr.get()); +} + +TEST(TestRtsan, SleepingAThreadDiesWhenRealtime) { + auto Func = []() { std::this_thread::sleep_for(1us); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, IfstreamCreationDiesWhenRealtime) { + auto Func = []() { std::ifstream ifs{"./file.txt"}; }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); + std::remove("./file.txt"); +} + +TEST(TestRtsan, OfstreamCreationDiesWhenRealtime) { + auto Func = []() { std::ofstream ofs{"./file.txt"}; }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); + std::remove("./file.txt"); +} + +TEST(TestRtsan, LockingAMutexDiesWhenRealtime) { + std::mutex mutex; + auto Func = [&]() { mutex.lock(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, UnlockingAMutexDiesWhenRealtime) { + std::mutex mutex; + mutex.lock(); + auto Func = [&]() { mutex.unlock(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +#if RTSAN_TEST_SHARED_MUTEX + +TEST(TestRtsan, LockingASharedMutexDiesWhenRealtime) { + std::shared_mutex mutex; + auto Func = [&]() { mutex.lock(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, UnlockingASharedMutexDiesWhenRealtime) { + std::shared_mutex mutex; + mutex.lock(); + auto Func = [&]() { mutex.unlock(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, SharedLockingASharedMutexDiesWhenRealtime) { + std::shared_mutex mutex; + auto Func = [&]() { mutex.lock_shared(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, SharedUnlockingASharedMutexDiesWhenRealtime) { + std::shared_mutex mutex; + mutex.lock_shared(); + auto Func = [&]() { mutex.unlock_shared(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +#endif // RTSAN_TEST_SHARED_MUTEX + +TEST(TestRtsan, LaunchingAThreadDiesWhenRealtime) { + auto Func = [&]() { + std::thread Thread{[]() {}}; + Thread.join(); + }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +namespace { +void InvokeStdFunction(std::function<void()> &&function) { function(); } +} // namespace + +TEST(TestRtsan, CopyingALambdaWithLargeCaptureDiesWhenRealtime) { + std::array<float, 16> lots_of_data; + auto lambda = [lots_of_data]() mutable { + // Stop everything getting optimised out + lots_of_data[3] = 0.25f; + EXPECT_EQ(16, lots_of_data.size()); + EXPECT_EQ(0.25f, lots_of_data[3]); + }; + auto Func = [&]() { InvokeStdFunction(lambda); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, AccessingALargeAtomicVariableDiesWhenRealtime) { + std::atomic<float> small_atomic{0.0f}; + ASSERT_TRUE(small_atomic.is_lock_free()); + RealtimeInvoke([&small_atomic]() { float x = small_atomic.load(); }); + + std::atomic<std::array<float, 2048>> large_atomic; + ASSERT_FALSE(large_atomic.is_lock_free()); + auto Func = [&]() { auto x = large_atomic.load(); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, FirstCoutDiesWhenRealtime) { + auto Func = []() { std::cout << "Hello, world!" << std::endl; }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, SecondCoutDiesWhenRealtime) { + std::cout << "Hello, world"; + auto Func = []() { std::cout << "Hello, again!" << std::endl; }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, PrintfDiesWhenRealtime) { + auto Func = []() { printf("Hello, world!\n"); }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, ThrowingAnExceptionDiesWhenRealtime) { + auto Func = [&]() { + try { + throw std::exception(); + } catch (std::exception &) { + } + }; + ExpectRealtimeDeath(Func); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsan, DoesNotDieIfTurnedOff) { + std::mutex mutex; + auto RealtimeUnsafeFunc = [&]() { + __rtsan_off(); + mutex.lock(); + mutex.unlock(); + __rtsan_on(); + }; + RealtimeInvoke(RealtimeUnsafeFunc); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp new file mode 100644 index 000000000000..f5b016089087 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp @@ -0,0 +1,516 @@ +//===--- rtsan_test_interceptors.cpp - Realtime Sanitizer -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include <sanitizer_common/sanitizer_platform.h> +#include <sanitizer_common/sanitizer_platform_interceptors.h> + +#include "rtsan_test_utilities.h" + +#if SANITIZER_APPLE +#include <libkern/OSAtomic.h> +#include <os/lock.h> +#endif + +#if SANITIZER_INTERCEPT_MEMALIGN || SANITIZER_INTERCEPT_PVALLOC +#include <malloc.h> +#endif + +#include <atomic> +#include <chrono> +#include <string> +#include <thread> + +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <sys/socket.h> + +using namespace testing; +using namespace rtsan_testing; +using namespace std::chrono_literals; + +void *FakeThreadEntryPoint(void *) { return nullptr; } + +class RtsanFileTest : public ::testing::Test { +protected: + void SetUp() override { + const ::testing::TestInfo *const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + file_path_ = std::string("/tmp/rtsan_temporary_test_file_") + + test_info->name() + ".txt"; + RemoveTemporaryFile(); + } + + // Gets a file path with the test's name in it + // This file will be removed if it exists at the end of the test + const char *GetTemporaryFilePath() const { return file_path_.c_str(); } + + void TearDown() override { RemoveTemporaryFile(); } + +private: + void RemoveTemporaryFile() const { std::remove(GetTemporaryFilePath()); } + std::string file_path_; +}; + +/* + Allocation and deallocation +*/ + +TEST(TestRtsanInterceptors, MallocDiesWhenRealtime) { + auto Func = []() { EXPECT_NE(nullptr, malloc(1)); }; + ExpectRealtimeDeath(Func, "malloc"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, ReallocDiesWhenRealtime) { + void *ptr_1 = malloc(1); + auto Func = [ptr_1]() { EXPECT_NE(nullptr, realloc(ptr_1, 8)); }; + ExpectRealtimeDeath(Func, "realloc"); + ExpectNonRealtimeSurvival(Func); +} + +#if SANITIZER_APPLE +TEST(TestRtsanInterceptors, ReallocfDiesWhenRealtime) { + void *ptr_1 = malloc(1); + auto Func = [ptr_1]() { EXPECT_NE(nullptr, reallocf(ptr_1, 8)); }; + ExpectRealtimeDeath(Func, "reallocf"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +TEST(TestRtsanInterceptors, VallocDiesWhenRealtime) { + auto Func = []() { EXPECT_NE(nullptr, valloc(4)); }; + ExpectRealtimeDeath(Func, "valloc"); + ExpectNonRealtimeSurvival(Func); +} + +#if SANITIZER_INTERCEPT_ALIGNED_ALLOC +TEST(TestRtsanInterceptors, AlignedAllocDiesWhenRealtime) { + auto Func = []() { EXPECT_NE(nullptr, aligned_alloc(16, 32)); }; + ExpectRealtimeDeath(Func, "aligned_alloc"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +// free_sized and free_aligned_sized (both C23) are not yet supported +TEST(TestRtsanInterceptors, FreeDiesWhenRealtime) { + void *ptr_1 = malloc(1); + void *ptr_2 = malloc(1); + ExpectRealtimeDeath([ptr_1]() { free(ptr_1); }, "free"); + ExpectNonRealtimeSurvival([ptr_2]() { free(ptr_2); }); + + // Prevent malloc/free pair being optimised out + ASSERT_NE(nullptr, ptr_1); + ASSERT_NE(nullptr, ptr_2); +} + +TEST(TestRtsanInterceptors, FreeSurvivesWhenRealtimeIfArgumentIsNull) { + RealtimeInvoke([]() { free(NULL); }); + ExpectNonRealtimeSurvival([]() { free(NULL); }); +} + +TEST(TestRtsanInterceptors, PosixMemalignDiesWhenRealtime) { + auto Func = []() { + void *ptr; + posix_memalign(&ptr, 4, 4); + }; + ExpectRealtimeDeath(Func, "posix_memalign"); + ExpectNonRealtimeSurvival(Func); +} + +#if SANITIZER_INTERCEPT_MEMALIGN +TEST(TestRtsanInterceptors, MemalignDiesWhenRealtime) { + auto Func = []() { EXPECT_NE(memalign(2, 2048), nullptr); }; + ExpectRealtimeDeath(Func, "memalign"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +#if SANITIZER_INTERCEPT_PVALLOC +TEST(TestRtsanInterceptors, PvallocDiesWhenRealtime) { + auto Func = []() { EXPECT_NE(pvalloc(2048), nullptr); }; + ExpectRealtimeDeath(Func, "pvalloc"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +/* + Sleeping +*/ + +TEST(TestRtsanInterceptors, SleepDiesWhenRealtime) { + auto Func = []() { sleep(0u); }; + ExpectRealtimeDeath(Func, "sleep"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, UsleepDiesWhenRealtime) { + auto Func = []() { usleep(1u); }; + ExpectRealtimeDeath(Func, "usleep"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, NanosleepDiesWhenRealtime) { + auto Func = []() { + timespec T{}; + nanosleep(&T, &T); + }; + ExpectRealtimeDeath(Func, "nanosleep"); + ExpectNonRealtimeSurvival(Func); +} + +/* + Filesystem +*/ + +TEST_F(RtsanFileTest, OpenDiesWhenRealtime) { + auto func = [this]() { open(GetTemporaryFilePath(), O_RDONLY); }; + ExpectRealtimeDeath(func, "open"); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, OpenatDiesWhenRealtime) { + auto func = [this]() { openat(0, GetTemporaryFilePath(), O_RDONLY); }; + ExpectRealtimeDeath(func, "openat"); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, OpenCreatesFileWithProperMode) { + const int mode = S_IRGRP | S_IROTH | S_IRUSR | S_IWUSR; + + const int fd = open(GetTemporaryFilePath(), O_CREAT | O_WRONLY, mode); + ASSERT_THAT(fd, Ne(-1)); + close(fd); + + struct stat st; + ASSERT_THAT(stat(GetTemporaryFilePath(), &st), Eq(0)); + + // Mask st_mode to get permission bits only + ASSERT_THAT(st.st_mode & 0777, Eq(mode)); +} + +TEST_F(RtsanFileTest, CreatDiesWhenRealtime) { + auto func = [this]() { creat(GetTemporaryFilePath(), S_IWOTH | S_IROTH); }; + ExpectRealtimeDeath(func, "creat"); + ExpectNonRealtimeSurvival(func); +} + +TEST(TestRtsanInterceptors, FcntlDiesWhenRealtime) { + auto func = []() { fcntl(0, F_GETFL); }; + ExpectRealtimeDeath(func, "fcntl"); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, FcntlFlockDiesWhenRealtime) { + int fd = creat(GetTemporaryFilePath(), S_IRUSR | S_IWUSR); + ASSERT_THAT(fd, Ne(-1)); + + auto func = [fd]() { + struct flock lock {}; + lock.l_type = F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = ::getpid(); + + ASSERT_THAT(fcntl(fd, F_GETLK, &lock), Eq(0)); + ASSERT_THAT(lock.l_type, F_UNLCK); + }; + ExpectRealtimeDeath(func, "fcntl"); + ExpectNonRealtimeSurvival(func); + + close(fd); +} + +TEST_F(RtsanFileTest, FcntlSetFdDiesWhenRealtime) { + int fd = creat(GetTemporaryFilePath(), S_IRUSR | S_IWUSR); + ASSERT_THAT(fd, Ne(-1)); + + auto func = [fd]() { + int old_flags = fcntl(fd, F_GETFD); + ASSERT_THAT(fcntl(fd, F_SETFD, FD_CLOEXEC), Eq(0)); + + int flags = fcntl(fd, F_GETFD); + ASSERT_THAT(flags, Ne(-1)); + ASSERT_THAT(flags & FD_CLOEXEC, Eq(FD_CLOEXEC)); + + ASSERT_THAT(fcntl(fd, F_SETFD, old_flags), Eq(0)); + ASSERT_THAT(fcntl(fd, F_GETFD), Eq(old_flags)); + }; + + ExpectRealtimeDeath(func, "fcntl"); + ExpectNonRealtimeSurvival(func); + + close(fd); +} + +TEST(TestRtsanInterceptors, CloseDiesWhenRealtime) { + auto func = []() { close(0); }; + ExpectRealtimeDeath(func, "close"); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, FopenDiesWhenRealtime) { + auto func = [this]() { + auto fd = fopen(GetTemporaryFilePath(), "w"); + EXPECT_THAT(fd, Ne(nullptr)); + }; + ExpectRealtimeDeath(func, "fopen"); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, FreadDiesWhenRealtime) { + auto fd = fopen(GetTemporaryFilePath(), "w"); + auto func = [fd]() { + char c{}; + fread(&c, 1, 1, fd); + }; + ExpectRealtimeDeath(func, "fread"); + ExpectNonRealtimeSurvival(func); + if (fd != nullptr) + fclose(fd); +} + +TEST_F(RtsanFileTest, FwriteDiesWhenRealtime) { + auto fd = fopen(GetTemporaryFilePath(), "w"); + ASSERT_NE(nullptr, fd); + auto message = "Hello, world!"; + auto func = [&]() { fwrite(&message, 1, 4, fd); }; + ExpectRealtimeDeath(func, "fwrite"); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, FcloseDiesWhenRealtime) { + auto fd = fopen(GetTemporaryFilePath(), "w"); + EXPECT_THAT(fd, Ne(nullptr)); + auto func = [fd]() { fclose(fd); }; + ExpectRealtimeDeath(func, "fclose"); + ExpectNonRealtimeSurvival(func); +} + +TEST(TestRtsanInterceptors, PutsDiesWhenRealtime) { + auto func = []() { puts("Hello, world!\n"); }; + ExpectRealtimeDeath(func); + ExpectNonRealtimeSurvival(func); +} + +TEST_F(RtsanFileTest, FputsDiesWhenRealtime) { + auto fd = fopen(GetTemporaryFilePath(), "w"); + ASSERT_THAT(fd, Ne(nullptr)) << errno; + auto func = [fd]() { fputs("Hello, world!\n", fd); }; + ExpectRealtimeDeath(func); + ExpectNonRealtimeSurvival(func); + if (fd != nullptr) + fclose(fd); +} + +/* + Concurrency +*/ + +TEST(TestRtsanInterceptors, PthreadCreateDiesWhenRealtime) { + auto Func = []() { + pthread_t thread{}; + const pthread_attr_t attr{}; + struct thread_info *thread_info; + pthread_create(&thread, &attr, &FakeThreadEntryPoint, thread_info); + }; + ExpectRealtimeDeath(Func, "pthread_create"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, PthreadMutexLockDiesWhenRealtime) { + auto Func = []() { + pthread_mutex_t mutex{}; + pthread_mutex_lock(&mutex); + }; + + ExpectRealtimeDeath(Func, "pthread_mutex_lock"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, PthreadMutexUnlockDiesWhenRealtime) { + auto Func = []() { + pthread_mutex_t mutex{}; + pthread_mutex_unlock(&mutex); + }; + + ExpectRealtimeDeath(Func, "pthread_mutex_unlock"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, PthreadMutexJoinDiesWhenRealtime) { + auto Func = []() { + pthread_t thread{}; + pthread_join(thread, nullptr); + }; + + ExpectRealtimeDeath(Func, "pthread_join"); + ExpectNonRealtimeSurvival(Func); +} + +#if SANITIZER_APPLE + +#pragma clang diagnostic push +// OSSpinLockLock is deprecated, but still in use in libc++ +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +TEST(TestRtsanInterceptors, OsSpinLockLockDiesWhenRealtime) { + auto Func = []() { + OSSpinLock spin_lock{}; + OSSpinLockLock(&spin_lock); + }; + ExpectRealtimeDeath(Func, "OSSpinLockLock"); + ExpectNonRealtimeSurvival(Func); +} +#pragma clang diagnostic pop + +TEST(TestRtsanInterceptors, OsUnfairLockLockDiesWhenRealtime) { + auto Func = []() { + os_unfair_lock_s unfair_lock{}; + os_unfair_lock_lock(&unfair_lock); + }; + ExpectRealtimeDeath(Func, "os_unfair_lock_lock"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +#if SANITIZER_LINUX +TEST(TestRtsanInterceptors, SpinLockLockDiesWhenRealtime) { + pthread_spinlock_t spin_lock; + pthread_spin_init(&spin_lock, PTHREAD_PROCESS_SHARED); + auto Func = [&]() { pthread_spin_lock(&spin_lock); }; + ExpectRealtimeDeath(Func, "pthread_spin_lock"); + ExpectNonRealtimeSurvival(Func); +} +#endif + +TEST(TestRtsanInterceptors, PthreadCondSignalDiesWhenRealtime) { + pthread_cond_t cond{}; + pthread_cond_init(&cond, NULL); + + auto Func = [&cond]() { pthread_cond_signal(&cond); }; + ExpectRealtimeDeath(Func, "pthread_cond_signal"); + ExpectNonRealtimeSurvival(Func); + + pthread_cond_destroy(&cond); +} + +TEST(TestRtsanInterceptors, PthreadCondBroadcastDiesWhenRealtime) { + pthread_cond_t cond{}; + pthread_cond_init(&cond, NULL); + + auto Func = [&cond]() { pthread_cond_broadcast(&cond); }; + ExpectRealtimeDeath(Func, "pthread_cond_broadcast"); + ExpectNonRealtimeSurvival(Func); + + pthread_cond_destroy(&cond); +} + +TEST(TestRtsanInterceptors, PthreadCondWaitDiesWhenRealtime) { + pthread_cond_t cond; + pthread_mutex_t mutex; + ASSERT_EQ(0, pthread_cond_init(&cond, nullptr)); + ASSERT_EQ(0, pthread_mutex_init(&mutex, nullptr)); + + auto Func = [&]() { pthread_cond_wait(&cond, &mutex); }; + ExpectRealtimeDeath(Func, "pthread_cond_wait"); + // It's very difficult to test the success case here without doing some + // sleeping, which is at the mercy of the scheduler. What's really important + // here is the interception - so we're only testing that for now. + + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); +} + +TEST(TestRtsanInterceptors, PthreadRwlockRdlockDiesWhenRealtime) { + auto Func = []() { + pthread_rwlock_t rw_lock; + pthread_rwlock_rdlock(&rw_lock); + }; + ExpectRealtimeDeath(Func, "pthread_rwlock_rdlock"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, PthreadRwlockUnlockDiesWhenRealtime) { + auto Func = []() { + pthread_rwlock_t rw_lock; + pthread_rwlock_unlock(&rw_lock); + }; + ExpectRealtimeDeath(Func, "pthread_rwlock_unlock"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, PthreadRwlockWrlockDiesWhenRealtime) { + auto Func = []() { + pthread_rwlock_t rw_lock; + pthread_rwlock_wrlock(&rw_lock); + }; + ExpectRealtimeDeath(Func, "pthread_rwlock_wrlock"); + ExpectNonRealtimeSurvival(Func); +} + +/* + Sockets +*/ +TEST(TestRtsanInterceptors, OpeningASocketDiesWhenRealtime) { + auto Func = []() { socket(PF_INET, SOCK_STREAM, 0); }; + ExpectRealtimeDeath(Func, "socket"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, SendToASocketDiesWhenRealtime) { + auto Func = []() { send(0, nullptr, 0, 0); }; + ExpectRealtimeDeath(Func, "send"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, SendmsgToASocketDiesWhenRealtime) { + msghdr msg{}; + auto Func = [&]() { sendmsg(0, &msg, 0); }; + ExpectRealtimeDeath(Func, "sendmsg"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, SendtoToASocketDiesWhenRealtime) { + sockaddr addr{}; + socklen_t len{}; + auto Func = [&]() { sendto(0, nullptr, 0, 0, &addr, len); }; + ExpectRealtimeDeath(Func, "sendto"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, RecvFromASocketDiesWhenRealtime) { + auto Func = []() { recv(0, nullptr, 0, 0); }; + ExpectRealtimeDeath(Func, "recv"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, RecvfromOnASocketDiesWhenRealtime) { + sockaddr addr{}; + socklen_t len{}; + auto Func = [&]() { recvfrom(0, nullptr, 0, 0, &addr, &len); }; + ExpectRealtimeDeath(Func, "recvfrom"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, RecvmsgOnASocketDiesWhenRealtime) { + msghdr msg{}; + auto Func = [&]() { recvmsg(0, &msg, 0); }; + ExpectRealtimeDeath(Func, "recvmsg"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, ShutdownOnASocketDiesWhenRealtime) { + auto Func = [&]() { shutdown(0, 0); }; + ExpectRealtimeDeath(Func, "shutdown"); + ExpectNonRealtimeSurvival(Func); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp new file mode 100644 index 000000000000..255ac9497103 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp @@ -0,0 +1,17 @@ +//===--- rtsan_test_main.cpp - Realtime Sanitizer ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_test_utils.h" + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h new file mode 100644 index 000000000000..6ca09cf65709 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h @@ -0,0 +1,47 @@ +//===--- rtsan_test_utilities.h - Realtime Sanitizer ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "rtsan.h" +#include "gmock/gmock.h" +#include <string> + +namespace rtsan_testing { + +template <typename Function> void RealtimeInvoke(Function &&Func) { + __rtsan_realtime_enter(); + std::forward<Function>(Func)(); + __rtsan_realtime_exit(); +} + +template <typename Function> +void ExpectRealtimeDeath(Function &&Func, + const char *intercepted_method_name = nullptr) { + + using namespace testing; + + auto GetExpectedErrorSubstring = [&]() -> std::string { + return intercepted_method_name != nullptr + ? "Real-time violation: intercepted call to real-time unsafe " + "function `" + + std::string(intercepted_method_name) + "`" + : ""; + }; + + EXPECT_EXIT(RealtimeInvoke(std::forward<Function>(Func)), + ExitedWithCode(EXIT_FAILURE), GetExpectedErrorSubstring()); +} + +template <typename Function> void ExpectNonRealtimeSurvival(Function &&Func) { + std::forward<Function>(Func)(); +} + +} // namespace rtsan_testing |