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 "syzygy/refinery/analyzers/stack_analyzer.h"
16 :
17 : #include "base/numerics/safe_math.h"
18 : #include "base/strings/stringprintf.h"
19 : #include "syzygy/common/com_utils.h"
20 : #include "syzygy/pe/dia_util.h"
21 : #include "syzygy/refinery/analyzers/stack_analyzer_impl.h"
22 : #include "syzygy/refinery/process_state/refinery.pb.h"
23 :
24 m : namespace refinery {
25 :
26 m : namespace {
27 :
28 m : bool GetRegisterValue(IDiaStackFrame* frame,
29 m : CV_HREG_e register_index,
30 m : uint32_t* register_value) {
31 m : DCHECK(frame); DCHECK(register_value);
32 :
33 m : uint64_t value = 0ULL;
34 m : if (!pe::GetRegisterValue(frame, register_index, &value)) {
35 m : return false;
36 m : }
37 m : base::CheckedNumeric<uint32_t> checked_value =
38 m : base::CheckedNumeric<uint32_t>::cast(value);
39 m : if (!checked_value.IsValid()) {
40 m : LOG(ERROR) << "register value is not a 32 bit value.";
41 m : return false;
42 m : }
43 m : *register_value = checked_value.ValueOrDie();
44 m : return true;
45 m : }
46 :
47 m : } // namespace
48 :
49 : // static
50 m : const char StackAnalyzer::kStackAnalyzerName[] = "StackAnalyzer";
51 :
52 m : StackAnalyzer::StackAnalyzer() : child_frame_context_(nullptr) {
53 m : }
54 :
55 m : Analyzer::AnalysisResult StackAnalyzer::Analyze(
56 m : const minidump::Minidump& minidump,
57 m : const ProcessAnalysis& process_analysis) {
58 m : DCHECK(process_analysis.process_state() != nullptr);
59 m : DCHECK(process_analysis.dia_symbol_provider() != nullptr);
60 :
61 : // Create stack walker and helper.
62 m : if (!pe::CreateDiaObject(stack_walker_.Receive(),
63 m : CLSID_DiaStackWalker)) {
64 m : return ANALYSIS_ERROR;
65 m : }
66 m : stack_walk_helper_ =
67 m : new StackWalkHelper(process_analysis.dia_symbol_provider());
68 :
69 : // Get the stack layer - it must already have been populated.
70 m : StackLayerPtr stack_layer;
71 m : if (!process_analysis.process_state()->FindLayer(&stack_layer)) {
72 m : LOG(ERROR) << "Missing stack layer.";
73 m : return ANALYSIS_ERROR;
74 m : }
75 :
76 : // Process each thread's stack.
77 m : Analyzer::AnalysisResult result = ANALYSIS_COMPLETE;
78 m : for (StackRecordPtr stack_record : *stack_layer) {
79 : // Attempt to stack walk. Note that the stack walk derailing is not an
80 : // analysis error.
81 m : Analyzer::AnalysisResult stack_result =
82 m : StackWalk(stack_record, process_analysis);
83 m : if (stack_result == ANALYSIS_ERROR)
84 m : return ANALYSIS_ERROR;
85 m : if (stack_result == ANALYSIS_ITERATE)
86 m : result = ANALYSIS_ITERATE;
87 m : }
88 :
89 m : return result;
90 m : }
91 :
92 m : Analyzer::AnalysisResult StackAnalyzer::StackWalk(
93 m : StackRecordPtr stack_record,
94 m : const ProcessAnalysis& process_analysis) {
95 m : stack_walk_helper_->SetState(stack_record, process_analysis.process_state());
96 m : child_frame_context_ = nullptr;
97 :
98 : // Create the frame enumerator.
99 m : base::win::ScopedComPtr<IDiaEnumStackFrames> frame_enumerator;
100 : // TODO(manzagop): this is for x86 platforms. Switch to getEnumFrames2.
101 m : HRESULT hr = stack_walker_->getEnumFrames(
102 m : static_cast<IDiaStackWalkHelper*>(stack_walk_helper_.get()),
103 m : frame_enumerator.Receive());
104 m : if (hr != S_OK) {
105 m : LOG(ERROR) << "Failed to get frame enumerator: " << common::LogHr(hr)
106 m : << ".";
107 m : return ANALYSIS_ERROR;
108 m : }
109 m : frame_enumerator->Reset();
110 :
111 : // Walk the stack frames.
112 : // TODO(manzagop): changes for non-X86 platforms (eg registers).
113 m : while (true) {
114 m : base::win::ScopedComPtr<IDiaStackFrame> stack_frame;
115 m : DWORD retrieved_cnt = 0;
116 m : hr = frame_enumerator->Next(1, stack_frame.Receive(), &retrieved_cnt);
117 m : if (!SUCCEEDED(hr)) {
118 : // Stack walking derailed. Not an an analyzer error per se.
119 m : LOG(ERROR) << "Failed to get stack frame: " << common::LogHr(hr) << ".";
120 m : return ANALYSIS_COMPLETE;
121 m : }
122 m : if (hr == S_FALSE || retrieved_cnt != 1)
123 m : break; // No frame.
124 :
125 m : if (!InsertStackFrameRecord(stack_frame.get(), process_analysis))
126 m : return ANALYSIS_ERROR;
127 :
128 : // WinDBG seems to use a null return address as a termination criterion.
129 m : ULONGLONG frame_return_addr = 0ULL;
130 m : hr = stack_frame->get_returnAddress(&frame_return_addr);
131 m : if (hr != S_OK) {
132 m : LOG(ERROR) << "Failed to get frame's return address: "
133 m : << common::LogHr(hr) << ".";
134 m : return ANALYSIS_ERROR;
135 m : }
136 m : if (frame_return_addr == 0ULL) {
137 m : stack_record->mutable_data()->set_stack_walk_success(true);
138 m : break;
139 m : }
140 m : }
141 :
142 m : return ANALYSIS_COMPLETE;
143 m : }
144 :
145 : // TODO(manzagop): revise when support expands beyond x86.
146 m : bool StackAnalyzer::InsertStackFrameRecord(
147 m : IDiaStackFrame* stack_frame,
148 m : const ProcessAnalysis& process_analysis) {
149 m : RegisterInformation* child_context = child_frame_context_;
150 m : child_frame_context_ = nullptr;
151 :
152 : // Get the frame's base.
153 m : uint64_t frame_base = 0ULL;
154 m : if (!pe::GetFrameBase(stack_frame, &frame_base))
155 m : return false;
156 :
157 : // Get frame's top.
158 m : uint64_t frame_top = 0ULL;
159 m : if (!pe::GetRegisterValue(stack_frame, CV_REG_ESP, &frame_top))
160 m : return false;
161 :
162 : // Get the frame's size. Note: this differs from the difference between
163 : // top of frame and base in that it excludes callee parameter size.
164 m : uint32_t frame_size = 0U;
165 m : if (!pe::GetSize(stack_frame, &frame_size))
166 m : return false;
167 :
168 : // Get base address of locals.
169 m : uint64_t locals_base = 0ULL;
170 m : if (!pe::GetLocalsBase(stack_frame, &locals_base))
171 m : return false;
172 :
173 : // TODO(manzagop): get register values and some notion about their validity.
174 :
175 : // Populate the stack frame layer.
176 :
177 : // Compute the frame's full size.
178 m : DCHECK_LE(frame_top, frame_base);
179 m : base::CheckedNumeric<Size> frame_full_size =
180 m : base::CheckedNumeric<Size>::cast(frame_base - frame_top);
181 m : if (!frame_full_size.IsValid()) {
182 m : LOG(ERROR) << "Frame full size doesn't fit a 32bit integer.";
183 m : return false;
184 m : }
185 :
186 m : if (frame_full_size.ValueOrDie() == 0U)
187 m : return true; // Skip empty frame.
188 :
189 : // Create the stack frame record.
190 m : AddressRange range(static_cast<Address>(frame_top),
191 m : static_cast<Size>(frame_full_size.ValueOrDie()));
192 m : if (!range.IsValid()) {
193 m : LOG(ERROR) << "Invalid frame range.";
194 m : return false;
195 m : }
196 :
197 m : StackFrameLayerPtr frame_layer;
198 m : process_analysis.process_state()->FindOrCreateLayer(&frame_layer);
199 :
200 m : StackFrameRecordPtr frame_record;
201 m : frame_layer->CreateRecord(range, &frame_record);
202 m : StackFrame* frame_proto = frame_record->mutable_data();
203 :
204 : // Populate the stack frame record.
205 :
206 : // Register context.
207 : // TODO(manzagop): flesh out the register context.
208 m : RegisterInformation* context = frame_proto->mutable_register_info();
209 m : uint32_t eip = 0U;
210 m : if (!GetRegisterValue(stack_frame, CV_REG_EIP, &eip))
211 m : return false;
212 m : context->set_eip(eip);
213 m : uint32_t allreg_vframe = 0U;
214 m : if (GetRegisterValue(stack_frame, CV_ALLREG_VFRAME, &allreg_vframe)) {
215 : // Register doesn't seem to always be available. Not considered an error.
216 m : context->set_allreg_vframe(allreg_vframe);
217 m : if (child_context)
218 m : child_context->set_parent_allreg_vframe(allreg_vframe);
219 m : }
220 :
221 m : frame_proto->set_frame_size_bytes(frame_size);
222 m : frame_proto->set_locals_base(locals_base);
223 :
224 m : child_frame_context_ = context;
225 m : return true;
226 m : }
227 :
228 m : } // namespace refinery
|