Coverage for /Syzygy/agent/asan/page_protection_helpers_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
97.0%1611660.C++test

Line-by-line coverage:

   1    :  // Copyright 2014 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/asan/page_protection_helpers.h"
  16    :  
  17    :  #include "gtest/gtest.h"
  18    :  #include "syzygy/agent/asan/unittest_util.h"
  19    :  
  20    :  namespace agent {
  21    :  namespace asan {
  22    :  
  23    :  namespace {
  24    :  
  25    :  // Use the unittest fixture with an OnExceptionCallback.
  26    :  class PageProtectionHelpersTest : public testing::OnExceptionCallbackTest {
  27    :   public:
  28    :    using Super = testing::OnExceptionCallbackTest;
  29    :  
  30    :    enum Protection {
  31    :      kProtectNone,
  32    :      kProtectRedzones,
  33    :      kProtectAll,
  34    :    };
  35    :  
  36  E :    void SetUp() override {
  37  E :      Super::SetUp();
  38  E :      shadow_.SetUp();
  39  E :    }
  40    :  
  41  E :    void TearDown() override {
  42  E :      shadow_.TearDown();
  43  E :      Super::TearDown();
  44  E :    }
  45    :  
  46    :    // Wrapper to testing::IsAccessible that also checks the shadow memory page
  47    :    // protection bits.
  48  E :    bool IsAccessible(void* addr) {
  49  E :      if (!testing::IsAccessible(addr))
  50  i :        return false;
  51  E :      if (shadow_.PageIsProtected(addr))
  52  i :        return false;
  53  E :      return true;
  54  E :    }
  55    :  
  56    :    // Wrapper to testing::IsNotAccessible that also checks the shadow memory page
  57    :    // protection bits.
  58  E :    bool IsNotAccessible(void* addr) {
  59  E :      if (!testing::IsNotAccessible(addr))
  60  i :        return false;
  61  E :      if (!shadow_.PageIsProtected(addr))
  62  i :        return false;
  63  E :      return true;
  64  E :    }
  65    :  
  66    :    void TestAccessUnderProtection(const BlockInfo& block_info,
  67  E :                                   Protection protection) {
  68    :      // Grab a set of points to sample for access, scattered across the various
  69    :      // components of the block.
  70  E :      std::set<void*> samples;
  71  E :      samples.insert(block_info.RawHeader());
  72  E :      samples.insert(block_info.RawHeaderPadding() - 1);
  73  E :      samples.insert(block_info.header_padding);
  74    :      samples.insert(block_info.RawHeaderPadding() +
  75  E :                     block_info.header_padding_size / 2);
  76    :      samples.insert(block_info.RawHeaderPadding() +
  77  E :                     block_info.header_padding_size - 1);
  78  E :      samples.insert(block_info.RawBody());
  79  E :      samples.insert(block_info.RawBody() + block_info.body_size / 2);
  80  E :      samples.insert(block_info.RawBody() + block_info.body_size - 1);
  81  E :      samples.insert(block_info.RawTrailerPadding());
  82    :      samples.insert(block_info.RawTrailerPadding() +
  83  E :                     block_info.trailer_padding_size / 2);
  84    :      samples.insert(block_info.RawTrailerPadding() +
  85  E :                     block_info.trailer_padding_size - 1);
  86  E :      samples.insert(block_info.RawTrailer());
  87  E :      samples.insert(block_info.RawTrailer());
  88  E :      samples.insert(block_info.RawBlock() + block_info.block_size - 1);
  89    :  
  90    :      // Also sample at points at the edges of the pages in the redzones.
  91  E :      if (block_info.left_redzone_pages_size > 0) {
  92  E :        if (block_info.RawBlock() < block_info.left_redzone_pages)
  93  i :          samples.insert(block_info.left_redzone_pages - 1);
  94  E :        samples.insert(block_info.left_redzone_pages);
  95    :        samples.insert(block_info.left_redzone_pages +
  96  E :                       block_info.left_redzone_pages_size - 1);
  97    :        samples.insert(block_info.left_redzone_pages +
  98  E :                       block_info.left_redzone_pages_size);
  99    :      }
 100  E :      if (block_info.right_redzone_pages_size > 0) {
 101  E :        samples.insert(block_info.right_redzone_pages - 1);
 102  E :        samples.insert(block_info.right_redzone_pages);
 103    :        samples.insert(block_info.right_redzone_pages +
 104  E :                       block_info.right_redzone_pages_size - 1);
 105    :        uint8* past_end =
 106  E :            block_info.right_redzone_pages + block_info.right_redzone_pages_size;
 107  E :        if (past_end < block_info.RawBlock() + block_info.block_size)
 108  E :          samples.insert(past_end);
 109    :      }
 110    :  
 111    :      uint8* left_end =
 112  E :          block_info.left_redzone_pages + block_info.left_redzone_pages_size;
 113    :      uint8* right_end =
 114  E :          block_info.right_redzone_pages + block_info.right_redzone_pages_size;
 115  E :      uint8* block_end = block_info.block_pages + block_info.block_pages_size;
 116    :  
 117  E :      std::set<void*>::const_iterator it = samples.begin();
 118  E :      for (; it != samples.end(); ++it) {
 119    :        if ((*it >= block_info.left_redzone_pages && *it < left_end) ||
 120  E :            (*it >= block_info.right_redzone_pages && *it < right_end)) {
 121    :          // In the left or right guard pages.
 122  E :          if (protection == kProtectNone) {
 123  E :            EXPECT_TRUE(IsAccessible(*it));
 124  E :          } else {
 125  E :            EXPECT_TRUE(IsNotAccessible(*it));
 126  E :          }
 127  E :        } else if (*it >= block_info.block_pages && *it < block_end) {
 128    :          // In the block pages, but not a left or right guard page.
 129  E :          if (protection == kProtectAll) {
 130  E :            EXPECT_TRUE(IsNotAccessible(*it));
 131  E :          } else {
 132  E :            EXPECT_TRUE(IsAccessible(*it));
 133    :          }
 134  E :        } else {
 135    :          // In the block, but in a page that is only partially covered.
 136  E :          EXPECT_TRUE(IsAccessible(*it));
 137    :        }
 138  E :      }
 139  E :    }
 140    :  
 141    :    // Tests that the page protections are as expected after calling
 142    :    // BlockProtectNone.
 143  E :    void TestProtectNone(const BlockInfo& block_info) {
 144  E :      BlockProtectNone(block_info, &shadow_);
 145  E :      EXPECT_NO_FATAL_FAILURE(
 146    :          TestAccessUnderProtection(block_info, kProtectNone));
 147  E :    }
 148    :  
 149    :    // Tests that the page protections are as expected after calling
 150    :    // BlockProtectRedzones.
 151  E :    void TestProtectRedzones(const BlockInfo& block_info) {
 152  E :      BlockProtectRedzones(block_info, &shadow_);
 153  E :      EXPECT_NO_FATAL_FAILURE(
 154    :          TestAccessUnderProtection(block_info, kProtectRedzones));
 155  E :    }
 156    :  
 157    :    // Tests that the page protections are as expected after calling
 158    :    // BlockProtectAll.
 159  E :    void TestProtectAll(const BlockInfo& block_info) {
 160  E :      BlockProtectAll(block_info, &shadow_);
 161  E :      EXPECT_NO_FATAL_FAILURE(TestAccessUnderProtection(block_info, kProtectAll));
 162  E :    }
 163    :  
 164    :    // Tests that all page protection transitions work.
 165    :    void TestAllProtectionTransitions(size_t chunk_size,
 166    :                                      size_t alignment,
 167    :                                      size_t size,
 168    :                                      size_t min_left_redzone_size,
 169  E :                                      size_t min_right_redzone_size) {
 170    :      // Create and initialize the given block.
 171  E :      BlockLayout layout = {};
 172  E :      EXPECT_TRUE(BlockPlanLayout(chunk_size, alignment, size,
 173    :                                  min_left_redzone_size, min_right_redzone_size,
 174    :                                  &layout));
 175    :      void* alloc =
 176  E :          ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT, PAGE_READWRITE);
 177  E :      ASSERT_TRUE(alloc != NULL);
 178  E :      BlockInfo block_info = {};
 179  E :      BlockInitialize(layout, alloc, false, &block_info);
 180    :  
 181    :      // By default the protections should be disabled for a fresh allocation.
 182  E :      EXPECT_NO_FATAL_FAILURE(
 183    :          TestAccessUnderProtection(block_info, kProtectNone));
 184    :  
 185    :      // Try a full cycle of page protections. This tests all possible
 186    :      // transitions, including self transitions.
 187  E :      EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
 188  E :      EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
 189  E :      EXPECT_NO_FATAL_FAILURE(TestProtectRedzones(block_info));
 190  E :      EXPECT_NO_FATAL_FAILURE(TestProtectRedzones(block_info));
 191  E :      EXPECT_NO_FATAL_FAILURE(TestProtectAll(block_info));
 192  E :      EXPECT_NO_FATAL_FAILURE(TestProtectAll(block_info));
 193  E :      EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
 194  E :      EXPECT_NO_FATAL_FAILURE(TestProtectAll(block_info));
 195  E :      EXPECT_NO_FATAL_FAILURE(TestProtectRedzones(block_info));
 196  E :      EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
 197    :  
 198  E :      ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
 199  E :    }
 200    :  
 201    :    Shadow shadow_;
 202    :  };
 203    :  
 204    :  using testing::_;
 205    :  
 206    :  }  // namespace
 207    :  
 208  E :  TEST_F(PageProtectionHelpersTest, GetBlockInfo) {
 209    :    // Plan a layout that is subject to page protections.
 210  E :    BlockLayout layout = {};
 211  E :    BlockPlanLayout(4096, 4096, 4096, 4096, 4096, &layout);
 212    :  
 213    :    void* alloc = ::VirtualAlloc(
 214  E :        nullptr, layout.block_size, MEM_COMMIT, PAGE_READWRITE);
 215  E :    ASSERT_TRUE(alloc != nullptr);
 216  E :    ::memset(alloc, 0, layout.block_size);
 217    :  
 218    :    // Initialize the block in both memory and the shadow memory.
 219  E :    BlockInfo info = {};
 220  E :    BlockInitialize(layout, alloc, false, &info);
 221  E :    shadow_.PoisonAllocatedBlock(info);
 222    :  
 223    :    // Try recovering in the usual case.
 224  E :    BlockInfo info_recovered = {};
 225  E :    EXPECT_TRUE(BlockInfoFromMemory(info.header, &info_recovered));
 226  E :    EXPECT_TRUE(GetBlockInfo(&shadow_, info.body, &info_recovered));
 227  E :    EXPECT_EQ(0, ::memcmp(&info, &info_recovered, sizeof(info)));
 228    :  
 229    :    // Muck up the header and try again.
 230  E :    info.header->magic++;
 231  E :    EXPECT_FALSE(BlockInfoFromMemory(info.header, &info_recovered));
 232  E :    EXPECT_TRUE(GetBlockInfo(&shadow_, info.body, &info_recovered));
 233  E :    EXPECT_EQ(0, ::memcmp(&info, &info_recovered, sizeof(info)));
 234  E :    info.header->magic--;
 235  E :    EXPECT_TRUE(BlockInfoFromMemory(info.header, &info_recovered));
 236    :  
 237    :    // Set page protections and try again.
 238  E :    BlockProtectRedzones(info, &shadow_);
 239  E :    EXPECT_CALL(*this, OnExceptionCallback(_));
 240  E :    EXPECT_FALSE(BlockInfoFromMemory(info.header, &info_recovered));
 241  E :    testing::Mock::VerifyAndClearExpectations(this);
 242  E :    EXPECT_TRUE(GetBlockInfo(&shadow_, info.body, &info_recovered));
 243  E :    EXPECT_EQ(0, ::memcmp(&info, &info_recovered, sizeof(info)));
 244  E :    BlockProtectNone(info, &shadow_);
 245  E :    EXPECT_TRUE(BlockInfoFromMemory(info.header, &info_recovered));
 246    :  
 247    :    // Clean up.
 248  E :    shadow_.Unpoison(info.header, info.block_size);
 249  E :    ::VirtualFree(alloc, layout.block_size, MEM_RELEASE);
 250  E :  }
 251    :  
 252  E :  TEST_F(PageProtectionHelpersTest, ProtectionTransitions) {
 253    :    // Left and right guard pages, everything page aligned.
 254    :    EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
 255  E :        4096, 4096, 4096, 4096, 4096));
 256    :  
 257    :    // Left and right guard pages will contain entire pages, but
 258    :    // not be entirely covered by pages.
 259    :    EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
 260  E :        8, 8, 128, 4100, 8200));
 261    :  
 262    :    // An allocation that will contain no pages whatsoever.
 263    :    EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
 264  E :        8, 8, 67, 0, 0));
 265    :  
 266    :    // An allocation with redzones containing no pages, but that covers an
 267    :    // entire page.
 268    :    EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
 269  E :        4096, 8, 67, 0, 0));
 270  E :  }
 271    :  
 272  E :  TEST_F(PageProtectionHelpersTest, BlockProtectAuto) {
 273  E :    BlockLayout layout = {};
 274  E :    const size_t kPageSize = GetPageSize();
 275    :    EXPECT_TRUE(BlockPlanLayout(kPageSize, kPageSize, kPageSize, kPageSize,
 276  E :                                kPageSize, &layout));
 277    :    void* alloc = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
 278  E :                                 PAGE_READWRITE);
 279  E :    ASSERT_TRUE(alloc != NULL);
 280    :  
 281  E :    BlockInfo block_info = {};
 282  E :    BlockInitialize(layout, alloc, false, &block_info);
 283  E :    TestAccessUnderProtection(block_info, kProtectNone);
 284    :  
 285  E :    block_info.header->state = ALLOCATED_BLOCK;
 286  E :    BlockProtectAuto(block_info, &shadow_);
 287  E :    TestAccessUnderProtection(block_info, kProtectRedzones);
 288  E :    BlockProtectNone(block_info, &shadow_);
 289    :  
 290  E :    block_info.header->state = QUARANTINED_BLOCK;
 291  E :    BlockProtectAuto(block_info, &shadow_);
 292  E :    TestAccessUnderProtection(block_info, kProtectAll);
 293  E :    BlockProtectNone(block_info, &shadow_);
 294    :  
 295  E :    block_info.header->state = QUARANTINED_FLOODED_BLOCK;
 296  E :    BlockProtectAuto(block_info, &shadow_);
 297  E :    TestAccessUnderProtection(block_info, kProtectAll);
 298  E :    BlockProtectNone(block_info, &shadow_);
 299    :  
 300  E :    block_info.header->state = FREED_BLOCK;
 301  E :    BlockProtectAuto(block_info, &shadow_);
 302  E :    TestAccessUnderProtection(block_info, kProtectAll);
 303  E :    BlockProtectNone(block_info, &shadow_);
 304    :  
 305  E :    ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
 306  E :  }
 307    :  
 308    :  }  // namespace asan
 309    :  }  // namespace agent

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