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 i : __declspec(naked) void* GetEbp() {
32 : __asm {
33 i : mov eax, ebp
34 i : ret
35 : }
36 : }
37 :
38 i : __declspec(naked) void* GetEsp() {
39 : __asm {
40 i : mov eax, esp
41 i : 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 E : __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 E : DCHECK_LE(reinterpret_cast<const void*>(4), stack_top);
62 E : return frame <= reinterpret_cast<const StackFrame*>(stack_top) - 1;
63 E : }
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 E : const StackFrame* frame) {
70 E : if (!IsFrameInBounds(stack_top, frame))
71 E : return false;
72 :
73 : // The current frame must be pointer aligned.
74 E : if (!::common::IsAligned(frame, kPointerSize))
75 i : return false;
76 :
77 : // The return address must not be null, and it can't be in the stack.
78 E : if (frame->return_address == nullptr)
79 E : return false;
80 : if (frame->return_address >= stack_bottom &&
81 E : frame->return_address < stack_top) {
82 E : return false;
83 : }
84 :
85 E : return true;
86 E : }
87 :
88 E : __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 E : if (frame + 1 > frame->next_frame)
93 E : return false;
94 E : return true;
95 E : }
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 E : 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 E : NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb());
107 E : void* stack_bottom = tib->StackLimit; // Lower address.
108 E : 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 E : reinterpret_cast<StackFrame*>(stack_bottom) + 1 >= stack_top) {
116 i : return 0;
117 : }
118 :
119 : // Ensure that the stack makes sense. If not, it's been hijacked and
120 : // something is seriously wrong.
121 E : void *current_esp = GetEsp();
122 E : void* current_ebp = GetEbp();
123 : if (stack_bottom > current_esp || current_esp > current_ebp ||
124 E : !IsFrameInBounds(stack_top, current_ebp)) {
125 i : return 0;
126 : }
127 :
128 : return WalkStackImpl(current_ebp, stack_bottom, stack_top,
129 : bottom_frames_to_skip, max_frame_count, frames,
130 E : absolute_stack_id);
131 E : }
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 E : StackId* absolute_stack_id) {
140 E : DCHECK(::common::IsAligned(current_ebp, kPointerSize));
141 E : DCHECK(::common::IsAligned(stack_top, kPointerSize));
142 E : DCHECK_LT(stack_bottom, stack_top);
143 E : DCHECK_LE(reinterpret_cast<const StackFrame*>(stack_bottom) + 1, stack_top);
144 E : DCHECK_LE(current_ebp, stack_top);
145 E : DCHECK_NE(static_cast<void**>(nullptr), frames);
146 E : DCHECK_NE(static_cast<StackId*>(nullptr), absolute_stack_id);
147 :
148 E : *absolute_stack_id = StackCapture::StartStackId();
149 :
150 : const StackFrame* current_frame =
151 E : reinterpret_cast<const StackFrame*>(current_ebp);
152 :
153 : // Skip over any requested frames.
154 E : while (bottom_frames_to_skip) {
155 E : if (!FrameHasValidReturnAddress(stack_bottom, stack_top, current_frame))
156 i : return 0;
157 E : if (!CanAdvanceFrame(current_frame))
158 i : return 0;
159 E : --bottom_frames_to_skip;
160 E : current_frame = current_frame->next_frame;
161 E : }
162 :
163 : // Grab as many frames as possible.
164 E : size_t num_frames = 0;
165 E : while (num_frames < max_frame_count) {
166 E : if (!FrameHasValidReturnAddress(stack_bottom, stack_top, current_frame))
167 E : break;
168 E : frames[num_frames] = current_frame->return_address;
169 E : ++num_frames;
170 : *absolute_stack_id = StackCapture::UpdateStackId(
171 E : *absolute_stack_id, current_frame->return_address);
172 :
173 E : if (!CanAdvanceFrame(current_frame))
174 E : break;
175 :
176 E : current_frame = current_frame->next_frame;
177 E : }
178 :
179 : *absolute_stack_id =
180 E : StackCapture::FinalizeStackId(*absolute_stack_id, num_frames);
181 :
182 E : return num_frames;
183 E : }
184 :
185 : } // namespace common
186 : } // namespace agent
|