Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: syzygy/agent/common/stack_walker_x86.cc

Issue 2194383007: Port some more code to x64 (Closed) Base URL: git@github.com:google/syzygy.git@master
Patch Set: Rename the stack_walker unittest and fix the x86-64 implementation of stack_walker Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/agent/common/stack_walker_x86.h"
16
17 #include <windows.h>
18 #include <winnt.h>
19
20 #include "base/logging.h"
21 #include "syzygy/agent/common/stack_capture.h"
22 #include "syzygy/common/align.h"
23
24 namespace agent {
25 namespace common {
26
27 namespace {
28
29 static size_t kPointerSize = sizeof(void*);
30
31 __declspec(naked) void* GetEbp() {
32 __asm {
33 mov eax, ebp
34 ret
35 }
36 }
37
38 __declspec(naked) void* GetEsp() {
39 __asm {
40 mov eax, esp
41 ret
42 }
43 }
44
45 // A small struct that can be laid out on top of a standard stack frame in
46 // order to grab the EBP and return address fields. Strictly speaking this
47 // is actually a snippet along the edge of two frames: |next_frame| belonging
48 // to the callee, and |return_address| belonging to the caller.
49 struct StackFrame {
50 StackFrame* next_frame;
51 void* return_address;
52 };
53
54 // Helper function to determine if the given stack frame is in bounds with
55 // respect to the top of the stack.
56 __forceinline bool IsFrameInBounds(const void* stack_top, const void* frame) {
57 // We've already confirmed that stack_bottom < stack_top, so stack_top can be
58 // safely decremented without underflowing. On the other hand, we can't
59 // increment |frame| without potentially overflowing. Yup, learned that one
60 // the hard way.
61 DCHECK_LE(reinterpret_cast<const void*>(4), stack_top);
62 return frame <= reinterpret_cast<const StackFrame*>(stack_top) - 1;
63 }
64
65 // Returns true if the stack frame has a valid return address that can be
66 // read from.
67 __forceinline bool FrameHasValidReturnAddress(const void* stack_bottom,
68 const void* stack_top,
69 const StackFrame* frame) {
70 if (!IsFrameInBounds(stack_top, frame))
71 return false;
72
73 // The current frame must be pointer aligned.
74 if (!::common::IsAligned(frame, kPointerSize))
75 return false;
76
77 // The return address must not be null, and it can't be in the stack.
78 if (frame->return_address == nullptr)
79 return false;
80 if (frame->return_address >= stack_bottom &&
81 frame->return_address < stack_top) {
82 return false;
83 }
84
85 return true;
86 }
87
88 __forceinline bool CanAdvanceFrame(const StackFrame* frame) {
89 // The next frame pointer must be at least a full frame beyond the current
90 // frame. Checking that the next frame lies within the stack is done by
91 // 'FrameHasValidReturnAddress' before it gets read.
92 if (frame + 1 > frame->next_frame)
93 return false;
94 return true;
95 }
96
97 } // namespace
98
99 size_t __declspec(noinline) WalkStack(size_t bottom_frames_to_skip,
100 size_t max_frame_count,
101 void** frames,
102 StackId* absolute_stack_id) {
103 // Get the stack extents.
104 // The first thing in the TEB is actually the TIB.
105 // http://www.nirsoft.net/kernel_struct/vista/TEB.html
106 NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb());
107 void* stack_bottom = tib->StackLimit; // Lower address.
108 void* stack_top = tib->StackBase; // Higher address.
109
110 // Ensure that the stack extents make sense, and bail early if they
111 // don't. Only proceed if there's at least room for a single pointer on
112 // the stack.
113 if (!::common::IsAligned(stack_top, kPointerSize) ||
114 stack_bottom >= stack_top ||
115 reinterpret_cast<StackFrame*>(stack_bottom) + 1 >= stack_top) {
116 return 0;
117 }
118
119 // Ensure that the stack makes sense. If not, it's been hijacked and
120 // something is seriously wrong.
121 void *current_esp = GetEsp();
122 void* current_ebp = GetEbp();
123 if (stack_bottom > current_esp || current_esp > current_ebp ||
124 !IsFrameInBounds(stack_top, current_ebp)) {
125 return 0;
126 }
127
128 return WalkStackImpl(current_ebp, stack_bottom, stack_top,
129 bottom_frames_to_skip, max_frame_count, frames,
130 absolute_stack_id);
131 }
132
133 size_t WalkStackImpl(const void* current_ebp,
134 const void* stack_bottom,
135 const void* stack_top,
136 size_t bottom_frames_to_skip,
137 size_t max_frame_count,
138 void** frames,
139 StackId* absolute_stack_id) {
140 DCHECK(::common::IsAligned(current_ebp, kPointerSize));
141 DCHECK(::common::IsAligned(stack_top, kPointerSize));
142 DCHECK_LT(stack_bottom, stack_top);
143 DCHECK_LE(reinterpret_cast<const StackFrame*>(stack_bottom) + 1, stack_top);
144 DCHECK_LE(current_ebp, stack_top);
145 DCHECK_NE(static_cast<void**>(nullptr), frames);
146 DCHECK_NE(static_cast<StackId*>(nullptr), absolute_stack_id);
147
148 *absolute_stack_id = StackCapture::StartStackId();
149
150 const StackFrame* current_frame =
151 reinterpret_cast<const StackFrame*>(current_ebp);
152
153 // Skip over any requested frames.
154 while (bottom_frames_to_skip) {
155 if (!FrameHasValidReturnAddress(stack_bottom, stack_top, current_frame))
156 return 0;
157 if (!CanAdvanceFrame(current_frame))
158 return 0;
159 --bottom_frames_to_skip;
160 current_frame = current_frame->next_frame;
161 }
162
163 // Grab as many frames as possible.
164 size_t num_frames = 0;
165 while (num_frames < max_frame_count) {
166 if (!FrameHasValidReturnAddress(stack_bottom, stack_top, current_frame))
167 break;
168 frames[num_frames] = current_frame->return_address;
169 ++num_frames;
170 *absolute_stack_id = StackCapture::UpdateStackId(
171 *absolute_stack_id, current_frame->return_address);
172
173 if (!CanAdvanceFrame(current_frame))
174 break;
175
176 current_frame = current_frame->next_frame;
177 }
178
179 *absolute_stack_id =
180 StackCapture::FinalizeStackId(*absolute_stack_id, num_frames);
181
182 return num_frames;
183 }
184
185 } // namespace common
186 } // namespace agent
OLDNEW
« no previous file with comments | « syzygy/agent/common/stack_walker_x86.h ('k') | syzygy/agent/common/stack_walker_x86_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698