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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
98.5%1951980.C++test

Line-by-line coverage:

   1    :  // Copyright 2012 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/asan_heap.h"
  16    :  
  17    :  #include "base/rand_util.h"
  18    :  #include "base/sha1.h"
  19    :  #include "gtest/gtest.h"
  20    :  #include "syzygy/agent/asan/asan_logger.h"
  21    :  #include "syzygy/agent/asan/asan_shadow.h"
  22    :  #include "syzygy/agent/asan/unittest_util.h"
  23    :  
  24    :  namespace agent {
  25    :  namespace asan {
  26    :  
  27    :  namespace {
  28    :  
  29    :  // A derived class to expose protected members for unit-testing.
  30    :  class TestHeapProxy : public HeapProxy {
  31    :   public:
  32    :    using HeapProxy::BlockHeader;
  33    :    using HeapProxy::FindAddressBlock;
  34    :    using HeapProxy::FreeBlockHeader;
  35    :    using HeapProxy::GetAllocSize;
  36    :    using HeapProxy::GetBadAccessKind;
  37    :    using HeapProxy::ToBlock;
  38    :  
  39  E :    explicit TestHeapProxy(StackCaptureCache* stack_cache, AsanLogger* logger)
  40    :        : HeapProxy(stack_cache, logger) {
  41  E :    }
  42    :  
  43    :    // Verify that the access to @p addr contained in @p header is an underflow.
  44  E :    bool IsUnderflowAccess(uint8* addr, BlockHeader* header) {
  45  E :      return GetBadAccessKind(addr, header) == HEAP_BUFFER_UNDERFLOW;
  46  E :    }
  47    :  
  48    :    // Verify that the access to @p addr contained in @p header is an overflow.
  49  E :    bool IsOverflowAccess(uint8* addr, BlockHeader* header) {
  50  E :      return GetBadAccessKind(addr, header) == HEAP_BUFFER_OVERFLOW;
  51  E :    }
  52    :  
  53    :    // Verify that the access to @p addr contained in @p header is an use after
  54    :    // free.
  55  E :    bool IsUseAfterAccess(uint8* addr, BlockHeader* header) {
  56  E :      return GetBadAccessKind(addr, header) == USE_AFTER_FREE;
  57  E :    }
  58    :  
  59    :    // Determines if the address @p mem corresponds to a block in quarantine.
  60  E :    bool InQuarantine(const void* mem) {
  61  E :      base::AutoLock lock(lock_);
  62  E :      FreeBlockHeader* current_block = head_;
  63  E :      while (current_block != NULL) {
  64  E :        void* block_alloc = static_cast<void*>(ToAlloc(current_block));
  65  E :        EXPECT_TRUE(block_alloc != NULL);
  66  E :        if (block_alloc == mem)
  67  E :          return true;
  68  E :        current_block = current_block->next;
  69  E :      }
  70  E :      return false;
  71  E :    }
  72    :  };
  73    :  
  74    :  class HeapTest : public testing::TestWithAsanLogger {
  75    :   public:
  76  E :    HeapTest() : stack_cache_(&logger_), proxy_(&stack_cache_, &logger_) {
  77  E :    }
  78    :  
  79  E :    virtual void SetUp() OVERRIDE {
  80  E :      testing::TestWithAsanLogger::SetUp();
  81    :  
  82  E :      logger_.set_instance_id(instance_id());
  83  E :      logger_.Init();
  84  E :      ASSERT_TRUE(proxy_.Create(0, 0, 0));
  85  E :    }
  86    :  
  87  E :    virtual void TearDown() OVERRIDE {
  88  E :      ASSERT_TRUE(proxy_.Destroy());
  89  E :      testing::TestWithAsanLogger::TearDown();
  90  E :    }
  91    :  
  92    :    // Verifies that [alloc, alloc + size) is accessible, and that
  93    :    // [alloc - 1] and [alloc+size] are poisoned.
  94  E :    void VerifyAllocAccess(void* alloc, size_t size) {
  95  E :      uint8* mem = reinterpret_cast<uint8*>(alloc);
  96  E :      ASSERT_FALSE(Shadow::IsAccessible(mem - 1));
  97  E :      for (size_t i = 0; i < size; ++i)
  98  E :        ASSERT_TRUE(Shadow::IsAccessible(mem + i));
  99  E :      ASSERT_FALSE(Shadow::IsAccessible(mem + size));
 100  E :    }
 101    :  
 102    :    // Verifies that [alloc-1, alloc+size] is poisoned.
 103  E :    void VerifyFreedAccess(void* alloc, size_t size) {
 104  E :      uint8* mem = reinterpret_cast<uint8*>(alloc);
 105  E :      ASSERT_FALSE(Shadow::IsAccessible(mem - 1));
 106  E :      for (size_t i = 0; i < size; ++i)
 107  E :        ASSERT_FALSE(Shadow::IsAccessible(mem + i));
 108  E :      ASSERT_FALSE(Shadow::IsAccessible(mem + size));
 109  E :    }
 110    :  
 111  E :    void RandomSetMemory(void* alloc, size_t size) {
 112  E :      base::RandBytes(alloc, size);
 113  E :    }
 114    :  
 115    :   protected:
 116    :    // Arbitrary constant for all size limit.
 117    :    static const size_t kMaxAllocSize = 134584;
 118    :  
 119    :    AsanLogger logger_;
 120    :    StackCaptureCache stack_cache_;
 121    :    TestHeapProxy proxy_;
 122    :  };
 123    :  
 124    :  }  // namespace
 125    :  
 126  E :  TEST_F(HeapTest, ToFromHandle) {
 127  E :    HANDLE handle = HeapProxy::ToHandle(&proxy_);
 128  E :    ASSERT_TRUE(handle != NULL);
 129  E :    ASSERT_EQ(&proxy_, HeapProxy::FromHandle(handle));
 130  E :  }
 131    :  
 132  E :  TEST_F(HeapTest, SetQuarantineMaxSize) {
 133  E :    size_t quarantine_size = proxy_.quarantine_max_size() * 2;
 134    :    // Increments the quarantine max size if it was set to 0.
 135  E :    if (quarantine_size == 0)
 136  i :      quarantine_size++;
 137  E :    DCHECK_GT(quarantine_size, 0U);
 138  E :    proxy_.set_quarantine_max_size(quarantine_size);
 139  E :    ASSERT_EQ(quarantine_size, proxy_.quarantine_max_size());
 140  E :  }
 141    :  
 142  E :  TEST_F(HeapTest, PopOnSetQuarantineMaxSize) {
 143  E :    const size_t kAllocSize = 100;
 144  E :    const size_t real_alloc_size = TestHeapProxy::GetAllocSize(kAllocSize);
 145  E :    LPVOID mem = proxy_.Alloc(0, kAllocSize);
 146  E :    ASSERT_FALSE(proxy_.InQuarantine(mem));
 147  E :    proxy_.set_quarantine_max_size(real_alloc_size);
 148  E :    ASSERT_TRUE(proxy_.Free(0, mem));
 149    :    // The quarantine is just large enough to keep this block.
 150  E :    ASSERT_TRUE(proxy_.InQuarantine(mem));
 151    :    // We resize the quarantine to a smaller size, the block should pop out.
 152  E :    proxy_.set_quarantine_max_size(real_alloc_size - 1);
 153  E :    ASSERT_FALSE(proxy_.InQuarantine(mem));
 154  E :  }
 155    :  
 156  E :  TEST_F(HeapTest, Quarantine) {
 157  E :    const size_t kAllocSize = 100;
 158  E :    const size_t real_alloc_size = TestHeapProxy::GetAllocSize(kAllocSize);
 159  E :    const size_t number_of_allocs = 16;
 160  E :    proxy_.set_quarantine_max_size(real_alloc_size * number_of_allocs);
 161    :  
 162  E :    LPVOID mem = proxy_.Alloc(0, kAllocSize);
 163  E :    ASSERT_TRUE(mem != NULL);
 164  E :    ASSERT_TRUE(proxy_.Free(0, mem));
 165    :    // Allocate a bunch of blocks until the first one is pushed out of the
 166    :    // quarantine.
 167  E :    for (size_t i = 0;i < number_of_allocs; ++i) {
 168  E :      ASSERT_TRUE(proxy_.InQuarantine(mem));
 169  E :      LPVOID mem2 = proxy_.Alloc(0, kAllocSize);
 170  E :      ASSERT_TRUE(mem2 != NULL);
 171  E :      ASSERT_TRUE(proxy_.Free(0, mem2));
 172  E :      ASSERT_TRUE(proxy_.InQuarantine(mem2));
 173  E :    }
 174    :  
 175  E :    ASSERT_FALSE(proxy_.InQuarantine(mem));
 176  E :  }
 177    :  
 178  E :  TEST_F(HeapTest, AllocFree) {
 179  E :    const size_t kAllocSize = 100;
 180  E :    LPVOID mem = proxy_.Alloc(0, kAllocSize);
 181  E :    ASSERT_TRUE(mem != NULL);
 182  E :    ASSERT_EQ(kAllocSize, proxy_.Size(0, mem));
 183  E :    const size_t kReAllocSize = 2 * kAllocSize;
 184  E :    mem = proxy_.ReAlloc(0, mem, kReAllocSize);
 185  E :    ASSERT_EQ(kReAllocSize, proxy_.Size(0, mem));
 186  E :    ASSERT_TRUE(proxy_.Free(0, mem));
 187  E :  }
 188    :  
 189  E :  TEST_F(HeapTest, DoubleFree) {
 190  E :    const size_t kAllocSize = 100;
 191    :    // Ensure that the quarantine is large enough to keep this block, this is
 192    :    // needed for the use-after-free check.
 193  E :    proxy_.set_quarantine_max_size(TestHeapProxy::GetAllocSize(kAllocSize));
 194  E :    LPVOID mem = proxy_.Alloc(0, kAllocSize);
 195  E :    ASSERT_TRUE(mem != NULL);
 196  E :    ASSERT_TRUE(proxy_.Free(0, mem));
 197  E :    ASSERT_FALSE(proxy_.Free(0, mem));
 198  E :    ASSERT_TRUE(LogContains("double-free"));
 199  E :  }
 200    :  
 201  E :  TEST_F(HeapTest, AllocsAccessibility) {
 202    :    // Ensure that the quarantine is large enough to keep the allocated blocks in
 203    :    // this test.
 204  E :    proxy_.set_quarantine_max_size(kMaxAllocSize * 2);
 205  E :    for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
 206    :      // Do an alloc/realloc/free and test that access is correctly managed.
 207  E :      void* mem = proxy_.Alloc(0, size);
 208  E :      ASSERT_TRUE(mem != NULL);
 209  E :      ASSERT_NO_FATAL_FAILURE(VerifyAllocAccess(mem, size));
 210  E :      RandomSetMemory(mem, size);
 211    :  
 212  E :      size_t new_size = size;
 213  E :      while (new_size == size)
 214  E :        new_size = base::RandInt(size / 2, size * 2);
 215    :  
 216  E :      unsigned char sha1_before[base::kSHA1Length] = {};
 217    :      base::SHA1HashBytes(reinterpret_cast<unsigned char*>(mem),
 218    :                          std::min(size, new_size),
 219  E :                          sha1_before);
 220    :  
 221  E :      void* new_mem = proxy_.ReAlloc(0, mem, size * 2);
 222  E :      ASSERT_TRUE(new_mem != NULL);
 223  E :      ASSERT_NE(mem, new_mem);
 224    :  
 225  E :      unsigned char sha1_after[base::kSHA1Length] = {};
 226    :      base::SHA1HashBytes(reinterpret_cast<unsigned char*>(new_mem),
 227    :                          std::min(size, new_size),
 228  E :                          sha1_after);
 229  E :      ASSERT_EQ(0, memcmp(sha1_before, sha1_after, base::kSHA1Length));
 230    :  
 231  E :      ASSERT_NO_FATAL_FAILURE(VerifyFreedAccess(mem, size));
 232  E :      ASSERT_NO_FATAL_FAILURE(VerifyAllocAccess(new_mem, size * 2));
 233    :  
 234  E :      ASSERT_TRUE(proxy_.Free(0, new_mem));
 235  E :      ASSERT_NO_FATAL_FAILURE(VerifyFreedAccess(new_mem, size * 2));
 236  E :    }
 237  E :  }
 238    :  
 239  E :  TEST_F(HeapTest, Size) {
 240  E :    for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
 241  E :      void* mem = proxy_.Alloc(0, size);
 242  E :      ASSERT_FALSE(mem == NULL);
 243  E :      ASSERT_EQ(size, proxy_.Size(0, mem));
 244  E :      ASSERT_TRUE(proxy_.Free(0, mem));
 245  E :    }
 246  E :  }
 247    :  
 248  E :  TEST_F(HeapTest, Validate) {
 249  E :    for (size_t size = 10; size < kMaxAllocSize; size = size * 5 + 123) {
 250  E :      void* mem = proxy_.Alloc(0, size);
 251  E :      ASSERT_FALSE(mem == NULL);
 252  E :      ASSERT_TRUE(proxy_.Validate(0, mem));
 253  E :      ASSERT_TRUE(proxy_.Free(0, mem));
 254  E :    }
 255  E :  }
 256    :  
 257  E :  TEST_F(HeapTest, Compact) {
 258    :    // Compact should return a non-zero size.
 259  E :    ASSERT_LT(0U, proxy_.Compact(0));
 260    :  
 261    :    // TODO(siggi): It may not be possible to allocate the size returned due
 262    :    //     to padding - fix and test.
 263  E :  }
 264    :  
 265  E :  TEST_F(HeapTest, LockUnlock) {
 266    :    // We can't really test these, aside from not crashing.
 267  E :    ASSERT_TRUE(proxy_.Lock());
 268  E :    ASSERT_TRUE(proxy_.Unlock());
 269  E :  }
 270    :  
 271  E :  TEST_F(HeapTest, Walk) {
 272    :    // We assume at least two entries to walk through.
 273  E :    PROCESS_HEAP_ENTRY entry = {};
 274  E :    ASSERT_TRUE(proxy_.Walk(&entry));
 275  E :    ASSERT_TRUE(proxy_.Walk(&entry));
 276  E :  }
 277    :  
 278  E :  TEST_F(HeapTest, SetQueryInformation) {
 279  E :    ULONG compat_flag = -1;
 280  E :    unsigned long ret = 0;
 281    :    // Get the current value of the compat flag.
 282    :    ASSERT_TRUE(
 283    :        proxy_.QueryInformation(HeapCompatibilityInformation,
 284  E :                                &compat_flag, sizeof(compat_flag), &ret));
 285  E :    ASSERT_EQ(sizeof(compat_flag), ret);
 286  E :    ASSERT_TRUE(compat_flag != -1);
 287    :  
 288    :    // Put the heap in LFH, which should always succeed, except when a debugger
 289    :    // is attached. When a debugger is attached, the heap is wedged in certain
 290    :    // debug settings.
 291  E :    if (base::debug::BeingDebugged()) {
 292  i :      LOG(WARNING) << "Can't test HeapProxy::SetInformation under debugger.";
 293  i :      return;
 294    :    }
 295    :  
 296  E :    compat_flag = 2;
 297    :    ASSERT_TRUE(
 298    :        proxy_.SetInformation(HeapCompatibilityInformation,
 299  E :                              &compat_flag, sizeof(compat_flag)));
 300  E :  }
 301    :  
 302  E :  TEST_F(HeapTest, FindAddressBlock) {
 303  E :    const size_t kAllocSize = 100;
 304  E :    void* mem = proxy_.Alloc(0, kAllocSize);
 305  E :    ASSERT_FALSE(mem == NULL);
 306  E :    ASSERT_FALSE(proxy_.FindAddressBlock(static_cast<const uint8*>(mem)) == NULL);
 307    :    uint8* out_of_bounds_address =
 308  E :        static_cast<uint8*>(mem) + kAllocSize * 2;
 309  E :    ASSERT_TRUE(proxy_.FindAddressBlock(out_of_bounds_address) == NULL);
 310  E :    ASSERT_TRUE(proxy_.Free(0, mem));
 311  E :  }
 312    :  
 313  E :  TEST_F(HeapTest, GetBadAccessKind) {
 314  E :    const size_t kAllocSize = 100;
 315    :    // Ensure that the quarantine is large enough to keep this block, this is
 316    :    // needed for the use-after-free check.
 317  E :    proxy_.set_quarantine_max_size(TestHeapProxy::GetAllocSize(kAllocSize));
 318  E :    uint8* mem = static_cast<uint8*>(proxy_.Alloc(0, kAllocSize));
 319  E :    ASSERT_FALSE(mem == NULL);
 320    :    TestHeapProxy::BlockHeader* header =
 321  E :        const_cast<TestHeapProxy::BlockHeader*>(proxy_.ToBlock(mem));
 322  E :    uint8* heap_underflow_address = mem - 1;
 323  E :    uint8* heap_overflow_address = mem + kAllocSize * sizeof(uint8);
 324  E :    ASSERT_TRUE(proxy_.IsUnderflowAccess(heap_underflow_address, header));
 325  E :    ASSERT_TRUE(proxy_.IsOverflowAccess(heap_overflow_address, header));
 326  E :    ASSERT_TRUE(proxy_.Free(0, mem));
 327  E :    ASSERT_TRUE(proxy_.IsUseAfterAccess(mem, header));
 328  E :  }
 329    :  
 330    :  }  // namespace asan
 331    :  }  // namespace agent

Coverage information generated Thu Mar 14 11:53:36 2013.