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