OLD | NEW |
| (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 | |
OLD | NEW |