[sanitizer_common] Support stripping interceptor prefixes in RenderFrame()
authorMarco Elver <elver@google.com>
Thu, 25 May 2023 09:08:00 +0000 (11:08 +0200)
committerMarco Elver <elver@google.com>
Thu, 25 May 2023 10:01:10 +0000 (12:01 +0200)
Rather than having every tool pass the right interceptor prefix, just
move this logic into RenderFrame().

Note that currently there are a few cases where due to aliasing the
intercepted function -> interceptor, the unwinder sees the intercepted
function - however this is never guaranteed. In a later change this
becomes more apparent, and other non-tsan sanitizer tests would fail as
well. By making the default RenderFrame() strip interceptor prefixes, we
don't rely on the linker aliasing preferences.

Reviewed By: dvyukov, vitalybuka, MaskRay

Differential Revision: https://reviews.llvm.org/D151319

compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp
compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.h
compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cpp
compiler-rt/lib/tsan/rtl/tsan_report.cpp

index 2d0eccc..ce9f24a 100644 (file)
@@ -19,12 +19,23 @@ namespace __sanitizer {
 // sanitizer_symbolizer_markup.cpp implements these differently.
 #if !SANITIZER_SYMBOLIZER_MARKUP
 
-static const char *StripFunctionName(const char *function, const char *prefix) {
-  if (!function) return nullptr;
-  if (!prefix) return function;
-  uptr prefix_len = internal_strlen(prefix);
-  if (0 == internal_strncmp(function, prefix, prefix_len))
-    return function + prefix_len;
+// Strip interceptor prefixes from function name.
+static const char *StripFunctionName(const char *function) {
+  if (!function)
+    return nullptr;
+  auto try_strip = [function](const char *prefix) -> const char * {
+    const uptr prefix_len = internal_strlen(prefix);
+    if (!internal_strncmp(function, prefix, prefix_len))
+      return function + prefix_len;
+    return nullptr;
+  };
+  if (SANITIZER_APPLE) {
+    if (const char *s = try_strip("wrap_"))
+      return s;
+  } else {
+    if (const char *s = try_strip("__interceptor_"))
+      return s;
+  }
   return function;
 }
 
@@ -121,7 +132,7 @@ static const char kDefaultFormat[] = "    #%n %p %F %L";
 
 void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
                  uptr address, const AddressInfo *info, bool vs_style,
-                 const char *strip_path_prefix, const char *strip_func_prefix) {
+                 const char *strip_path_prefix) {
   // info will be null in the case where symbolization is not needed for the
   // given format. This ensures that the code below will get a hard failure
   // rather than print incorrect information in case RenderNeedsSymbolization
@@ -157,8 +168,8 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
       MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer);
       break;
     case 'f':
-      buffer->append("%s", DemangleFunctionName(StripFunctionName(
-                               info->function, strip_func_prefix)));
+      buffer->append("%s",
+                     DemangleFunctionName(StripFunctionName(info->function)));
       break;
     case 'q':
       buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
@@ -178,8 +189,8 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
     case 'F':
       // Function name and offset, if file is unknown.
       if (info->function) {
-        buffer->append("in %s", DemangleFunctionName(StripFunctionName(
-                                    info->function, strip_func_prefix)));
+        buffer->append("in %s",
+                       DemangleFunctionName(StripFunctionName(info->function)));
         if (!info->file && info->function_offset != AddressInfo::kUnknown)
           buffer->append("+0x%zx", info->function_offset);
       }
index 96119b2..457e321 100644 (file)
@@ -26,8 +26,7 @@ namespace __sanitizer {
 // will be turned into
 //   "  frame 10: function foo::bar() at my/file.cc:10"
 // You may additionally pass "strip_path_prefix" to strip prefixes of paths to
-// source files and modules, and "strip_func_prefix" to strip prefixes of
-// function names.
+// source files and modules.
 // Here's the full list of available placeholders:
 //   %% - represents a '%' character;
 //   %n - frame number (copy of frame_no);
@@ -48,8 +47,7 @@ namespace __sanitizer {
 //   %M - prints module basename and offset, if it is known, or PC.
 void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
                  uptr address, const AddressInfo *info, bool vs_style,
-                 const char *strip_path_prefix = "",
-                 const char *strip_func_prefix = "");
+                 const char *strip_path_prefix = "");
 
 bool RenderNeedsSymbolization(const char *format);
 
index ce75f83..62b34cd 100644 (file)
@@ -12,6 +12,7 @@
 #include "sanitizer_common/sanitizer_stacktrace_printer.h"
 
 #include "gtest/gtest.h"
+#include "interception/interception.h"
 
 namespace __sanitizer {
 
@@ -71,7 +72,7 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
   info.address = 0x400000;
   info.module = internal_strdup("/path/to/my/module");
   info.module_offset = 0x200;
-  info.function = internal_strdup("function_foo");
+  info.function = internal_strdup("foo");
   info.function_offset = 0x100;
   info.file = internal_strdup("/path/to/my/source");
   info.line = 10;
@@ -83,11 +84,24 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
               "%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
               "Function:%f FunctionOffset:%q Source:%s Line:%l "
               "Column:%c",
-              frame_no, info.address, &info, false, "/path/to/", "function_");
+              frame_no, info.address, &info, false, "/path/to/");
   EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
                "Function:foo FunctionOffset:0x100 Source:my/source Line:10 "
                "Column:5",
                str.data());
+
+  str.clear();
+  // Check that RenderFrame() strips interceptor prefixes.
+  info.function = internal_strdup(SANITIZER_STRINGIFY(WRAP(bar)));
+  RenderFrame(&str,
+              "%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
+              "Function:%f FunctionOffset:%q Source:%s Line:%l "
+              "Column:%c",
+              frame_no, info.address, &info, false, "/path/to/");
+  EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
+               "Function:bar FunctionOffset:0x100 Source:my/source Line:10 "
+               "Column:5",
+               str.data());
   info.Clear();
   str.clear();
 
index 9b03adc..7c8d125 100644 (file)
@@ -98,12 +98,6 @@ static const char *ReportTypeString(ReportType typ, uptr tag) {
   UNREACHABLE("missing case");
 }
 
-#if SANITIZER_APPLE
-static const char *const kInterposedFunctionPrefix = "wrap_";
-#else
-static const char *const kInterposedFunctionPrefix = "__interceptor_";
-#endif
-
 void PrintStack(const ReportStack *ent) {
   if (ent == 0 || ent->frames == 0) {
     Printf("    [failed to restore the stack]\n\n");
@@ -115,7 +109,7 @@ void PrintStack(const ReportStack *ent) {
     RenderFrame(&res, common_flags()->stack_trace_format, i,
                 frame->info.address, &frame->info,
                 common_flags()->symbolize_vs_style,
-                common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
+                common_flags()->strip_path_prefix);
     Printf("%s\n", res.data());
   }
   Printf("\n");