1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124

base / profiler / stack_copier.cc [blame]

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "base/profiler/stack_copier.h"

#include <vector>

#include "base/bits.h"
#include "base/compiler_specific.h"
#include "base/profiler/stack_buffer.h"

#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
#include "partition_alloc/tagging.h"  // nogncheck
#endif

namespace base {

StackCopier::~StackCopier() = default;

std::unique_ptr<StackBuffer> StackCopier::CloneStack(
    const StackBuffer& stack_buffer,
    uintptr_t* stack_top,
    RegisterContext* thread_context) {
  const uintptr_t original_top = *stack_top;
  const uintptr_t original_bottom =
      reinterpret_cast<uintptr_t>(stack_buffer.buffer());
  size_t stack_size = original_top - original_bottom;
  auto cloned_stack_buffer = std::make_unique<StackBuffer>(stack_size);
  const uint8_t* stack_copy_bottom = CopyStackContentsAndRewritePointers(
      reinterpret_cast<const uint8_t*>(stack_buffer.buffer()),
      reinterpret_cast<const uintptr_t*>(original_top),
      StackBuffer::kPlatformStackAlignment, cloned_stack_buffer->buffer());

  // `stack_buffer` is double pointer aligned by default so we should always
  // get the same result.
  CHECK(stack_copy_bottom ==
        reinterpret_cast<uint8_t*>(cloned_stack_buffer->buffer()));
  *stack_top =
      reinterpret_cast<const uintptr_t>(stack_copy_bottom) + stack_size;

  for (uintptr_t* reg : GetRegistersToRewrite(thread_context)) {
    *reg = RewritePointerIfInOriginalStack(
        reinterpret_cast<const uint8_t*>(original_bottom),
        reinterpret_cast<const uintptr_t*>(original_top), stack_copy_bottom,
        *reg);
  }
  return cloned_stack_buffer;
}

// static
uintptr_t StackCopier::RewritePointerIfInOriginalStack(
    const uint8_t* original_stack_bottom,
    const uintptr_t* original_stack_top,
    const uint8_t* stack_copy_bottom,
    uintptr_t pointer) {
  auto original_stack_bottom_uint =
      reinterpret_cast<uintptr_t>(original_stack_bottom);
  auto original_stack_top_uint =
      reinterpret_cast<uintptr_t>(original_stack_top);
  auto stack_copy_bottom_uint = reinterpret_cast<uintptr_t>(stack_copy_bottom);

  if (pointer < original_stack_bottom_uint ||
      pointer >= original_stack_top_uint)
    return pointer;

  return stack_copy_bottom_uint + (pointer - original_stack_bottom_uint);
}

// static
NO_SANITIZE("address")
const uint8_t* StackCopier::CopyStackContentsAndRewritePointers(
    const uint8_t* original_stack_bottom,
    const uintptr_t* original_stack_top,
    size_t platform_stack_alignment,
    uintptr_t* stack_buffer_bottom) {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
  // Disable MTE during this function because this function indiscriminately
  // reads stack frames, some of which belong to system libraries, not Chrome
  // itself. With stack tagging, some bytes on the stack have MTE tags different
  // from the stack pointer tag.
  partition_alloc::SuspendTagCheckingScope suspend_tag_checking_scope;
#endif

  const uint8_t* byte_src = original_stack_bottom;
  // The first address in the stack with pointer alignment. Pointer-aligned
  // values from this point to the end of the stack are possibly rewritten using
  // RewritePointerIfInOriginalStack(). Bytes before this cannot be a pointer
  // because they occupy less space than a pointer would.
  const uint8_t* first_aligned_address =
      bits::AlignUp(byte_src, sizeof(uintptr_t));

  // The stack copy bottom, which is offset from |stack_buffer_bottom| by the
  // same alignment as in the original stack. This guarantees identical
  // alignment between values in the original stack and the copy. This uses the
  // platform stack alignment rather than pointer alignment so that the stack
  // copy is aligned to platform expectations.
  uint8_t* stack_copy_bottom =
      reinterpret_cast<uint8_t*>(stack_buffer_bottom) +
      (byte_src - bits::AlignDown(byte_src, platform_stack_alignment));
  uint8_t* byte_dst = stack_copy_bottom;

  // Copy bytes verbatim up to the first aligned address.
  for (; byte_src < first_aligned_address; ++byte_src, ++byte_dst)
    *byte_dst = *byte_src;

  // Copy the remaining stack by pointer-sized values, rewriting anything that
  // looks like a pointer into the stack.
  const uintptr_t* src = reinterpret_cast<const uintptr_t*>(byte_src);
  uintptr_t* dst = reinterpret_cast<uintptr_t*>(byte_dst);
  for (; src < original_stack_top; ++src, ++dst) {
    *dst = RewritePointerIfInOriginalStack(
        original_stack_bottom, original_stack_top, stack_copy_bottom, *src);
  }

  return stack_copy_bottom;
}

}  // namespace base