Coverage for /Syzygy/agent/common/stack_walker_x86.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
84.3%59700.C++source

Line-by-line coverage:

   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

Coverage information generated Thu Jan 14 17:40:38 2016.