Add a new method to check if an address point to the beginning of an user alloc

This will be useful to check if a block has been allocated without guards.

R=chrisha@chromium.org

BUG=

Review URL: https://codereview.appspot.com/131430043

git-svn-id: http://sawbuck.googlecode.com/svn/trunk@2275 15e8cca8-e42c-11de-a347-f34a4f72eb7d
diff --git a/syzygy/agent/asan/heap_managers/block_heap_manager.cc b/syzygy/agent/asan/heap_managers/block_heap_manager.cc
index d6c3b53..b63392b 100644
--- a/syzygy/agent/asan/heap_managers/block_heap_manager.cc
+++ b/syzygy/agent/asan/heap_managers/block_heap_manager.cc
@@ -146,10 +146,9 @@
   DCHECK_NE(static_cast<HeapId>(NULL), heap_id);
 
   BlockInfo block_info = {};
-  BlockHeader* header = BlockGetHeaderFromBody(alloc);
-  if (header == NULL || !Shadow::BlockInfoFromShadow(header, &block_info)) {
-    // TODO(chrisha|sebmarchand): Handle invalid allocation addresses. Currently
-    //     we can't tell these apart from unguarded allocations.
+  if (!Shadow::IsBeginningOfBlockBody(alloc) ||
+      !Shadow::BlockInfoFromShadow(alloc, &block_info)) {
+    // TODO(chrisha|sebmarchand): Handle invalid allocation addresses.
 
     // Assume that this block was allocated without guards.
     return unguarded_allocation_heap_->Free(alloc);
diff --git a/syzygy/agent/asan/shadow.cc b/syzygy/agent/asan/shadow.cc
index e71c402..a56d36f 100644
--- a/syzygy/agent/asan/shadow.cc
+++ b/syzygy/agent/asan/shadow.cc
@@ -232,6 +232,13 @@
   return true;
 }
 
+bool Shadow::IsBeginningOfBlockBody(const void* addr) {
+  DCHECK_NE(static_cast<void*>(NULL), addr);
+  if (IsLeftRedzone(addr) || IsRightRedzone(addr))
+    return false;
+  return IsLeftRedzone(reinterpret_cast<const uint8*>(addr) - 1);
+}
+
 void Shadow::CloneShadowRange(const void* src_pointer,
                               void* dst_pointer,
                               size_t size) {
diff --git a/syzygy/agent/asan/shadow.h b/syzygy/agent/asan/shadow.h
index 34c00d5..019737f 100644
--- a/syzygy/agent/asan/shadow.h
+++ b/syzygy/agent/asan/shadow.h
@@ -216,6 +216,13 @@
   static bool ParentBlockInfoFromShadow(
       const BlockInfo& nested, BlockInfo* info);
 
+  // Checks if the address @p addr corresponds to the beginning of a block's
+  // body, i.e. if it's preceded by a left redzone.
+  // @param addr The address that we want to check.
+  // @returns true if the address corresponds to the beginning of a block's
+  //     body, false otherwise.
+  static bool IsBeginningOfBlockBody(const void* addr);
+
  protected:
   // Reset the shadow memory.
   static void Reset();
diff --git a/syzygy/agent/asan/shadow_unittest.cc b/syzygy/agent/asan/shadow_unittest.cc
index bd1dabf..b3d7a81 100644
--- a/syzygy/agent/asan/shadow_unittest.cc
+++ b/syzygy/agent/asan/shadow_unittest.cc
@@ -15,6 +15,7 @@
 #include "syzygy/agent/asan/shadow.h"
 
 #include "base/rand_util.h"
+#include "base/memory/scoped_ptr.h"
 #include "gtest/gtest.h"
 #include "syzygy/common/align.h"
 
@@ -300,9 +301,9 @@
   ASSERT_NE(0U, kAllocSize % kShadowRatio);
   BlockPlanLayout(kShadowRatio, kShadowRatio, kAllocSize, 0, 0, &layout);
 
-  uint8* data = new uint8[layout.block_size];
+  scoped_ptr<uint8> data(new uint8[layout.block_size]);
   BlockInfo info = {};
-  BlockInitialize(layout, data, false, &info);
+  BlockInitialize(layout, data.get(), false, &info);
 
   Shadow::PoisonAllocatedBlock(info);
   uint8* cursor = info.block;
@@ -321,7 +322,6 @@
   }
 
   Shadow::Unpoison(info.block, info.block_size);
-  delete [] data;
 }
 
 
@@ -406,6 +406,30 @@
   EXPECT_NO_FATAL_FAILURE(TestBlockInfoFromShadow(layout2, layout0));
 }
 
+TEST(ShadowTest, IsBeginningOfBlockBody) {
+  BlockLayout l = {};
+  BlockPlanLayout(kShadowRatio, kShadowRatio, 7, 0, 0, &l);
+
+  size_t data_size = l.block_size;
+  scoped_ptr<uint8> data(new uint8[data_size]);
+
+  BlockInfo block_info = {};
+  BlockInitialize(l, data.get(), false, &block_info);
+
+  Shadow::PoisonAllocatedBlock(block_info);
+
+  EXPECT_TRUE(Shadow::IsBeginningOfBlockBody(block_info.body));
+  EXPECT_FALSE(Shadow::IsBeginningOfBlockBody(data.get()));
+
+  block_info.header->state = QUARANTINED_BLOCK;
+  Shadow::MarkAsFreed(block_info.body, block_info.body_size);
+
+  EXPECT_TRUE(Shadow::IsBeginningOfBlockBody(block_info.body));
+  EXPECT_FALSE(Shadow::IsBeginningOfBlockBody(data.get()));
+
+  Shadow::Unpoison(data.get(), data_size);
+}
+
 TEST(ShadowWalkerTest, WalksNonNestedBlocks) {
   BlockLayout l = {};
   BlockPlanLayout(kShadowRatio, kShadowRatio, 7, 0, 0, &l);