[PATCH] D60981: [TSan] Improve handling of stack pointer mangling in {set,long}jmp, pt.1

Julian Lettner via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 22 14:47:48 PDT 2019


yln created this revision.
Herald added subscribers: llvm-commits, Sanitizers, kristof.beyls, krytarowski, javed.absar, kubamracek.
Herald added projects: Sanitizers, LLVM.

TSan needs to infer which calls to setjmp/longjmp are corresponding
pairs. My understanding is, that we can't simply use the jmp_buf
address, since this buffer is just a plain data structure storing the
environment (registers) with no additional semantics, i.e., it can be
copied around and is still expected to work. So we use the stack pointer
(SP) instead.

The setjmp interceptor stores some metadata, which is then consumed in
the corresponding call to longjmp. We use the SP as an "index" (stable
identifier) into the metadata table. So far so good.

However, when mangling is used, the setjmp interceptor observes the
UNmangled SP, but the longjmp interceptor only knows the mangled value
for SP. To still correlate corresponding pairs of calls, TSan currently
derives the mangled representation in setjmp and uses it as the stable
identifer, so that longjmp can do it's lookup.

Currently, this works since "mangling" simply means XOR with a secret
value. However, in the future we want to use operations that do not
allow us to easily go from unmangled -> mangled (pointer
authentication). Going from mangled -> unmangled should still be
possible (for pointer authentication it means zeroing a few bits).

This patch is part 1 of changing set/longjmp interceptors to use the
unmangled SP for metadata lookup. Instead of deriving the mangled SP in
setjmp, we will derive the unmangled SP in longjmp. Since this change
involves difficult-to-test code, it will be done in (at least) 2 parts:
This patch only replicates the existing behavior and checks that the
newly computed value for SP matches with what we have been doing so far.
This should help me to fix issues on architectures I cannot test
directly. I tested this patch on x86-64 (Linux/Darwin) and arm64
(Darwin).

This patch will also address an orthogonal issue: there is a lot of code
duplication in the assembly files, because the
`void __tsan_setjmp(uptr sp, uptr mangled_sp)` already demands the
mangled SP. This means that the code for computing the mangled SP is
duplicated at every call site (in assembly).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D60981

Files:
  compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
  compiler-rt/lib/tsan/rtl/tsan_platform.h
  compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc
  compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc


Index: compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc
===================================================================
--- compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc
+++ compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc
@@ -260,6 +260,10 @@
   }
 }
 
+uptr UnmangleLongJmpSp(uptr mangled_sp) {
+  return mangled_sp ^ __tsan_darwin_setjmp_xor_key;
+}
+
 #if !SANITIZER_GO
 void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
   // The pointer to the ThreadState object is stored in the shadow memory
Index: compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc
===================================================================
--- compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc
+++ compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc
@@ -69,6 +69,7 @@
 
 #if SANITIZER_LINUX && defined(__aarch64__)
 void InitializeGuardPtr() __attribute__((visibility("hidden")));
+extern "C" uptr _tsan_pointer_chk_guard;
 #endif
 
 namespace __tsan {
@@ -334,6 +335,38 @@
   return res;
 }
 
+// Reverse operation of libc stack pointer mangling
+uptr UnmangleLongJmpSp(uptr mangled_sp) {
+#if defined(__x86_64__)
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD
+  return mangled_sp;
+#else  // Linux
+  // Reverse of:
+  //   xor  %fs:0x30, %rsi
+  //   rol  $0x11, %rsi
+  uptr sp;
+  asm("ror  $0x11,     %0 \n"
+      "xor  %%fs:0x30, %0 \n"
+      : "=r" (sp)
+      : "0" (mangled_sp));
+  return sp;
+#endif
+#elif defined(__aarch64__)
+  return mangled_sp ^ _tsan_pointer_chk_guard;
+#elif defined(__powerpc64__)
+  // Reverse of:
+  //  ld   r4, -28696(r13)
+  //  xor  r4, r3, r4
+  uptr xor_guard;
+  asm("ld  %0, -28696(%%r13) \n" : "=r" (xor_guard));
+  return mangled_sp ^ xor_guard;
+#elif defined(__mips__)
+  return mangled_sp;
+#else
+  #error "Unknown platform"
+#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;
Index: compiler-rt/lib/tsan/rtl/tsan_platform.h
===================================================================
--- compiler-rt/lib/tsan/rtl/tsan_platform.h
+++ compiler-rt/lib/tsan/rtl/tsan_platform.h
@@ -1011,6 +1011,7 @@
 void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
 int ExtractResolvFDs(void *state, int *fds, int nfd);
 int ExtractRecvmsgFDs(void *msg, int *fds, int nfd);
+uptr UnmangleLongJmpSp(uptr mangled_sp);
 void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size);
 
 int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
Index: compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
===================================================================
--- compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
+++ compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
@@ -528,10 +528,13 @@
   uptr mangled_sp = env[6];
 # endif
 #endif
+  uptr sp = UnmangleLongJmpSp(mangled_sp);
   // Find the saved buf by mangled_sp.
   for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
     JmpBuf *buf = &thr->jmp_bufs[i];
     if (buf->mangled_sp == mangled_sp) {
+      CHECK_EQ(buf->sp, sp);
+      // TODO(yln): Lookup via sp, remove mangled_sp from struct.
       CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos);
       // Unwind the stack.
       while (thr->shadow_stack_pos > buf->shadow_stack_pos)


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D60981.196136.patch
Type: text/x-patch
Size: 3286 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20190422/341f53c4/attachment.bin>


More information about the llvm-commits mailing list