1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include <Windows.h> // NOLINT
16 : #include <DbgHelp.h>
17 :
18 : #include <memory>
19 : #include <string>
20 : #include <vector>
21 :
22 : #include "base/debug/alias.h"
23 : #include "base/files/file_path.h"
24 : #include "base/threading/platform_thread.h"
25 : #include "base/win/scoped_com_initializer.h"
26 : #include "gtest/gtest.h"
27 : #include "syzygy/common/com_utils.h"
28 : #include "syzygy/common/unittest_util.h"
29 : #include "syzygy/minidump/minidump.h"
30 : #include "syzygy/pe/unittest_util.h"
31 : #include "syzygy/refinery/unittest_util.h"
32 : #include "syzygy/refinery/analyzers/analysis_runner.h"
33 : #include "syzygy/refinery/analyzers/analyzer_util.h"
34 : #include "syzygy/refinery/analyzers/exception_analyzer.h"
35 : #include "syzygy/refinery/analyzers/memory_analyzer.h"
36 : #include "syzygy/refinery/analyzers/module_analyzer.h"
37 : #include "syzygy/refinery/analyzers/stack_analyzer.h"
38 : #include "syzygy/refinery/analyzers/stack_frame_analyzer.h"
39 : #include "syzygy/refinery/analyzers/thread_analyzer.h"
40 : #include "syzygy/refinery/process_state/process_state.h"
41 : #include "syzygy/refinery/process_state/process_state_util.h"
42 : #include "syzygy/refinery/process_state/refinery.pb.h"
43 : #include "syzygy/refinery/symbols/dia_symbol_provider.h"
44 :
45 m : namespace refinery {
46 :
47 m : namespace {
48 :
49 m : struct SimpleUDT {
50 m : int one;
51 m : const char two;
52 m : };
53 :
54 m : __declspec(noinline) DWORD GetEip() {
55 m : return reinterpret_cast<DWORD>(_ReturnAddress());
56 m : }
57 :
58 m : } // namespace
59 :
60 m : class StackAndFrameAnalyzersTest : public testing::Test {
61 m : protected:
62 m : void SetUp() override {
63 : // Override NT symbol path.
64 m : ASSERT_TRUE(scoped_symbol_path_.Setup());
65 :
66 m : symbol_provider_ = new SymbolProvider();
67 :
68 m : expected_esp_ = 0U;
69 m : eip_lowerbound_ = 0U;
70 m : eip_upperbound_ = 0U;
71 :
72 m : expected_param_address_ = 0ULL;
73 m : expected_udt_address_ = 0ULL;
74 m : expected_udt_ptr_address_ = 0ULL;
75 m : }
76 :
77 m : base::FilePath minidump_path() { return scoped_minidump_.minidump_path(); }
78 m : uint32_t expected_esp() { return expected_esp_; }
79 m : uint32_t eip_lowerbound() { return eip_lowerbound_; }
80 m : uint32_t eip_upperbound() { return eip_upperbound_; }
81 m : Address expected_param_address() { return expected_param_address_; }
82 m : Address expected_udt_address() { return expected_udt_address_; }
83 m : Address expected_udt_ptr_address() { return expected_udt_ptr_address_; }
84 :
85 m : bool SetupStackFrameAndGenerateMinidump(int dummy_param) {
86 m : bool success = true;
87 :
88 : // Create some local variables to validate analysis.
89 m : SimpleUDT udt_local = {42, 'a'};
90 m : base::debug::Alias(&udt_local);
91 m : SimpleUDT* udt_ptr_local = &udt_local;
92 m : base::debug::Alias(&udt_ptr_local);
93 :
94 : // Copy esp to expected_esp_. Note: esp must not be changed prior to calling
95 : // GenerateMinidump.
96 m : __asm {
97 m : mov ebx, this
98 m : mov [ebx].expected_esp_, esp
99 m : }
100 :
101 m : eip_lowerbound_ = GetEip();
102 :
103 : // Note: GenerateMinidump takes one parameter. This means when the frame
104 : // is walked, its top should equal the captured esp less the size of that
105 : // argument.
106 m : expected_esp_ -= sizeof(testing::ScopedMinidump::kMinidumpWithStacks);
107 m : success = scoped_minidump_.GenerateMinidump(
108 m : testing::ScopedMinidump::kMinidumpWithStacks);
109 :
110 m : eip_upperbound_ = GetEip();
111 :
112 m : expected_param_address_ = reinterpret_cast<Address>(&dummy_param);
113 m : expected_udt_address_ = reinterpret_cast<Address>(&udt_local);
114 m : expected_udt_ptr_address_ = reinterpret_cast<Address>(&udt_ptr_local);
115 :
116 m : return success;
117 m : }
118 :
119 m : bool AnalyzeMinidump(ProcessState* process_state) {
120 m : minidump::FileMinidump minidump;
121 m : if (!minidump.Open(minidump_path()))
122 m : return false;
123 :
124 m : AnalysisRunner runner;
125 m : std::unique_ptr<Analyzer> analyzer(new refinery::MemoryAnalyzer());
126 m : runner.AddAnalyzer(std::move(analyzer));
127 m : analyzer.reset(new refinery::ThreadAnalyzer());
128 m : runner.AddAnalyzer(std::move(analyzer));
129 m : analyzer.reset(new refinery::ExceptionAnalyzer());
130 m : runner.AddAnalyzer(std::move(analyzer));
131 m : analyzer.reset(new refinery::ModuleAnalyzer());
132 m : runner.AddAnalyzer(std::move(analyzer));
133 m : analyzer.reset(new refinery::StackAnalyzer());
134 m : runner.AddAnalyzer(std::move(analyzer));
135 m : analyzer.reset(new refinery::StackFrameAnalyzer());
136 m : runner.AddAnalyzer(std::move(analyzer));
137 :
138 m : scoped_refptr<DiaSymbolProvider> dia_symbol_provider(
139 m : new DiaSymbolProvider());
140 m : SimpleProcessAnalysis analysis(process_state, dia_symbol_provider,
141 m : symbol_provider_);
142 :
143 m : return runner.Analyze(minidump, analysis) == Analyzer::ANALYSIS_COMPLETE;
144 m : }
145 :
146 m : void ValidateTypedBlock(ProcessState* process_state,
147 m : Address expected_address,
148 m : Size expected_size,
149 m : ModuleId expected_module_id,
150 m : const std::string& expected_variable_name,
151 m : const base::string16& expected_type_name) {
152 m : TypedBlockRecordPtr typedblock_record;
153 : // Note: using FindSingleRecord as there should be no typed block overlap in
154 : // the context of this test.
155 m : ASSERT_TRUE(
156 m : process_state->FindSingleRecord(expected_address, &typedblock_record));
157 :
158 m : ASSERT_EQ(expected_address, typedblock_record->range().start());
159 m : ASSERT_EQ(expected_size, typedblock_record->range().size());
160 :
161 m : const TypedBlock& typedblock = typedblock_record->data();
162 m : ASSERT_EQ(expected_module_id, typedblock.module_id());
163 :
164 : // Validate the recovered type id corresponds to the expected name.
165 m : ModuleLayerAccessor accessor(process_state);
166 m : pe::PEFile::Signature signature;
167 m : ASSERT_TRUE(accessor.GetModuleSignature(expected_module_id, &signature));
168 :
169 m : scoped_refptr<TypeRepository> type_repository;
170 m : ASSERT_TRUE(symbol_provider_->FindOrCreateTypeRepository(signature,
171 m : &type_repository));
172 :
173 m : TypePtr recovered_type = type_repository->GetType(typedblock.type_id());
174 m : ASSERT_NE(nullptr, recovered_type);
175 m : ASSERT_EQ(expected_type_name, recovered_type->GetName());
176 :
177 m : ASSERT_EQ(expected_variable_name, typedblock.data_name());
178 m : }
179 :
180 m : private:
181 m : testing::ScopedMinidump scoped_minidump_;
182 :
183 m : scoped_refptr<SymbolProvider> symbol_provider_;
184 :
185 : // For stack frame validation.
186 m : uint32_t expected_esp_;
187 m : uint32_t eip_lowerbound_;
188 m : uint32_t eip_upperbound_;
189 :
190 : // Typed block validation.
191 m : Address expected_param_address_;
192 m : Address expected_udt_address_;
193 m : Address expected_udt_ptr_address_;
194 :
195 m : testing::ScopedSymbolPath scoped_symbol_path_;
196 m : };
197 :
198 : // This test fails under coverage instrumentation which is probably not friendly
199 : // to stackwalking.
200 : #ifdef _COVERAGE_BUILD
201 m : TEST_F(StackAndFrameAnalyzersTest, DISABLED_BasicTest) {
202 : #else
203 m : TEST_F(StackAndFrameAnalyzersTest, BasicTest) {
204 : #endif
205 m : base::win::ScopedCOMInitializer com_initializer;
206 :
207 : // Note: intentionally declared before determining expected_frame_base.
208 m : int dummy_argument = 22;
209 :
210 : // Generate the minidump, then analyze it.
211 : // Note: the expected frame base for SetupStackFrameAndGenerateMinidump should
212 : // be sizeof(void*) + sizeof(int) off of the current frame's top of stack
213 : // immediately prior to the call (accounting for callee argument and return
214 : // address).
215 m : uint32_t expected_frame_base = 0U;
216 m : __asm {
217 m : mov expected_frame_base, esp
218 m : }
219 m : expected_frame_base -= (sizeof(void*) + sizeof(int));
220 :
221 m : ASSERT_TRUE(SetupStackFrameAndGenerateMinidump(dummy_argument));
222 :
223 m : ProcessState process_state;
224 m : ASSERT_TRUE(AnalyzeMinidump(&process_state));
225 :
226 : // Ensure the test's thread was successfully walked.
227 m : StackRecordPtr stack;
228 m : DWORD thread_id = ::GetCurrentThreadId();
229 m : ASSERT_TRUE(
230 m : process_state.FindStackRecord(static_cast<size_t>(thread_id), &stack));
231 m : ASSERT_TRUE(stack->data().stack_walk_success());
232 :
233 : // Validate SetupStackFrameAndGenerateMinidump's frame.
234 m : StackFrameRecordPtr frame_record;
235 : // Note: using FindSingleRecord as there should be no frame record overlap
236 : // in the context of this test.
237 m : ASSERT_TRUE(process_state.FindSingleRecord(
238 m : static_cast<Address>(expected_esp()), &frame_record));
239 :
240 m : ASSERT_EQ(expected_esp(), frame_record->range().start());
241 m : ASSERT_EQ(expected_frame_base - expected_esp(), frame_record->range().size());
242 :
243 m : const StackFrame& frame = frame_record->data();
244 m : uint32_t recovered_eip = frame.register_info().eip();
245 m : ASSERT_LT(eip_lowerbound(), recovered_eip);
246 m : ASSERT_GT(eip_upperbound(), recovered_eip);
247 :
248 : // Sanity and tightness check.
249 m : ASSERT_GT(eip_upperbound(), eip_lowerbound());
250 m : ASSERT_LT(eip_upperbound() - eip_lowerbound(), 100);
251 :
252 : // TODO(manzagop): validate frame_size_bytes. It should be sizeof(void*)
253 : // smaller than expected_frame_base - expected_esp(), to account for ebp
254 : // and since the function called into has no parameters.
255 :
256 : // TODO(manzagop): validate locals_base. It should be sizeof(void*) off of
257 : // the frame base, to account for ebp.
258 :
259 : // Validate typed block layer for SetupStackFrameAndGenerateMinidump.
260 m : ModuleLayerAccessor accessor(&process_state);
261 m : ModuleId expected_module_id = accessor.GetModuleId(recovered_eip);
262 m : ASSERT_NE(kNoModuleId, expected_module_id);
263 :
264 : // - Validate some locals.
265 m : ASSERT_NO_FATAL_FAILURE(
266 m : ValidateTypedBlock(&process_state, expected_udt_address(),
267 m : sizeof(SimpleUDT), expected_module_id, "udt_local",
268 m : L"refinery::`anonymous-namespace'::SimpleUDT"));
269 m : ASSERT_NO_FATAL_FAILURE(ValidateTypedBlock(
270 m : &process_state, expected_udt_ptr_address(), sizeof(SimpleUDT*),
271 m : expected_module_id, "udt_ptr_local",
272 m : L"refinery::`anonymous-namespace'::SimpleUDT*"));
273 : // - Validate a parameter.
274 m : ASSERT_NO_FATAL_FAILURE(
275 m : ValidateTypedBlock(&process_state, expected_param_address(), sizeof(int),
276 m : expected_module_id, "dummy_param", L"int32_t"));
277 m : }
278 :
279 m : } // namespace refinery
|