diff options
Diffstat (limited to 'third_party/tcmalloc')
3 files changed, 193 insertions, 0 deletions
diff --git a/third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc new file mode 100644 index 0000000..a060b54 --- /dev/null +++ b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "linux_shadow_stacks.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static const int kMaxShadowIndex = 2048; +static const char kOverflowMessage[] = "Shadow stack overflow\n"; + +// Thread-local vars. +__thread +int shadow_index = -1; +__thread +void *shadow_ip_stack[kMaxShadowIndex]; +__thread +void *shadow_sp_stack[kMaxShadowIndex]; + +enum Status {UNINITIALIZED = -1, DISABLED, ENABLED}; +Status status = UNINITIALIZED; + +void init() { + if (!getenv("KEEP_SHADOW_STACKS")) { + status = DISABLED; + return; + } + status = ENABLED; +} + +void __cyg_profile_func_enter(void *this_fn, void *call_site) { + if (status == DISABLED) return; + if (status == UNINITIALIZED) { + init(); + if (status == DISABLED) return; + } + shadow_index++; + if (shadow_index > kMaxShadowIndex) { + // Avoid memory allocation when reporting an error. + write(2, kOverflowMessage, sizeof(kOverflowMessage)); + int a = 0; + a = a / a; + } + // Update the shadow IP stack + shadow_ip_stack[shadow_index] = this_fn; + // Update the shadow SP stack. The code for obtaining the frame address was + // borrowed from Google Perftools, http://code.google.com/p/google-perftools/ + // + // Copyright (c) 2005, Google Inc. + // All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are + // met: + // + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // * Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following disclaimer + // in the documentation and/or other materials provided with the + // distribution. + // * Neither the name of Google Inc. nor the names of its + // contributors may be used to endorse or promote products derived from + // this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + void **sp; +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) || __llvm__ + // __builtin_frame_address(0) can return the wrong address on gcc-4.1.0-k8. + // It's always correct on llvm, and the techniques below aren't (in + // particular, llvm-gcc will make a copy of this_fn, so it's not in sp[2]), + // so we also prefer __builtin_frame_address when running under llvm. + sp = reinterpret_cast<void**>(__builtin_frame_address(0)); +#elif defined(__i386__) + // Stack frame format: + // sp[0] pointer to previous frame + // sp[1] caller address + // sp[2] first argument + // ... + // NOTE: This will break under llvm, since result is a copy and not in sp[2] + sp = (void **)&this_fn - 2; +#elif defined(__x86_64__) + unsigned long rbp; + // Move the value of the register %rbp into the local variable rbp. + // We need 'volatile' to prevent this instruction from getting moved + // around during optimization to before function prologue is done. + // An alternative way to achieve this + // would be (before this __asm__ instruction) to call Noop() defined as + // static void Noop() __attribute__ ((noinline)); // prevent inlining + // static void Noop() { asm(""); } // prevent optimizing-away + __asm__ volatile ("mov %%rbp, %0" : "=r" (rbp)); + // Arguments are passed in registers on x86-64, so we can't just + // offset from &result + sp = (void **) rbp; +#else +# error Cannot obtain SP (possibly compiling on a non x86 architecture) +#endif + shadow_sp_stack[shadow_index] = (void*)sp; + return; +} + +void __cyg_profile_func_exit(void *this_fn, void *call_site) { + if (status == DISABLED) return; + shadow_index--; +} + +void *get_shadow_ip_stack(int *index /*OUT*/) { + *index = shadow_index; + return shadow_ip_stack; +} + +void *get_shadow_sp_stack(int *index /*OUT*/) { + *index = shadow_index; + return shadow_sp_stack; +} diff --git a/third_party/tcmalloc/chromium/src/linux_shadow_stacks.h b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.h new file mode 100644 index 0000000..e519d29 --- /dev/null +++ b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.h @@ -0,0 +1,20 @@ +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_TCMALLOC_CHROMIUM_SRC_LINUX_SHADOW_STACKS_H__ +#define THIRD_PARTY_TCMALLOC_CHROMIUM_SRC_LINUX_SHADOW_STACKS_H__ + +#define NO_INSTRUMENT __attribute__((no_instrument_function)) + +extern "C" { +void init() NO_INSTRUMENT; +void __cyg_profile_func_enter(void *this_fn, void *call_site) NO_INSTRUMENT; +void __cyg_profile_func_exit(void *this_fn, void *call_site) NO_INSTRUMENT; +void *get_shadow_ip_stack(int *index /*OUT*/) NO_INSTRUMENT; +void *get_shadow_sp_stack(int *index /*OUT*/) NO_INSTRUMENT; +} + +#undef NO_INSTRUMENT + +#endif // THIRD_PARTY_TCMALLOC_CHROMIUM_SRC_LINUX_SHADOW_STACKS_H__ diff --git a/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h b/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h index a140ab6..0f8c4de 100644 --- a/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h +++ b/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h @@ -65,6 +65,9 @@ typedef ucontext ucontext_t; #endif #include "google/stacktrace.h" +#if defined(KEEP_SHADOW_STACKS) +#include "linux_shadow_stacks.h" +#endif // KEEP_SHADOW_STACKS #if defined(__linux__) && defined(__i386__) && defined(__ELF__) && defined(HAVE_MMAP) // Count "push %reg" instructions in VDSO __kernel_vsyscall(), @@ -316,6 +319,21 @@ int GET_STACK_TRACE_OR_FRAMES { #endif int n = 0; +#if defined(KEEP_SHADOW_STACKS) + void **shadow_ip_stack; + void **shadow_sp_stack; + int stack_size; + shadow_ip_stack = (void**) get_shadow_ip_stack(&stack_size); + shadow_sp_stack = (void**) get_shadow_sp_stack(&stack_size); + int shadow_index = stack_size - 1; + for (int i = stack_size - 1; i >= 0; i--) { + if (sp == shadow_sp_stack[i]) { + shadow_index = i; + break; + } + } + void **prev_sp = NULL; +#endif // KEEP_SHADOW_STACKS while (sp && n < max_depth) { if (*(sp+1) == reinterpret_cast<void *>(0)) { // In 64-bit code, we often see a frame that @@ -328,8 +346,17 @@ int GET_STACK_TRACE_OR_FRAMES { void **next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp); if (skip_count > 0) { skip_count--; +#if defined(KEEP_SHADOW_STACKS) + shadow_index--; +#endif // KEEP_SHADOW_STACKS } else { result[n] = *(sp+1); +#if defined(KEEP_SHADOW_STACKS) + if ((shadow_index > 0) && (sp == shadow_sp_stack[shadow_index])) { + shadow_index--; + } +#endif // KEEP_SHADOW_STACKS + #if IS_STACK_FRAMES if (next_sp > sp) { sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp; @@ -340,7 +367,25 @@ int GET_STACK_TRACE_OR_FRAMES { #endif n++; } +#if defined(KEEP_SHADOW_STACKS) + prev_sp = sp; +#endif // KEEP_SHADOW_STACKS sp = next_sp; } + +#if defined(KEEP_SHADOW_STACKS) + if (shadow_index >= 0) { + for (int i = shadow_index; i >= 0; i--) { + if (shadow_sp_stack[i] > prev_sp) { + result[n] = shadow_ip_stack[i]; + if (n + 1 < max_depth) { + n++; + continue; + } + } + break; + } + } +#endif // KEEP_SHADOW_STACKS return n; } |