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