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> checked_value =
38 m : base::CheckedNumeric<uint32>::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(scoped_refptr<DiaSymbolProvider> symbol_provider)
53 m : : symbol_provider_(symbol_provider), child_frame_context_(nullptr) {
54 m : DCHECK(symbol_provider.get() != nullptr);
55 m : }
56 :
57 m : Analyzer::AnalysisResult StackAnalyzer::Analyze(
58 m : const minidump::Minidump& minidump,
59 m : ProcessState* process_state) {
60 m : DCHECK(process_state != nullptr);
61 :
62 : // Create stack walker and helper.
63 m : HRESULT hr = stack_walker_.CreateInstance(CLSID_DiaStackWalker);
64 m : if (hr != S_OK) {
65 m : LOG(ERROR) << "Failed to create DiaStackWalker: " << common::LogHr(hr)
66 m : << ".";
67 m : return ANALYSIS_ERROR;
68 m : }
69 m : stack_walk_helper_ = new StackWalkHelper(symbol_provider_);
70 :
71 : // Get the stack layer - it must already have been populated.
72 m : StackLayerPtr stack_layer;
73 m : if (!process_state->FindLayer(&stack_layer)) {
74 m : LOG(ERROR) << "Missing stack layer.";
75 m : return ANALYSIS_ERROR;
76 m : }
77 :
78 : // Process each thread's stack.
79 m : Analyzer::AnalysisResult result = ANALYSIS_COMPLETE;
80 m : for (StackRecordPtr stack_record : *stack_layer) {
81 : // Attempt to stack walk. Note that the stack walk derailing is not an
82 : // analysis error.
83 m : Analyzer::AnalysisResult stack_result =
84 m : StackWalk(stack_record, process_state);
85 m : if (stack_result == ANALYSIS_ERROR)
86 m : return ANALYSIS_ERROR;
87 m : if (stack_result == ANALYSIS_ITERATE)
88 m : result = ANALYSIS_ITERATE;
89 m : }
90 :
91 m : return result;
92 m : }
93 :
94 m : Analyzer::AnalysisResult StackAnalyzer::StackWalk(StackRecordPtr stack_record,
95 m : ProcessState* process_state) {
96 m : stack_walk_helper_->SetState(stack_record, process_state);
97 m : child_frame_context_ = nullptr;
98 :
99 : // Create the frame enumerator.
100 m : base::win::ScopedComPtr<IDiaEnumStackFrames> frame_enumerator;
101 : // TODO(manzagop): this is for x86 platforms. Switch to getEnumFrames2.
102 m : HRESULT hr = stack_walker_->getEnumFrames(
103 m : static_cast<IDiaStackWalkHelper*>(stack_walk_helper_.get()),
104 m : frame_enumerator.Receive());
105 m : if (hr != S_OK) {
106 m : LOG(ERROR) << "Failed to get frame enumerator: " << common::LogHr(hr)
107 m : << ".";
108 m : return ANALYSIS_ERROR;
109 m : }
110 m : frame_enumerator->Reset();
111 :
112 : // Walk the stack frames.
113 : // TODO(manzagop): changes for non-X86 platforms (eg registers).
114 m : while (true) {
115 m : base::win::ScopedComPtr<IDiaStackFrame> stack_frame;
116 m : DWORD retrieved_cnt = 0;
117 m : hr = frame_enumerator->Next(1, stack_frame.Receive(), &retrieved_cnt);
118 m : if (!SUCCEEDED(hr)) {
119 : // Stack walking derailed. Not an an analyzer error per se.
120 m : LOG(ERROR) << "Failed to get stack frame: " << common::LogHr(hr) << ".";
121 m : return ANALYSIS_COMPLETE;
122 m : }
123 m : if (hr == S_FALSE || retrieved_cnt != 1)
124 m : break; // No frame.
125 :
126 m : if (!InsertStackFrameRecord(stack_frame.get(), process_state))
127 m : return ANALYSIS_ERROR;
128 :
129 : // WinDBG seems to use a null return address as a termination criterion.
130 m : ULONGLONG frame_return_addr = 0ULL;
131 m : hr = stack_frame->get_returnAddress(&frame_return_addr);
132 m : if (hr != S_OK) {
133 m : LOG(ERROR) << "Failed to get frame's return address: "
134 m : << common::LogHr(hr) << ".";
135 m : return ANALYSIS_ERROR;
136 m : }
137 m : if (frame_return_addr == 0ULL) {
138 m : stack_record->mutable_data()->set_stack_walk_success(true);
139 m : break;
140 m : }
141 m : }
142 :
143 m : return ANALYSIS_COMPLETE;
144 m : }
145 :
146 : // TODO(manzagop): revise when support expands beyond x86.
147 m : bool StackAnalyzer::InsertStackFrameRecord(IDiaStackFrame* stack_frame,
148 m : ProcessState* process_state) {
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 : // Create the stack frame record.
187 m : AddressRange range(static_cast<Address>(frame_top),
188 m : static_cast<Size>(frame_full_size.ValueOrDie()));
189 m : if (!range.IsValid()) {
190 m : LOG(ERROR) << "Invalid frame range.";
191 m : return false;
192 m : }
193 :
194 m : StackFrameLayerPtr frame_layer;
195 m : process_state->FindOrCreateLayer(&frame_layer);
196 :
197 m : StackFrameRecordPtr frame_record;
198 m : frame_layer->CreateRecord(range, &frame_record);
199 m : StackFrame* frame_proto = frame_record->mutable_data();
200 :
201 : // Populate the stack frame record.
202 :
203 : // Register context.
204 : // TODO(manzagop): flesh out the register context.
205 m : RegisterInformation* context = frame_proto->mutable_register_info();
206 m : uint32_t eip = 0U;
207 m : if (!GetRegisterValue(stack_frame, CV_REG_EIP, &eip))
208 m : return false;
209 m : context->set_eip(eip);
210 m : uint32_t allreg_vframe = 0U;
211 m : if (GetRegisterValue(stack_frame, CV_ALLREG_VFRAME, &allreg_vframe)) {
212 : // Register doesn't seem to always be available. Not considered an error.
213 m : context->set_allreg_vframe(allreg_vframe);
214 m : child_context->set_parent_allreg_vframe(allreg_vframe);
215 m : }
216 :
217 m : frame_proto->set_frame_size_bytes(frame_size);
218 m : frame_proto->set_locals_base(locals_base);
219 :
220 m : child_frame_context_ = context;
221 m : return true;
222 m : }
223 :
224 m : } // namespace refinery
|