| 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 |