[compiler-rt] [rtsan] Support basic call stack suppressions (PR #111608)

Chris Apple via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 8 17:00:25 PDT 2024


https://github.com/cjappl created https://github.com/llvm/llvm-project/pull/111608

This adds basic support for suppressions, which is a first class feature of most (all?) of the other sanitizers. Example docs from [tsan](https://github.com/google/sanitizers/wiki/threadsanitizersuppressions).

# What

This introduces an "anywhere in the call stack" suppression, which is the most basic (and most expensive) kind. For a similar approach, see asan:

https://github.com/llvm/llvm-project/blob/04a8bffdf7b1d6e30616561de1734373375cfef5/compiler-rt/lib/asan/asan_suppressions.cpp#L65-L100


Extra note for @davidtrevelyan  - this just introduces the first version (and least controversial) of the three suppressions we chatted about. I figured we can break this into little chunks where we can discuss the finer points of the other types in more detail. This review introduces a ton of the boring boilerplate to just get it off the ground. 

>From 19b53fe639df77a96173abb0e2a58bdb251ed997 Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
Date: Thu, 26 Sep 2024 10:53:37 -0700
Subject: [PATCH] [rtsan] Support suppressions list

---
 compiler-rt/lib/rtsan/CMakeLists.txt          |  3 +
 compiler-rt/lib/rtsan/rtsan.cpp               |  4 +-
 compiler-rt/lib/rtsan/rtsan_assertions.h      |  4 +
 compiler-rt/lib/rtsan/rtsan_checks.inc        | 19 ++++
 compiler-rt/lib/rtsan/rtsan_flags.inc         |  1 +
 compiler-rt/lib/rtsan/rtsan_suppressions.cpp  | 94 +++++++++++++++++++
 compiler-rt/lib/rtsan/rtsan_suppressions.h    | 22 +++++
 compiler-rt/test/rtsan/stack_suppressions.cpp | 53 +++++++++++
 .../test/rtsan/stack_suppressions.cpp.supp    |  4 +
 9 files changed, 203 insertions(+), 1 deletion(-)
 create mode 100644 compiler-rt/lib/rtsan/rtsan_checks.inc
 create mode 100644 compiler-rt/lib/rtsan/rtsan_suppressions.cpp
 create mode 100644 compiler-rt/lib/rtsan/rtsan_suppressions.h
 create mode 100644 compiler-rt/test/rtsan/stack_suppressions.cpp
 create mode 100644 compiler-rt/test/rtsan/stack_suppressions.cpp.supp

diff --git a/compiler-rt/lib/rtsan/CMakeLists.txt b/compiler-rt/lib/rtsan/CMakeLists.txt
index af34fb63cf53cc..f8dd4d735bc2a3 100644
--- a/compiler-rt/lib/rtsan/CMakeLists.txt
+++ b/compiler-rt/lib/rtsan/CMakeLists.txt
@@ -7,6 +7,7 @@ set(RTSAN_CXX_SOURCES
   rtsan_flags.cpp
   rtsan_interceptors.cpp
   rtsan_stats.cpp
+  rtsan_suppressions.cpp
   )
 
 set(RTSAN_PREINIT_SOURCES
@@ -14,12 +15,14 @@ set(RTSAN_PREINIT_SOURCES
 
 set(RTSAN_HEADERS
   rtsan.h
+  rtsan_checks.inc
   rtsan_assertions.h
   rtsan_context.h
   rtsan_diagnostics.h
   rtsan_flags.h
   rtsan_flags.inc
   rtsan_stats.h
+  rtsan_suppressions.h
   )
 
 set(RTSAN_DEPS)
diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp
index 7691815fd5c1dc..e9f42d3760aa82 100644
--- a/compiler-rt/lib/rtsan/rtsan.cpp
+++ b/compiler-rt/lib/rtsan/rtsan.cpp
@@ -14,12 +14,12 @@
 #include "rtsan/rtsan_flags.h"
 #include "rtsan/rtsan_interceptors.h"
 #include "rtsan/rtsan_stats.h"
+#include "rtsan/rtsan_suppressions.h"
 
 #include "sanitizer_common/sanitizer_atomic.h"
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_mutex.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
 
 using namespace __rtsan;
 using namespace __sanitizer;
@@ -85,6 +85,8 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() {
   InitializeFlags();
   InitializeInterceptors();
 
+  InitializeSuppressions();
+
   if (flags().print_stats_on_exit)
     Atexit(PrintStatisticsSummary);
 
diff --git a/compiler-rt/lib/rtsan/rtsan_assertions.h b/compiler-rt/lib/rtsan/rtsan_assertions.h
index 745cbea0eb3a29..8183a8202478ff 100644
--- a/compiler-rt/lib/rtsan/rtsan_assertions.h
+++ b/compiler-rt/lib/rtsan/rtsan_assertions.h
@@ -15,6 +15,7 @@
 #include "rtsan/rtsan.h"
 #include "rtsan/rtsan_context.h"
 #include "rtsan/rtsan_diagnostics.h"
+#include "rtsan/rtsan_suppressions.h"
 
 #include "sanitizer_common/sanitizer_stacktrace.h"
 
@@ -34,6 +35,9 @@ void ExpectNotRealtime(Context &context, const DiagnosticsInfo &info,
     stack.Unwind(info.pc, info.bp, nullptr,
                  __sanitizer::common_flags()->fast_unwind_on_fatal);
 
+    if (IsStackTraceSuppressed(stack))
+      return;
+
     OnViolation(stack, info);
   }
 }
diff --git a/compiler-rt/lib/rtsan/rtsan_checks.inc b/compiler-rt/lib/rtsan/rtsan_checks.inc
new file mode 100644
index 00000000000000..d0a720c4575d27
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_checks.inc
@@ -0,0 +1,19 @@
+//===-- rtsan_checks.inc ----------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// List of suppression checks handled by RTSan runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef RTSAN_CHECK
+#error "Define RTSAN_CHECK prior to including this file!"
+#endif
+
+// RTSAN_CHECK(Name, SummaryKind)
+// SummaryKind should be a string literal.
+
+RTSAN_CHECK(InCallStack, "in-call-stack")
diff --git a/compiler-rt/lib/rtsan/rtsan_flags.inc b/compiler-rt/lib/rtsan/rtsan_flags.inc
index 1df71127d19d37..5c3eb3f53a5eb4 100644
--- a/compiler-rt/lib/rtsan/rtsan_flags.inc
+++ b/compiler-rt/lib/rtsan/rtsan_flags.inc
@@ -18,3 +18,4 @@
 
 RTSAN_FLAG(bool, halt_on_error, true, "Exit after first reported error.")
 RTSAN_FLAG(bool, print_stats_on_exit, false, "Print stats on exit.")
+RTSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/compiler-rt/lib/rtsan/rtsan_suppressions.cpp b/compiler-rt/lib/rtsan/rtsan_suppressions.cpp
new file mode 100644
index 00000000000000..a1f9c316f98d6e
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_suppressions.cpp
@@ -0,0 +1,94 @@
+//===--- rtsan_suppressions.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of the RTSan runtime, providing support for suppressions
+//
+//===----------------------------------------------------------------------===//
+
+#include "rtsan/rtsan_suppressions.h"
+
+#include "rtsan/rtsan_flags.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+#include <new>
+
+using namespace __sanitizer;
+using namespace __rtsan;
+
+namespace {
+enum class ErrorType {
+#define RTSAN_CHECK(Name, FSanitizeFlagName) Name,
+#include "rtsan_checks.inc"
+#undef RTSAN_CHECK
+};
+} // namespace
+
+alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+
+static const char *kSuppressionTypes[] = {
+#define RTSAN_CHECK(Name, FSanitizeFlagName) FSanitizeFlagName,
+#include "rtsan_checks.inc"
+#undef RTSAN_CHECK
+};
+
+static const char *ConvertTypeToFlagName(ErrorType Type) {
+  switch (Type) {
+#define RTSAN_CHECK(Name, FSanitizeFlagName)                                   \
+  case ErrorType::Name:                                                        \
+    return FSanitizeFlagName;
+#include "rtsan_checks.inc"
+#undef RTSAN_CHECK
+  }
+  UNREACHABLE("unknown ErrorType!");
+}
+
+void __rtsan::InitializeSuppressions() {
+  CHECK_EQ(nullptr, suppression_ctx);
+
+  // We will use suppression_ctx == nullptr as an early out
+  if (flags().suppressions[0] == 0)
+    return;
+
+  suppression_ctx = new (suppression_placeholder)
+      SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+  suppression_ctx->ParseFromFile(flags().suppressions);
+}
+
+bool __rtsan::IsStackTraceSuppressed(const StackTrace &stack) {
+  if (suppression_ctx == nullptr)
+    return false;
+
+  const char *call_stack_flag = ConvertTypeToFlagName(ErrorType::InCallStack);
+  if (!suppression_ctx->HasSuppressionType(call_stack_flag))
+    return false;
+
+  Symbolizer *symbolizer = Symbolizer::GetOrInit();
+  Suppression *s;
+
+  for (uptr i = 0; i < stack.size && stack.trace[i]; i++) {
+    const uptr addr = stack.trace[i];
+
+    SymbolizedStackHolder symbolized_stack(symbolizer->SymbolizePC(addr));
+    const SymbolizedStack *frames = symbolized_stack.get();
+    CHECK(frames);
+    for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
+      const char *function_name = cur->info.function;
+      if (!function_name)
+        continue;
+
+      if (suppression_ctx->Match(function_name, call_stack_flag, &s))
+        return true;
+    }
+  }
+  return false;
+}
diff --git a/compiler-rt/lib/rtsan/rtsan_suppressions.h b/compiler-rt/lib/rtsan/rtsan_suppressions.h
new file mode 100644
index 00000000000000..45545f8c0e0b65
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_suppressions.h
@@ -0,0 +1,22 @@
+//===--- rtsan_suppressions.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of the RTSan runtime, providing support for suppressions
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "sanitizer_common/sanitizer_stacktrace.h"
+
+namespace __rtsan {
+
+void InitializeSuppressions();
+bool IsStackTraceSuppressed(const __sanitizer::StackTrace &stack);
+
+} // namespace __rtsan
diff --git a/compiler-rt/test/rtsan/stack_suppressions.cpp b/compiler-rt/test/rtsan/stack_suppressions.cpp
new file mode 100644
index 00000000000000..2aceedbb313b11
--- /dev/null
+++ b/compiler-rt/test/rtsan/stack_suppressions.cpp
@@ -0,0 +1,53 @@
+// RUN: %clangxx -fsanitize=realtime %s -o %t
+// RUN: %env_rtsan_opts=suppressions='%s.supp' not %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: ios
+
+// Intent: Ensure that suppressions work as intended
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <vector>
+
+void *MallocViolation() { return malloc(10); }
+
+void VectorViolations() {
+  // All of these should be suppressed by *vector*
+  std::vector<int> v(10);
+  v.resize(20);
+  v.clear();
+  v.resize(0);
+  v.push_back(1);
+  v.reserve(10);
+}
+
+void BlockFunc() [[clang::blocking]] { usleep(1); }
+
+void *process() [[clang::nonblocking]] {
+  void *ptr = MallocViolation();
+  VectorViolations();
+  BlockFunc();
+  free(ptr);
+
+  // This is the one that should abort the program
+  // Everything else is suppressed
+  usleep(1);
+
+  return ptr;
+}
+
+int main() {
+  process();
+  return 0;
+}
+
+// CHECK-NOT: failed to open suppressions file
+// CHECK: Intercepted call to real-time unsafe function
+// CHECK-SAME: usleep
+
+// CHECK-NOT: Intercepted call to real-time unsafe function
+// CHECK-NOT: malloc
+// CHECK-NOT: vector
+// CHECK-NOT: free
+// CHECK-NOT: BlockFunc
diff --git a/compiler-rt/test/rtsan/stack_suppressions.cpp.supp b/compiler-rt/test/rtsan/stack_suppressions.cpp.supp
new file mode 100644
index 00000000000000..73e1a935c741d8
--- /dev/null
+++ b/compiler-rt/test/rtsan/stack_suppressions.cpp.supp
@@ -0,0 +1,4 @@
+in-call-stack:MallocViolation
+in-call-stack:std::*vector
+in-call-stack:free
+in-call-stack:BlockFunc



More information about the llvm-commits mailing list