• 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 <cstring>
13 #include <iterator>
14 #include <memory>
15 #include <numeric>
16 
17 #include "base/profiler/register_context.h"
18 #include "base/profiler/stack_buffer.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 
22 namespace base {
23 
24 namespace {
25 
26 class CopyFunctions : public StackCopier {
27  public:
28   using StackCopier::CopyStackContentsAndRewritePointers;
29   using StackCopier::RewritePointerIfInOriginalStack;
30 
GetRegistersToRewrite(RegisterContext * thread_context)31   std::vector<uintptr_t*> GetRegistersToRewrite(
32       RegisterContext* thread_context) override {
33     return {&RegisterContextStackPointer(thread_context)};
34   }
35 
CopyStack(StackBuffer * stack_buffer,uintptr_t * stack_top,TimeTicks * timestamp,RegisterContext * thread_context,Delegate * delegate)36   bool CopyStack(StackBuffer* stack_buffer,
37                  uintptr_t* stack_top,
38                  TimeTicks* timestamp,
39                  RegisterContext* thread_context,
40                  Delegate* delegate) override {
41     return false;
42   }
43 };
44 
45 static constexpr size_t kTestStackBufferSize = sizeof(uintptr_t) * 4;
46 
47 union alignas(StackBuffer::kPlatformStackAlignment) TestStackBuffer {
48   uintptr_t as_uintptr[kTestStackBufferSize / sizeof(uintptr_t)];
49   uint16_t as_uint16[kTestStackBufferSize / sizeof(uint16_t)];
50   uint8_t as_uint8[kTestStackBufferSize / sizeof(uint8_t)];
51 };
52 
53 }  // namespace
54 
TEST(StackCopierTest,RewritePointerIfInOriginalStack_InStack)55 TEST(StackCopierTest, RewritePointerIfInOriginalStack_InStack) {
56   uintptr_t original_stack[4];
57   uintptr_t stack_copy[4];
58   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy[2]),
59             CopyFunctions::RewritePointerIfInOriginalStack(
60                 reinterpret_cast<uint8_t*>(&original_stack[0]),
61                 &original_stack[0] + std::size(original_stack),
62                 reinterpret_cast<uint8_t*>(&stack_copy[0]),
63                 reinterpret_cast<uintptr_t>(&original_stack[2])));
64 }
65 
TEST(StackCopierTest,RewritePointerIfInOriginalStack_NotInStack)66 TEST(StackCopierTest, RewritePointerIfInOriginalStack_NotInStack) {
67   // We use this variable only for its address, which is outside of
68   // original_stack.
69   uintptr_t non_stack_location;
70   uintptr_t original_stack[4];
71   uintptr_t stack_copy[4];
72 
73   EXPECT_EQ(reinterpret_cast<uintptr_t>(&non_stack_location),
74             CopyFunctions::RewritePointerIfInOriginalStack(
75                 reinterpret_cast<uint8_t*>(&original_stack[0]),
76                 &original_stack[0] + std::size(original_stack),
77                 reinterpret_cast<uint8_t*>(&stack_copy[0]),
78                 reinterpret_cast<uintptr_t>(&non_stack_location)));
79 }
80 
TEST(StackCopierTest,StackCopy)81 TEST(StackCopierTest, StackCopy) {
82   TestStackBuffer original_stack;
83   // Fill the stack buffer with increasing uintptr_t values.
84   std::iota(
85       &original_stack.as_uintptr[0],
86       &original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
87       100);
88   // Replace the third value with an address within the buffer.
89   original_stack.as_uintptr[2] =
90       reinterpret_cast<uintptr_t>(&original_stack.as_uintptr[1]);
91   TestStackBuffer stack_copy;
92 
93   CopyFunctions::CopyStackContentsAndRewritePointers(
94       &original_stack.as_uint8[0],
95       &original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
96       StackBuffer::kPlatformStackAlignment, &stack_copy.as_uintptr[0]);
97 
98   EXPECT_EQ(original_stack.as_uintptr[0], stack_copy.as_uintptr[0]);
99   EXPECT_EQ(original_stack.as_uintptr[1], stack_copy.as_uintptr[1]);
100   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy.as_uintptr[1]),
101             stack_copy.as_uintptr[2]);
102   EXPECT_EQ(original_stack.as_uintptr[3], stack_copy.as_uintptr[3]);
103 }
104 
TEST(StackCopierTest,StackCopy_NonAlignedStackPointerCopy)105 TEST(StackCopierTest, StackCopy_NonAlignedStackPointerCopy) {
106   TestStackBuffer stack_buffer;
107 
108   // Fill the stack buffer with increasing uint16_t values.
109   std::iota(&stack_buffer.as_uint16[0],
110             &stack_buffer.as_uint16[0] + std::size(stack_buffer.as_uint16),
111             100);
112 
113   // Set the stack bottom to the unaligned location one uint16_t into the
114   // buffer.
115   uint8_t* unaligned_stack_bottom =
116       reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
117 
118   // Leave extra space within the stack buffer beyond the end of the stack, but
119   // preserve the platform alignment.
120   const size_t extra_space = StackBuffer::kPlatformStackAlignment;
121   uintptr_t* stack_top =
122       &stack_buffer.as_uintptr[std::size(stack_buffer.as_uintptr) -
123                                extra_space / sizeof(uintptr_t)];
124 
125   // Initialize the copy to all zeros.
126   TestStackBuffer stack_copy_buffer = {{0}};
127 
128   const uint8_t* stack_copy_bottom =
129       CopyFunctions::CopyStackContentsAndRewritePointers(
130           unaligned_stack_bottom, stack_top,
131           StackBuffer::kPlatformStackAlignment,
132           &stack_copy_buffer.as_uintptr[0]);
133 
134   // The stack copy bottom address is expected to be at the same offset into the
135   // stack copy buffer as the unaligned stack bottom is from the stack buffer.
136   // Since the buffers have the same platform stack alignment this also ensures
137   // the alignment of the bottom addresses is the same.
138   EXPECT_EQ(unaligned_stack_bottom - &stack_buffer.as_uint8[0],
139             stack_copy_bottom - &stack_copy_buffer.as_uint8[0]);
140 
141   // The first value in the copy should not be overwritten since the stack
142   // starts at the second uint16_t.
143   EXPECT_EQ(0u, stack_copy_buffer.as_uint16[0]);
144 
145   // The next values up to the extra space should have been copied.
146   const size_t max_index =
147       std::size(stack_copy_buffer.as_uint16) - extra_space / sizeof(uint16_t);
148   for (size_t i = 1; i < max_index; ++i)
149     EXPECT_EQ(i + 100, stack_copy_buffer.as_uint16[i]);
150 
151   // None of the values in the empty space should have been copied.
152   for (size_t i = max_index; i < std::size(stack_copy_buffer.as_uint16); ++i)
153     EXPECT_EQ(0u, stack_copy_buffer.as_uint16[i]);
154 }
155 
156 // Checks that an unaligned within-stack pointer value at the start of the stack
157 // is not rewritten.
TEST(StackCopierTest,StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart)158 TEST(StackCopierTest, StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart) {
159   // Initially fill the buffer with 0s.
160   TestStackBuffer stack_buffer = {{0}};
161 
162   // Set the stack bottom to the unaligned location one uint16_t into the
163   // buffer.
164   uint8_t* unaligned_stack_bottom =
165       reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
166 
167   // Set the first unaligned pointer-sized value to an address within the stack.
168   uintptr_t within_stack_pointer =
169       reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
170   std::memcpy(unaligned_stack_bottom, &within_stack_pointer,
171               sizeof(within_stack_pointer));
172 
173   TestStackBuffer stack_copy_buffer = {{0}};
174 
175   const uint8_t* stack_copy_bottom =
176       CopyFunctions::CopyStackContentsAndRewritePointers(
177           unaligned_stack_bottom,
178           &stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
179           StackBuffer::kPlatformStackAlignment,
180           &stack_copy_buffer.as_uintptr[0]);
181 
182   uintptr_t copied_within_stack_pointer;
183   std::memcpy(&copied_within_stack_pointer, stack_copy_bottom,
184               sizeof(copied_within_stack_pointer));
185 
186   // The rewriting should only operate on pointer-aligned values so the
187   // unaligned value should be copied verbatim.
188   EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
189 }
190 
191 // Checks that an unaligned within-stack pointer after the start of the stack is
192 // not rewritten.
TEST(StackCopierTest,StackCopy_NonAlignedStackPointerUnalignedRewriteAfterStart)193 TEST(StackCopierTest,
194      StackCopy_NonAlignedStackPointerUnalignedRewriteAfterStart) {
195   // Initially fill the buffer with 0s.
196   TestStackBuffer stack_buffer = {{0}};
197 
198   // Set the stack bottom to the unaligned location one uint16_t into the
199   // buffer.
200   uint8_t* unaligned_stack_bottom =
201       reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
202 
203   // Set the second unaligned pointer-sized value to an address within the
204   // stack.
205   uintptr_t within_stack_pointer =
206       reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
207   std::memcpy(unaligned_stack_bottom + sizeof(uintptr_t), &within_stack_pointer,
208               sizeof(within_stack_pointer));
209 
210   TestStackBuffer stack_copy_buffer = {{0}};
211 
212   const uint8_t* stack_copy_bottom =
213       CopyFunctions::CopyStackContentsAndRewritePointers(
214           unaligned_stack_bottom,
215           &stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
216           StackBuffer::kPlatformStackAlignment,
217           &stack_copy_buffer.as_uintptr[0]);
218 
219   uintptr_t copied_within_stack_pointer;
220   std::memcpy(&copied_within_stack_pointer,
221               stack_copy_bottom + sizeof(uintptr_t),
222               sizeof(copied_within_stack_pointer));
223 
224   // The rewriting should only operate on pointer-aligned values so the
225   // unaligned value should be copied verbatim.
226   EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
227 }
228 
TEST(StackCopierTest,StackCopy_NonAlignedStackPointerAlignedRewrite)229 TEST(StackCopierTest, StackCopy_NonAlignedStackPointerAlignedRewrite) {
230   // Initially fill the buffer with 0s.
231   TestStackBuffer stack_buffer = {{0}};
232 
233   // Set the stack bottom to the unaligned location one uint16_t into the
234   // buffer.
235   uint8_t* unaligned_stack_bottom =
236       reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
237 
238   // Set the second aligned pointer-sized value to an address within the stack.
239   stack_buffer.as_uintptr[1] =
240       reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
241 
242   TestStackBuffer stack_copy_buffer = {{0}};
243 
244   CopyFunctions::CopyStackContentsAndRewritePointers(
245       unaligned_stack_bottom,
246       &stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
247       StackBuffer::kPlatformStackAlignment, &stack_copy_buffer.as_uintptr[0]);
248 
249   // The aligned pointer should have been rewritten to point within the stack
250   // copy.
251   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy_buffer.as_uintptr[2]),
252             stack_copy_buffer.as_uintptr[1]);
253 }
254 
TEST(StackCopierTest,CloneStack)255 TEST(StackCopierTest, CloneStack) {
256   StackBuffer original_stack(kTestStackBufferSize);
257   // Fill the stack buffer with increasing uintptr_t values.
258   std::iota(
259       &original_stack.buffer()[0],
260       &original_stack.buffer()[0] + (original_stack.size() / sizeof(uintptr_t)),
261       100);
262   // Replace the third value with an address within the buffer.
263   original_stack.buffer()[2] =
264       reinterpret_cast<uintptr_t>(&original_stack.buffer()[1]);
265 
266   uintptr_t stack_top = reinterpret_cast<uintptr_t>(original_stack.buffer()) +
267                         original_stack.size();
268   CopyFunctions copy_functions;
269   RegisterContext thread_context;
270   RegisterContextStackPointer(&thread_context) =
271       reinterpret_cast<uintptr_t>(original_stack.buffer());
272   std::unique_ptr<StackBuffer> cloned_stack =
273       copy_functions.CloneStack(original_stack, &stack_top, &thread_context);
274 
275   EXPECT_EQ(original_stack.buffer()[0], cloned_stack->buffer()[0]);
276   EXPECT_EQ(original_stack.buffer()[1], cloned_stack->buffer()[1]);
277   EXPECT_EQ(reinterpret_cast<uintptr_t>(&cloned_stack->buffer()[1]),
278             cloned_stack->buffer()[2]);
279   EXPECT_EQ(original_stack.buffer()[3], cloned_stack->buffer()[3]);
280   uintptr_t expected_stack_top =
281       reinterpret_cast<uintptr_t>(cloned_stack->buffer()) +
282       original_stack.size();
283   EXPECT_EQ(RegisterContextStackPointer(&thread_context),
284             reinterpret_cast<uintptr_t>(cloned_stack->buffer()));
285   EXPECT_EQ(stack_top, expected_stack_top);
286 }
287 
288 }  // namespace base
289