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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
99.3%4114140.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/block.h"
  16    :  
  17    :  #include <set>
  18    :  
  19    :  #include "windows.h"
  20    :  
  21    :  #include "base/memory/scoped_ptr.h"
  22    :  #include "gtest/gtest.h"
  23    :  #include "syzygy/agent/asan/asan_runtime.h"
  24    :  #include "syzygy/agent/asan/page_protection_helpers.h"
  25    :  
  26    :  namespace agent {
  27    :  namespace asan {
  28    :  
  29    :  namespace {
  30    :  
  31    :  BlockLayout BuildBlockLayout(size_t block_alignment,
  32    :                               size_t block_size,
  33    :                               size_t header_size,
  34    :                               size_t header_padding_size,
  35    :                               size_t body_size,
  36    :                               size_t trailer_padding_size,
  37  E :                               size_t trailer_size) {
  38    :    BlockLayout layout = { block_alignment, block_size, header_size,
  39  E :        header_padding_size, body_size, trailer_padding_size, trailer_size };
  40  E :    return layout;
  41  E :  }
  42    :  
  43    :  // Checks that the given block is valid, and initialized as expected.
  44  E :  void IsValidBlockImpl(const BlockInfo& block, bool just_initialized) {
  45  E :    EXPECT_EQ(0u, block.block_size % kShadowRatio);
  46    :  
  47    :    // Validate the layout of the block.
  48  E :    EXPECT_TRUE(block.block != NULL);
  49  E :    EXPECT_EQ(0u, block.block_size % kShadowRatio);
  50  E :    EXPECT_EQ(block.block, reinterpret_cast<void*>(block.header));
  51  E :    EXPECT_EQ(0u, block.header_padding_size % kShadowRatio);
  52    :    EXPECT_EQ(reinterpret_cast<uint8*>(block.header + 1),
  53  E :              block.header_padding);
  54    :    EXPECT_EQ(block.header_padding + block.header_padding_size,
  55  E :              block.body);
  56    :    EXPECT_EQ(reinterpret_cast<uint8*>(block.body + block.body_size),
  57  E :              block.trailer_padding);
  58    :    EXPECT_EQ(block.trailer_padding + block.trailer_padding_size,
  59  E :              reinterpret_cast<uint8*>(block.trailer));
  60    :    EXPECT_EQ(block.block + block.block_size,
  61  E :              reinterpret_cast<uint8*>(block.trailer + 1));
  62    :  
  63    :    // Validate the actual contents of the various parts of the block.
  64    :  
  65    :    // Check the header.
  66  E :    EXPECT_EQ(kBlockHeaderMagic, block.header->magic);
  67  E :    EXPECT_FALSE(block.header->is_nested);
  68  E :    EXPECT_LT(0u, block.header->body_size);
  69  E :    EXPECT_EQ(block.header->body_size, block.body_size);
  70  E :    if (just_initialized) {
  71  E :      EXPECT_EQ(0u, block.header->checksum);
  72  E :      EXPECT_EQ(NULL, block.header->alloc_stack);
  73  E :      EXPECT_EQ(NULL, block.header->free_stack);
  74  E :      EXPECT_EQ(ALLOCATED_BLOCK, block.header->state);
  75    :    }
  76    :  
  77    :    // By default we assume the blocks to not be nested.
  78  E :    EXPECT_FALSE(block.header->is_nested);
  79  E :    EXPECT_EQ(static_cast<bool>(block.header->is_nested), block.is_nested);
  80    :  
  81    :    // Check the header padding.
  82  E :    if (block.header->has_header_padding) {
  83  E :      EXPECT_LE(kShadowRatio, block.header_padding_size);
  84    :      EXPECT_EQ(block.header_padding_size,
  85  E :                *reinterpret_cast<const uint32*>(block.header_padding));
  86    :      EXPECT_EQ(block.header_padding_size,
  87    :                *reinterpret_cast<const uint32*>(block.header_padding +
  88  E :                    block.header_padding_size - sizeof(uint32)));
  89  E :      for (size_t i = sizeof(uint32);
  90  E :           i < block.header_padding_size - sizeof(uint32);
  91  E :           ++i) {
  92  E :        EXPECT_EQ(kBlockHeaderPaddingByte, block.header_padding[i]);
  93  E :      }
  94    :    }
  95    :  
  96    :    // Check the trailer padding.
  97  E :    size_t start_of_trailer_iteration = 0;
  98  E :    if (block.header->has_excess_trailer_padding) {
  99  E :      start_of_trailer_iteration = 4;
 100    :      EXPECT_EQ(block.trailer_padding_size,
 101  E :                *reinterpret_cast<const uint32*>(block.trailer_padding));
 102    :    }
 103  E :    for (size_t i = start_of_trailer_iteration; i < block.trailer_padding_size;
 104  E :         ++i) {
 105  E :      EXPECT_EQ(kBlockTrailerPaddingByte, block.trailer_padding[i]);
 106  E :    }
 107    :  
 108    :    // Check the trailer.
 109  E :    EXPECT_NE(0u, block.trailer->alloc_tid);
 110  E :    EXPECT_GE(::GetTickCount(), block.trailer->alloc_ticks);
 111  E :    if (just_initialized) {
 112  E :      EXPECT_EQ(0u, block.trailer->free_tid);
 113  E :      EXPECT_EQ(0u, block.trailer->free_ticks);
 114    :    }
 115  E :  }
 116    :  
 117  E :  void IsValidInitializedBlock(const BlockInfo& block) {
 118  E :    IsValidBlockImpl(block, true);
 119  E :  }
 120    :  
 121    :  void IsValidBlock(const BlockInfo& block) {
 122    :    IsValidBlockImpl(block, false);
 123    :  }
 124    :  
 125    :  }  // namespace
 126    :  
 127  E :  bool operator==(const BlockLayout& bl1, const BlockLayout& bl2) {
 128  E :    return ::memcmp(&bl1, &bl2, sizeof(BlockLayout)) == 0;
 129  E :  }
 130    :  
 131  E :  bool operator==(const BlockInfo& bi1, const BlockInfo& bi2) {
 132  E :    return ::memcmp(&bi1, &bi2, sizeof(BlockInfo)) == 0;
 133  E :  }
 134    :  
 135  E :  TEST(BlockTest, BlockPlanLayout) {
 136  E :    BlockLayout layout = {};
 137    :  
 138    :    // Zero sized allocations should work fine.
 139  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 0, 0, 0, &layout));
 140  E :    EXPECT_EQ(BuildBlockLayout(8, 40, 16, 0, 0, 4, 20), layout);
 141    :  
 142  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 60, 32, 32, &layout));
 143  E :    EXPECT_EQ(BuildBlockLayout(8, 128, 16, 16, 60, 16, 20), layout);
 144    :  
 145  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 60, 0, 0, &layout));
 146  E :    EXPECT_EQ(BuildBlockLayout(8, 96, 16, 0, 60, 0, 20), layout);
 147    :  
 148  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 64, 0, 0, &layout));
 149  E :    EXPECT_EQ(BuildBlockLayout(8, 104, 16, 0, 64, 4, 20), layout);
 150    :  
 151  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 61, 0, 0, &layout));
 152  E :    EXPECT_EQ(BuildBlockLayout(8, 104, 16, 0, 61, 7, 20), layout);
 153    :  
 154    :    // Plan a layout that would use guard pages.
 155  E :    EXPECT_TRUE(BlockPlanLayout(4096, 8, 100, 4096, 4096, &layout));
 156  E :    EXPECT_EQ(BuildBlockLayout(4096, 3 * 4096, 16, 8072, 100, 4080, 20), layout);
 157    :  
 158    :    // Plan a layout with an invalid size, this should fail.
 159  E :    EXPECT_FALSE(BlockPlanLayout(8, 8, 0xffffffff, 0, 0, &layout));
 160  E :  }
 161    :  
 162  E :  TEST(BlockTest, EndToEnd) {
 163  E :    BlockLayout layout = {};
 164  E :    BlockInfo block_info = {};
 165    :  
 166  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 4, 0, 0, &layout));
 167  E :    scoped_ptr<uint8> block_data(new uint8[layout.block_size]);
 168  E :    ::memset(block_data.get(), 0, layout.block_size);
 169  E :    ASSERT_TRUE(block_data != NULL);
 170  E :    BlockInitialize(layout, block_data.get(), false, &block_info);
 171  E :    EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
 172  E :    block_data.reset(NULL);
 173    :  
 174  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 61, 0, 0, &layout));
 175  E :    block_data.reset(new uint8[layout.block_size]);
 176  E :    ::memset(block_data.get(), 0, layout.block_size);
 177  E :    ASSERT_TRUE(block_data != NULL);
 178  E :    BlockInitialize(layout, block_data.get(), false, &block_info);
 179  E :    EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
 180  E :    block_data.reset(NULL);
 181    :  
 182  E :    EXPECT_TRUE(BlockPlanLayout(8, 8, 60, 32, 32, &layout));
 183  E :    block_data.reset(new uint8[layout.block_size]);
 184  E :    ::memset(block_data.get(), 0, layout.block_size);
 185  E :    ASSERT_TRUE(block_data != NULL);
 186  E :    BlockInitialize(layout, block_data.get(), false, &block_info);
 187  E :    EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
 188  E :    block_data.reset(NULL);
 189    :  
 190    :    // Do an allocation that uses entire pages.
 191  E :    EXPECT_TRUE(BlockPlanLayout(4096, 8, 100, 4096, 4096, &layout));
 192    :    void* data = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
 193  E :                                PAGE_READWRITE);
 194  E :    ::memset(data, 0, layout.block_size);
 195  E :    ASSERT_TRUE(data != NULL);
 196  E :    BlockInitialize(layout, data, false, &block_info);
 197  E :    EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
 198  E :    ASSERT_EQ(TRUE, ::VirtualFree(data, 0, MEM_RELEASE));
 199  E :  }
 200    :  
 201  E :  TEST(BlockTest, GetHeaderFromBody) {
 202    :    // Plan two layouts, one with header padding and another without.
 203  E :    BlockLayout layout1 = {};
 204  E :    BlockLayout layout2 = {};
 205  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout1));
 206  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 32, 0, &layout2));
 207    :  
 208  E :    scoped_ptr<uint8> data(new uint8[layout2.block_size]);
 209  E :    ::memset(data.get(), 0, layout2.block_size);
 210    :  
 211    :    // First try navigating a block without header padding.
 212  E :    BlockInfo info = {};
 213  E :    BlockInitialize(layout1, data.get(), false, &info);
 214    :    // This should succeed as expected.
 215  E :    EXPECT_EQ(info.header, BlockGetHeaderFromBody(info.body));
 216    :    // This fails because of invalid alignment.
 217  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 1) == NULL);
 218    :    // This fails because the pointer is not at the beginning of the
 219    :    // body.
 220  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 8) == NULL);
 221    :    // This fails because of invalid header magic.
 222  E :    ++info.header->magic;
 223  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
 224    :    // This fails because the header indicates there's padding.
 225  E :    --info.header->magic;
 226  E :    info.header->has_header_padding = 1;
 227  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
 228    :  
 229    :    // Now navigate a block with header padding.
 230  E :    BlockInitialize(layout2, data.get(), false, &info);
 231    :    // This should succeed as expected.
 232  E :    EXPECT_EQ(info.header, BlockGetHeaderFromBody(info.body));
 233    :    // This fails because of invalid alignment.
 234  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 1) == NULL);
 235    :    // This fails because the pointer is not at the beginning of the
 236    :    // body.
 237  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 8) == NULL);
 238    :    // This fails because of invalid header magic.
 239  E :    ++info.header->magic;
 240  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
 241    :    // This fails because the header indicates there's no padding.
 242  E :    --info.header->magic;
 243  E :    info.header->has_header_padding = 0;
 244  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
 245    :    // This fails because the padding length is invalid.
 246  E :    info.header->has_header_padding = 1;
 247  E :    uint32* head = reinterpret_cast<uint32*>(info.header_padding);
 248  E :    uint32* tail = head + (info.header_padding_size / sizeof(uint32)) - 1;
 249  E :    ++(*tail);
 250  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
 251    :    // This fails because the padding lengths don't agree.
 252  E :    --(*tail);
 253  E :    ++(*head);
 254  E :    EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
 255  E :  }
 256    :  
 257  E :  TEST(BlockTest, GetHeaderFromBodyProtectedMemory) {
 258  E :    BlockLayout layout = {};
 259  E :    EXPECT_TRUE(BlockPlanLayout(4096, 4096, 4096, 4096, 4096, &layout));
 260    :    void* alloc = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
 261  E :                                 PAGE_READWRITE);
 262  E :    ASSERT_TRUE(alloc != NULL);
 263  E :    BlockInfo block_info = {};
 264  E :    BlockInitialize(layout, alloc, false, &block_info);
 265    :  
 266  E :    BlockProtectRedzones(block_info);
 267  E :    EXPECT_TRUE(BlockGetHeaderFromBody(block_info.body) == NULL);
 268  E :    BlockProtectNone(block_info);
 269    :  
 270  E :    ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
 271  E :  }
 272    :  
 273  E :  TEST(BlockTest, ConvertBlockInfo) {
 274  E :    BlockLayout layout = {};
 275  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout));
 276    :  
 277  E :    scoped_ptr<uint8> data(new uint8[layout.block_size]);
 278  E :    ::memset(data.get(), 0, layout.block_size);
 279    :  
 280  E :    BlockInfo expanded = {};
 281  E :    BlockInitialize(layout, data.get(), false, &expanded);
 282    :  
 283  E :    CompactBlockInfo compact = {};
 284  E :    ConvertBlockInfo(expanded, &compact);
 285  E :    EXPECT_EQ(layout.block_size, compact.block_size);
 286    :    EXPECT_EQ(layout.header_size + layout.header_padding_size,
 287  E :              compact.header_size);
 288    :    EXPECT_EQ(layout.trailer_size + layout.trailer_padding_size,
 289  E :              compact.trailer_size);
 290  E :    EXPECT_FALSE(compact.is_nested);
 291    :  
 292  E :    BlockInfo expanded2 = {};
 293  E :    ConvertBlockInfo(compact, &expanded2);
 294  E :    EXPECT_EQ(0, ::memcmp(&expanded, &expanded2, sizeof(expanded)));
 295  E :  }
 296    :  
 297  E :  TEST(BlockTest, BlockInfoFromMemory) {
 298    :    // Plan two layouts, one with header padding and another without.
 299  E :    BlockLayout layout1 = {};
 300  E :    BlockLayout layout2 = {};
 301  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout1));
 302  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 32, 0, &layout2));
 303    :  
 304  E :    scoped_ptr<uint8> data(new uint8[layout2.block_size]);
 305  E :    ::memset(data.get(), 0, layout2.block_size);
 306    :  
 307    :    // First recover a block without header padding.
 308  E :    BlockInfo info = {};
 309  E :    BlockInitialize(layout1, data.get(), false, &info);
 310  E :    BlockInfo info_recovered = {};
 311  E :    EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
 312  E :    EXPECT_EQ(info, info_recovered);
 313    :    // Failed because its not aligned.
 314  E :    EXPECT_FALSE(BlockInfoFromMemory(info.block + 1, &info_recovered));
 315    :    // Failed because the magic is invalid.
 316  E :    ++info.header->magic;
 317  E :    EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
 318  E :    --info.header->magic;
 319    :    // This fails because the header indicates there's padding yet there is
 320    :    // none.
 321  E :    info.header->has_header_padding = 1;
 322  E :    EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
 323    :  
 324    :    // Now recover a block with header padding.
 325  E :    BlockInitialize(layout2, data.get(), false, &info);
 326  E :    EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
 327  E :    EXPECT_EQ(info, info_recovered);
 328    :    // Failed because the magic is invalid.
 329  E :    ++info.header->magic;
 330  E :    EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
 331  E :    --info.header->magic;
 332    :    // Failed because the header padding lengths don't match.
 333  E :    uint32* head = reinterpret_cast<uint32*>(info.header_padding);
 334  E :    uint32* tail = head + (info.header_padding_size / sizeof(uint32)) - 1;
 335  E :    ++(*tail);
 336  E :    EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
 337  E :    --(*tail);
 338    :  
 339    :    // Finally ensure that we can recover information about blocks of various
 340    :    // sizes.
 341  E :    const size_t kAllocSize = 3 * GetPageSize();
 342  E :    void* alloc = ::VirtualAlloc(NULL, kAllocSize, MEM_COMMIT, PAGE_READWRITE);
 343  E :    for (size_t block_size = 0; block_size < kShadowRatio * 2; ++block_size) {
 344  E :      BlockLayout layout = {};
 345    :      EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, block_size, 0, 0,
 346  E :                                  &layout));
 347  E :      ASSERT_LE(layout.block_size, kAllocSize);
 348  E :      BlockInitialize(layout, alloc, false, &info);
 349  E :      EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
 350  E :      EXPECT_EQ(info.body_size, info_recovered.body_size);
 351  E :      EXPECT_EQ(info, info_recovered);
 352    :  
 353    :      EXPECT_TRUE(BlockPlanLayout(4096, 4096, block_size, 4096, 4096,
 354  E :                                  &layout));
 355  E :      ASSERT_LE(layout.block_size, kAllocSize);
 356  E :      BlockInitialize(layout, alloc, false, &info);
 357  E :      EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
 358  E :      EXPECT_EQ(info.body_size, info_recovered.body_size);
 359  E :      EXPECT_EQ(info, info_recovered);
 360  E :    }
 361  E :    ::VirtualFree(alloc, 0, MEM_RELEASE);
 362  E :  }
 363    :  
 364  E :  TEST(BlockTest, BlockInfoFromMemoryInvalidPadding) {
 365  E :    BlockLayout layout = {};
 366    :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10,
 367  E :        4 * sizeof(BlockHeader), 0, &layout));
 368    :  
 369  E :    scoped_ptr<uint8> data(new uint8[layout.block_size]);
 370  E :    ::memset(data.get(), 0, layout.block_size);
 371    :  
 372  E :    BlockInfo info = {};
 373  E :    BlockInitialize(layout, data.get(), false, &info);
 374  E :    EXPECT_TRUE(info.header->has_header_padding = 1);
 375  E :    BlockInfo info_recovered = {};
 376  E :    EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
 377  E :    EXPECT_EQ(info, info_recovered);
 378    :  
 379    :    // Invalidates the padding size and make sure that we can't retrieve the block
 380    :    // information.
 381  E :    size_t* padding_size = reinterpret_cast<size_t*>(info.header + 1);
 382  E :    EXPECT_GE(*padding_size, 2 * sizeof(uint32));
 383  E :    for (*padding_size = 0;
 384  E :         *padding_size < 2 * sizeof(uint32);
 385  E :         ++(*padding_size)) {
 386  E :      EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
 387  E :    }
 388  E :  }
 389    :  
 390  E :  TEST(BlockTest, BlockInfoFromMemoryProtectedMemory) {
 391  E :    BlockLayout layout = {};
 392  E :    EXPECT_TRUE(BlockPlanLayout(4096, 4096, 4096, 4096, 4096, &layout));
 393    :    void* alloc = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
 394  E :                                 PAGE_READWRITE);
 395  E :    ASSERT_TRUE(alloc != NULL);
 396  E :    BlockInfo block_info = {};
 397  E :    BlockInitialize(layout, alloc, false, &block_info);
 398    :  
 399  E :    BlockProtectRedzones(block_info);
 400  E :    BlockInfo recovered_info = {};
 401  E :    EXPECT_FALSE(BlockInfoFromMemory(block_info.block, &recovered_info));
 402  E :    BlockProtectNone(block_info);
 403    :  
 404  E :    ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
 405  E :  }
 406    :  
 407  E :  TEST(BlockTest, BlockInfoFromMemoryForNestedBlock) {
 408  E :    BlockLayout layout = {};
 409  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout));
 410    :  
 411  E :    scoped_ptr<uint8> data(new uint8[layout.block_size]);
 412  E :    BlockInfo block_info = {};
 413  E :    BlockInitialize(layout, data.get(), true, &block_info);
 414    :  
 415  E :    BlockInfo recovered_info = {};
 416  E :    EXPECT_TRUE(BlockInfoFromMemory(block_info.block, &recovered_info));
 417    :  
 418  E :    EXPECT_TRUE(recovered_info.is_nested);
 419  E :    EXPECT_TRUE(recovered_info.header->is_nested);
 420  E :  }
 421    :  
 422  E :  TEST(BlockTest, ChecksumWorksForAllStates) {
 423  E :    BlockLayout layout = {};
 424  E :    EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout));
 425  E :    scoped_ptr<uint8> data(new uint8[layout.block_size]);
 426  E :    ::memset(data.get(), 0, layout.block_size);
 427  E :    BlockInfo info = {};
 428  E :    BlockInitialize(layout, data.get(), false, &info);
 429  E :    while (true) {
 430  E :      BlockCalculateChecksum(info);
 431  E :      ++info.header->state;
 432  E :      if (info.header->state == 0)
 433  E :        break;
 434  E :    }
 435  E :  }
 436    :  
 437    :  namespace {
 438    :  
 439    :  // Given two arrays of data, compares them byte-by-byte to find the first
 440    :  // byte with altered data. Within that byte determines the mask of bits that
 441    :  // have been altered. Returns the results via |offset| and |mask|.
 442    :  void FindModifiedBits(size_t length,
 443    :                        const uint8* buffer1,
 444    :                        const uint8* buffer2,
 445    :                        size_t* offset,
 446  E :                        uint8* mask) {
 447  E :    ASSERT_TRUE(buffer1 != NULL);
 448  E :    ASSERT_TRUE(buffer2 != NULL);
 449  E :    ASSERT_TRUE(offset != NULL);
 450  E :    ASSERT_TRUE(mask != NULL);
 451    :  
 452  E :    for (size_t i = 0; i < length; ++i) {
 453  E :      if (buffer1[i] != buffer2[i]) {
 454  E :        *offset = i;
 455  E :        *mask = buffer1[i] ^ buffer2[i];
 456  E :        return;
 457    :      }
 458  E :    }
 459    :  
 460  i :    *offset = 0;
 461  i :    *mask = 0;
 462  E :  }
 463    :  
 464    :  // This is initialized by TestChecksumDetectsTampering, but referred to by
 465    :  // ChecksumDetectsTamperingWithMask as well, hence not in a function.
 466    :  size_t state_offset = -1;
 467    :  uint8 state_mask = 0;
 468    :  
 469    :  bool ChecksumDetectsTamperingWithMask(const BlockInfo& block_info,
 470    :                                        void* address_to_modify,
 471  E :                                        uint8 mask_to_modify) {
 472  E :    uint8* byte_to_modify = reinterpret_cast<uint8*>(address_to_modify);
 473    :  
 474    :    // Remember the original contents.
 475  E :    uint8 original_value = *byte_to_modify;
 476  E :    uint8 original_bits = original_value & ~mask_to_modify;
 477    :  
 478    :    // Since the checksum can collide we check a handful of times to build up
 479    :    // some confidence. Since we sometimes expect this to return false the number
 480    :    // of iterations needs to be kept reasonably low to keep the unittest fast.
 481  E :    bool detected = false;
 482  E :    BlockSetChecksum(block_info);
 483  E :    uint32 checksum = block_info.header->checksum;
 484  E :    for (size_t i = 0; i < 4; ++i) {
 485    :      // Modify the value, altering only bits in |mask_to_modify|.
 486  E :      while (true) {
 487  E :        ++(*byte_to_modify);
 488  E :        if (((*byte_to_modify) & ~mask_to_modify) == original_bits)
 489  E :          break;
 490  i :      }
 491  E :      BlockSetChecksum(block_info);
 492  E :      if (block_info.header->checksum != checksum) {
 493    :        // Success, the checksum detected the change!
 494    :        // Restore the original checksum so the block analysis can continue.
 495  E :        block_info.header->checksum = checksum;
 496  E :        detected = true;
 497  E :        break;
 498    :      }
 499  E :    }
 500    :  
 501    :    // Run a detailed analysis on the block. We expect the results of this to
 502    :    // agree with where the block was modified.
 503  E :    BlockAnalysisResult result = {};
 504  E :    BlockAnalyze(block_info, &result);
 505  E :    if (address_to_modify < block_info.body) {
 506  E :      EXPECT_EQ(kDataIsCorrupt, result.block_state);
 507    :      // If the thing being modified is the block state, then this is so
 508    :      // localized that the analysis will sometimes mess up. Seeing this in
 509    :      // the wild is quite unlikely.
 510    :      // TODO(chrisha): If we ever have individual checksums for the header
 511    :      // the body and the trailer, then revisit this.
 512    :      if (address_to_modify != block_info.block + state_offset ||
 513  E :          mask_to_modify != state_mask) {
 514  E :        EXPECT_EQ(kDataIsCorrupt, result.header_state);
 515  E :        EXPECT_EQ(kDataStateUnknown, result.body_state);
 516  E :        EXPECT_EQ(kDataIsClean, result.trailer_state);
 517  E :      }
 518  E :    } else if (address_to_modify >= block_info.trailer_padding) {
 519  E :      EXPECT_EQ(kDataIsCorrupt, result.block_state);
 520  E :      EXPECT_EQ(kDataIsClean, result.header_state);
 521  E :      EXPECT_EQ(kDataStateUnknown, result.body_state);
 522  E :      EXPECT_EQ(kDataIsCorrupt, result.trailer_state);
 523  E :    } else {
 524    :      // The byte being modified is in the body. Only expect to find
 525    :      // tampering if the block is quarantined or freed.
 526  E :      if (block_info.header->state != ALLOCATED_BLOCK) {
 527  E :        EXPECT_EQ(kDataIsCorrupt, result.block_state);
 528  E :        EXPECT_EQ(kDataIsClean, result.header_state);
 529  E :        EXPECT_EQ(kDataIsCorrupt, result.body_state);
 530  E :        EXPECT_EQ(kDataIsClean, result.trailer_state);
 531  E :      } else {
 532  E :        EXPECT_EQ(kDataIsClean, result.block_state);
 533  E :        EXPECT_EQ(kDataIsClean, result.header_state);
 534  E :        EXPECT_EQ(kDataIsClean, result.body_state);
 535  E :        EXPECT_EQ(kDataIsClean, result.trailer_state);
 536    :      }
 537    :    }
 538    :  
 539    :    // Restore the original value before returning.
 540  E :    *byte_to_modify = original_value;
 541  E :    return detected;
 542  E :  }
 543    :  
 544    :  bool ChecksumDetectsTampering(const BlockInfo& block_info,
 545  E :                                void* address_to_modify) {
 546  E :    if (!ChecksumDetectsTamperingWithMask(block_info, address_to_modify, 0xFF))
 547  E :      return false;
 548  E :    return true;
 549  E :  }
 550    :  
 551  E :  void TestChecksumDetectsTampering(const BlockInfo& block_info) {
 552  E :    uint32 checksum = BlockCalculateChecksum(block_info);
 553  E :    block_info.header->checksum = checksum;
 554  E :    EXPECT_TRUE(BlockChecksumIsValid(block_info));
 555  E :    ++block_info.header->checksum;
 556  E :    EXPECT_FALSE(BlockChecksumIsValid(block_info));
 557  E :    BlockSetChecksum(block_info);
 558  E :    EXPECT_EQ(checksum, block_info.header->checksum);
 559    :  
 560    :    // A detailed block analysis should find nothing awry.
 561  E :    BlockAnalysisResult result = {};
 562  E :    BlockAnalyze(block_info, &result);
 563  E :    EXPECT_EQ(kDataIsClean, result.block_state);
 564  E :    EXPECT_EQ(kDataIsClean, result.header_state);
 565  E :    EXPECT_EQ(kDataIsClean, result.body_state);
 566  E :    EXPECT_EQ(kDataIsClean, result.trailer_state);
 567    :  
 568    :    // Get the offset of the byte and the mask of the bits containing the
 569    :    // block state. This is resilient to changes in the BlockHeader layout.
 570  E :    if (state_offset == -1) {
 571  E :      BlockHeader header1 = {};
 572  E :      BlockHeader header2 = {};
 573  E :      header2.state = -1;
 574    :      FindModifiedBits(sizeof(BlockHeader),
 575    :                       reinterpret_cast<const uint8*>(&header1),
 576    :                       reinterpret_cast<const uint8*>(&header2),
 577    :                       &state_offset,
 578  E :                       &state_mask);
 579    :    }
 580    :  
 581    :    // Header bytes should be tamper proof.
 582  E :    EXPECT_TRUE(ChecksumDetectsTampering(block_info, block_info.header));
 583    :    EXPECT_TRUE(ChecksumDetectsTampering(block_info,
 584  E :                                         &block_info.header->alloc_stack));
 585    :    EXPECT_TRUE(ChecksumDetectsTamperingWithMask(
 586    :        block_info,
 587    :        block_info.block + state_offset,
 588  E :        state_mask));
 589    :  
 590    :    // Header padding should be tamper proof.
 591  E :    if (block_info.header_padding_size > 0) {
 592    :      EXPECT_TRUE(ChecksumDetectsTampering(block_info,
 593  E :          block_info.header_padding + block_info.header_padding_size / 2));
 594    :    }
 595    :  
 596    :    // Trailer padding should be tamper proof.
 597  E :    if (block_info.trailer_padding_size > 0) {
 598    :      EXPECT_TRUE(ChecksumDetectsTampering(block_info,
 599  E :          block_info.trailer_padding + block_info.trailer_padding_size / 2));
 600    :    }
 601    :  
 602    :    // Trailer bytes should be tamper proof.
 603  E :    EXPECT_TRUE(ChecksumDetectsTampering(block_info, block_info.trailer));
 604    :    EXPECT_TRUE(ChecksumDetectsTampering(block_info,
 605  E :                                         &block_info.trailer->heap_id));
 606    :  
 607    :    // Expect the checksum to detect body tampering in quarantined and freed
 608    :    // states, but not in the allocated state.
 609  E :    bool expected = (block_info.header->state != ALLOCATED_BLOCK);
 610  E :    EXPECT_EQ(expected, ChecksumDetectsTampering(block_info, block_info.body));
 611    :    EXPECT_EQ(expected, ChecksumDetectsTampering(block_info,
 612  E :        block_info.body + block_info.body_size / 2));
 613    :    EXPECT_EQ(expected, ChecksumDetectsTampering(block_info,
 614  E :        block_info.body + block_info.body_size - 1));
 615  E :  }
 616    :  
 617    :  }  // namespace
 618    :  
 619  E :  TEST(BlockTest, ChecksumDetectsTampering) {
 620    :    // This test requires a runtime because it makes use of BlockAnalyze.
 621    :    // Initialize it with valid values.
 622  E :    AsanRuntime runtime;
 623  E :    ASSERT_NO_FATAL_FAILURE(runtime.SetUp(L""));
 624  E :    HeapManagerInterface::HeapId valid_heap_id = runtime.GetProcessHeap();
 625  E :    runtime.AddThreadId(::GetCurrentThreadId());
 626  E :    common::StackCapture capture;
 627  E :    capture.InitFromStack();
 628    :    const common::StackCapture* valid_stack =
 629  E :        runtime.stack_cache()->SaveStackTrace(capture);
 630    :  
 631  E :    size_t kSizes[] = { 1, 4, 7, 16, 23, 32, 117, 1000, 4096 };
 632    :  
 633    :    // Doing a single allocation makes this test a bit faster.
 634  E :    size_t kAllocSize = 4 * 4096;
 635  E :    void* alloc = ::VirtualAlloc(NULL, kAllocSize, MEM_COMMIT, PAGE_READWRITE);
 636  E :    ASSERT_TRUE(alloc != NULL);
 637    :  
 638    :    // We test 9 different sizes, 9 different chunk sizes, 1 to 9 different
 639    :    // alignments, and 2 different redzone sizes. This is 810 different
 640    :    // combinations. We test each of these block allocations in all 3 possible
 641    :    // states. The probe itself tests the block at 7 to 9 different points, and
 642    :    // the tests require multiple iterations. Be careful playing with these
 643    :    // constants or the unittest time can easily spiral out of control! This
 644    :    // currently requires less than half a second, and is strictly CPU bound.
 645  E :    for (size_t chunk_size = kShadowRatio; chunk_size <= GetPageSize();
 646  E :         chunk_size *= 2) {
 647  E :      for (size_t align = kShadowRatio; align <= chunk_size; align *= 2) {
 648  E :        for (size_t redzone = 0; redzone <= chunk_size; redzone += chunk_size) {
 649  E :          for (size_t i = 0; i < arraysize(kSizes); ++i) {
 650  E :            BlockLayout layout = {};
 651    :            EXPECT_TRUE(BlockPlanLayout(chunk_size, align, kSizes[i], redzone,
 652  E :                                        redzone, &layout));
 653  E :            ASSERT_GT(kAllocSize, layout.block_size);
 654    :  
 655  E :            BlockInfo block_info = {};
 656  E :            BlockInitialize(layout, alloc, false, &block_info);
 657  E :            block_info.header->alloc_stack = valid_stack;
 658  E :            block_info.trailer->heap_id = valid_heap_id;
 659    :  
 660    :            // Test that the checksum detects tampering as expected in each block
 661    :            // state.
 662  E :            block_info.header->state = ALLOCATED_BLOCK;
 663  E :            ASSERT_NO_FATAL_FAILURE(TestChecksumDetectsTampering(block_info));
 664    :  
 665  E :            block_info.header->state = QUARANTINED_BLOCK;
 666  E :            block_info.header->free_stack = valid_stack;
 667  E :            block_info.trailer->free_tid = ::GetCurrentThreadId();
 668  E :            block_info.trailer->free_ticks = ::GetTickCount();
 669  E :            ASSERT_NO_FATAL_FAILURE(TestChecksumDetectsTampering(block_info));
 670    :  
 671  E :            block_info.header->state = FREED_BLOCK;
 672  E :            ASSERT_NO_FATAL_FAILURE(TestChecksumDetectsTampering(block_info));
 673  E :          }  // kSizes[i]
 674  E :        }  // redzone
 675  E :      }  // align
 676  E :    }  // chunk_size
 677    :  
 678  E :    ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
 679  E :    ASSERT_NO_FATAL_FAILURE(runtime.TearDown());
 680  E :  }
 681    :  
 682    :  }  // namespace asan
 683    :  }  // namespace agent

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