Reland "[TSan] Improve handling of stack pointer mangling in {set,long}jmp, pt.8"
authorJulian Lettner <jlettner@apple.com>
Tue, 9 Jul 2019 21:27:37 +0000 (21:27 +0000)
committerJulian Lettner <jlettner@apple.com>
Tue, 9 Jul 2019 21:27:37 +0000 (21:27 +0000)
Fix compilation errors related to `SANITIZER_GO` `#ifdef`s.

Refine longjmp key management.  For Linux, re-implement key retrieval in
C (instead of assembly).  Removal of `InitializeGuardPtr` and a final
round of cleanups will be done in the next commit.

Reviewed By: dvyukov

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

> llvm-svn: 365513

llvm-svn: 365560

compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc

index 71f6f48..b470a46 100644 (file)
@@ -68,12 +68,17 @@ void *__libc_stack_end = 0;
 #endif
 
 #if SANITIZER_LINUX && defined(__aarch64__)
-void InitializeGuardPtr() __attribute__((visibility("hidden")));
+__tsan::uptr InitializeGuardPtr() __attribute__((visibility("hidden")));
 extern "C" __tsan::uptr _tsan_pointer_chk_guard;
 #endif
 
 namespace __tsan {
 
+#if SANITIZER_LINUX && defined(__aarch64__) && !SANITIZER_GO
+static void InitializeLongjmpXorKey();
+static uptr longjmp_xor_key;
+#endif
+
 #ifdef TSAN_RUNTIME_VMA
 // Runtime detected VMA size.
 uptr vmaSize;
@@ -249,7 +254,8 @@ void InitializePlatform() {
   // Go maps shadow memory lazily and works fine with limited address space.
   // Unlimited stack is not a problem as well, because the executable
   // is not compiled with -pie.
-  if (!SANITIZER_GO) {
+#if !SANITIZER_GO
+  {
     bool reexec = false;
     // TSan doesn't play well with unlimited stack size (as stack
     // overlaps with shadow memory). If we detect unlimited stack size,
@@ -285,16 +291,33 @@ void InitializePlatform() {
       reexec = true;
     }
     // Initialize the guard pointer used in {sig}{set,long}jump.
-    InitializeGuardPtr();
+    longjmp_xor_key = InitializeGuardPtr();
+    uptr old_value = longjmp_xor_key;
+    InitializeLongjmpXorKey();
+    CHECK_EQ(longjmp_xor_key, old_value);
+    // If the above check fails for you, please contact me (jlettner@apple.com)
+    // and let me know the values of the two differing keys.  Please also set a
+    // breakpoint on `InitializeGuardPtr` and `InitializeLongjmpXorKey` and tell
+    // me the stack pointer (SP) values that go into the XOR operation (where we
+    // derive the key):
+    //
+    //   InitializeLongjmpXorKey:
+    //     uptr sp = (uptr)__builtin_frame_address(0);
+    //
+    //   InitializeGuardPtr (in tsan_rtl_aarch64.S):
+    //       mov  x0, sp
+    //       ...
+    //       eor  x0, x0, x1
+    //
+    // Then feel free to comment out the call to `InitializeLongjmpXorKey`.
 #endif
     if (reexec)
       ReExec();
   }
 
-#if !SANITIZER_GO
   CheckAndProtect();
   InitTlsSize();
-#endif
+#endif  // !SANITIZER_GO
 }
 
 #if !SANITIZER_GO
@@ -353,9 +376,7 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
 # endif
 #elif defined(__aarch64__)
 # if SANITIZER_LINUX
-  // TODO(yln): fix this
-  // return mangled_sp ^ _tsan_pointer_chk_guard;
-  return mangled_sp;
+  return mangled_sp ^ longjmp_xor_key;
 # else
   return mangled_sp;
 # endif
@@ -394,6 +415,27 @@ uptr ExtractLongJmpSp(uptr *env) {
   return UnmangleLongJmpSp(mangled_sp);
 }
 
+#if SANITIZER_LINUX && defined(__aarch64__)
+#include "interception/interception.h"
+DECLARE_REAL(int, setjmp, void* env);
+// GLIBC mangles the function pointers in jmp_buf (used in {set,long}*jmp
+// functions) by XORing them with a random key.  For AArch64 it is a global
+// variable rather than a TCB one (as for x86_64/powerpc).  We obtain the key by
+// issuing a setjmp and XORing the SP pointer values to derive the key.
+static void InitializeLongjmpXorKey() {
+  // 1. Call REAL(setjmp), which stores the mangled SP in env.
+  jmp_buf env;
+  REAL(setjmp)(env);
+
+  // 2. Retrieve mangled/vanilla SP.
+  uptr mangled_sp = ((uptr *)&env)[LONG_JMP_SP_ENV_SLOT];
+  uptr sp = (uptr)__builtin_frame_address(0);
+
+  // 3. xor SPs to obtain key.
+  longjmp_xor_key = mangled_sp ^ sp;
+}
+#endif
+
 void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
   // Check that the thr object is in tls;
   const uptr thr_beg = (uptr)thr;
@@ -421,7 +463,7 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
   pthread_cleanup_pop(0);
   return res;
 }
-#endif
+#endif  // !SANITIZER_GO
 
 #if !SANITIZER_GO
 void ReplaceSystemMalloc() { }