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

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

Coverage information generated Thu Mar 26 16:15:41 2015.