• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/profiler/stack_copier.h"
11 
12 #include <vector>
13 
14 #include "base/bits.h"
15 #include "base/compiler_specific.h"
16 #include "base/profiler/stack_buffer.h"
17 
18 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
19 #include "partition_alloc/tagging.h"  // nogncheck
20 #endif
21 
22 namespace base {
23 
24 StackCopier::~StackCopier() = default;
25 
CloneStack(const StackBuffer & stack_buffer,uintptr_t * stack_top,RegisterContext * thread_context)26 std::unique_ptr<StackBuffer> StackCopier::CloneStack(
27     const StackBuffer& stack_buffer,
28     uintptr_t* stack_top,
29     RegisterContext* thread_context) {
30   const uintptr_t original_top = *stack_top;
31   const uintptr_t original_bottom =
32       reinterpret_cast<uintptr_t>(stack_buffer.buffer());
33   size_t stack_size = original_top - original_bottom;
34   auto cloned_stack_buffer = std::make_unique<StackBuffer>(stack_size);
35   const uint8_t* stack_copy_bottom = CopyStackContentsAndRewritePointers(
36       reinterpret_cast<const uint8_t*>(stack_buffer.buffer()),
37       reinterpret_cast<const uintptr_t*>(original_top),
38       StackBuffer::kPlatformStackAlignment, cloned_stack_buffer->buffer());
39 
40   // `stack_buffer` is double pointer aligned by default so we should always
41   // get the same result.
42   CHECK(stack_copy_bottom ==
43         reinterpret_cast<uint8_t*>(cloned_stack_buffer->buffer()));
44   *stack_top =
45       reinterpret_cast<const uintptr_t>(stack_copy_bottom) + stack_size;
46 
47   for (uintptr_t* reg : GetRegistersToRewrite(thread_context)) {
48     *reg = RewritePointerIfInOriginalStack(
49         reinterpret_cast<const uint8_t*>(original_bottom),
50         reinterpret_cast<const uintptr_t*>(original_top), stack_copy_bottom,
51         *reg);
52   }
53   return cloned_stack_buffer;
54 }
55 
56 // static
RewritePointerIfInOriginalStack(const uint8_t * original_stack_bottom,const uintptr_t * original_stack_top,const uint8_t * stack_copy_bottom,uintptr_t pointer)57 uintptr_t StackCopier::RewritePointerIfInOriginalStack(
58     const uint8_t* original_stack_bottom,
59     const uintptr_t* original_stack_top,
60     const uint8_t* stack_copy_bottom,
61     uintptr_t pointer) {
62   auto original_stack_bottom_uint =
63       reinterpret_cast<uintptr_t>(original_stack_bottom);
64   auto original_stack_top_uint =
65       reinterpret_cast<uintptr_t>(original_stack_top);
66   auto stack_copy_bottom_uint = reinterpret_cast<uintptr_t>(stack_copy_bottom);
67 
68   if (pointer < original_stack_bottom_uint ||
69       pointer >= original_stack_top_uint)
70     return pointer;
71 
72   return stack_copy_bottom_uint + (pointer - original_stack_bottom_uint);
73 }
74 
75 // static
76 NO_SANITIZE("address")
CopyStackContentsAndRewritePointers(const uint8_t * original_stack_bottom,const uintptr_t * original_stack_top,size_t platform_stack_alignment,uintptr_t * stack_buffer_bottom)77 const uint8_t* StackCopier::CopyStackContentsAndRewritePointers(
78     const uint8_t* original_stack_bottom,
79     const uintptr_t* original_stack_top,
80     size_t platform_stack_alignment,
81     uintptr_t* stack_buffer_bottom) {
82 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
83   // Disable MTE during this function because this function indiscriminately
84   // reads stack frames, some of which belong to system libraries, not Chrome
85   // itself. With stack tagging, some bytes on the stack have MTE tags different
86   // from the stack pointer tag.
87   partition_alloc::SuspendTagCheckingScope suspend_tag_checking_scope;
88 #endif
89 
90   const uint8_t* byte_src = original_stack_bottom;
91   // The first address in the stack with pointer alignment. Pointer-aligned
92   // values from this point to the end of the stack are possibly rewritten using
93   // RewritePointerIfInOriginalStack(). Bytes before this cannot be a pointer
94   // because they occupy less space than a pointer would.
95   const uint8_t* first_aligned_address =
96       bits::AlignUp(byte_src, sizeof(uintptr_t));
97 
98   // The stack copy bottom, which is offset from |stack_buffer_bottom| by the
99   // same alignment as in the original stack. This guarantees identical
100   // alignment between values in the original stack and the copy. This uses the
101   // platform stack alignment rather than pointer alignment so that the stack
102   // copy is aligned to platform expectations.
103   uint8_t* stack_copy_bottom =
104       reinterpret_cast<uint8_t*>(stack_buffer_bottom) +
105       (byte_src - bits::AlignDown(byte_src, platform_stack_alignment));
106   uint8_t* byte_dst = stack_copy_bottom;
107 
108   // Copy bytes verbatim up to the first aligned address.
109   for (; byte_src < first_aligned_address; ++byte_src, ++byte_dst)
110     *byte_dst = *byte_src;
111 
112   // Copy the remaining stack by pointer-sized values, rewriting anything that
113   // looks like a pointer into the stack.
114   const uintptr_t* src = reinterpret_cast<const uintptr_t*>(byte_src);
115   uintptr_t* dst = reinterpret_cast<uintptr_t*>(byte_dst);
116   for (; src < original_stack_top; ++src, ++dst) {
117     *dst = RewritePointerIfInOriginalStack(
118         original_stack_bottom, original_stack_top, stack_copy_bottom, *src);
119   }
120 
121   return stack_copy_bottom;
122 }
123 
124 }  // namespace base
125