Added an utility function to ensure that we don't "asanize" the same image twice.

BUG=

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

git-svn-id: http://sawbuck.googlecode.com/svn/trunk/syzygy@1220 15e8cca8-e42c-11de-a347-f34a4f72eb7d
diff --git a/agent/asan/asan_shadow_unittest.cc b/agent/asan/asan_shadow_unittest.cc
index 1c4ee6f..9add91f 100644
--- a/agent/asan/asan_shadow_unittest.cc
+++ b/agent/asan/asan_shadow_unittest.cc
@@ -11,6 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+
 #include "syzygy/agent/asan/asan_shadow.h"
 
 #include "base/rand_util.h"
diff --git a/instrument/transforms/asan_transform.cc b/instrument/transforms/asan_transform.cc
index 9db46d0..6ee0f8c 100644
--- a/instrument/transforms/asan_transform.cc
+++ b/instrument/transforms/asan_transform.cc
@@ -22,6 +22,7 @@
 #include "syzygy/block_graph/basic_block_assembler.h"
 #include "syzygy/block_graph/block_builder.h"
 #include "syzygy/pe/block_util.h"
+#include "syzygy/pe/pe_utils.h"
 #include "syzygy/pe/transforms/add_imports_transform.h"
 #include "third_party/distorm/files/include/mnemonics.h"
 #include "third_party/distorm/files/src/x86defs.h"
@@ -41,6 +42,7 @@
 using block_graph::Immediate;
 using block_graph::Instruction;
 using block_graph::Operand;
+using block_graph::TypedBlock;
 using block_graph::Value;
 using core::Register;
 using core::RegisterCode;
@@ -324,6 +326,18 @@
 
 bool AsanTransform::PreBlockGraphIteration(BlockGraph* block_graph,
                                            BlockGraph::Block* header_block) {
+  bool already_instrumented = false;
+  // Ensure that this image has not already been instrumented.
+  if (!pe::HasImportEntry(header_block, kSyzyAsanDll, &already_instrumented)) {
+    LOG(ERROR) << "Unable to check if the image is already instrumented.";
+    return false;
+  }
+
+  if (already_instrumented) {
+    LOG(ERROR) << "The image is already instrumented.";
+    return false;
+  }
+
   // Add an import entry for the ASAN runtime.
   AddImportsTransform::ImportedModule import_module(asan_dll_name_.c_str());
 
diff --git a/pe/pe_utils.cc b/pe/pe_utils.cc
index 319c7b6..7248093 100644
--- a/pe/pe_utils.cc
+++ b/pe/pe_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 Google Inc.
+// Copyright 2012 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
 
 #include "syzygy/pe/pe_utils.h"
 
+#include "base/string_util.h"
 #include "syzygy/block_graph/typed_block.h"
 #include "syzygy/pe/dos_stub.h"
 
@@ -26,6 +27,16 @@
 
 namespace {
 
+// A simple struct that can be used to let us access strings using TypedBlock.
+struct StringStruct {
+  const char string[1];
+};
+
+typedef TypedBlock<IMAGE_DOS_HEADER> DosHeader;
+typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor;
+typedef TypedBlock<IMAGE_NT_HEADERS> NtHeaders;
+typedef TypedBlock<StringStruct> String;
+
 template <typename BlockPtr>
 BlockPtr UncheckedGetNtHeadersBlockFromDosHeaderBlock(
     BlockPtr dos_header_block) {
@@ -338,4 +349,70 @@
   return true;
 }
 
+bool HasImportEntry(block_graph::BlockGraph::Block* header_block,
+                    const base::StringPiece& dll_name,
+                    bool* has_import_entry) {
+  DCHECK(header_block != NULL);
+  DCHECK(dll_name != NULL);
+  DCHECK(!dll_name.empty());
+  DCHECK(has_import_entry != NULL);
+
+  *has_import_entry = false;
+
+  DosHeader dos_header;
+  NtHeaders nt_headers;
+  if (!dos_header.Init(0, header_block) ||
+      !dos_header.Dereference(dos_header->e_lfanew, &nt_headers)) {
+    LOG(ERROR) << "Unable to cast image headers.";
+    return false;
+  }
+
+  BlockGraph::Block* image_import_descriptor_block;
+  IMAGE_DATA_DIRECTORY* import_directory =
+      nt_headers->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
+  DCHECK(nt_headers.HasReference(import_directory->VirtualAddress));
+
+  ImageImportDescriptor image_import_descriptor;
+  if (!nt_headers.Dereference(import_directory->VirtualAddress,
+                              &image_import_descriptor)) {
+    // This could happen if the image import descriptor array is empty, and
+    // terminated by a *partial* null entry. However, we've not yet seen that.
+    LOG(ERROR) << "Failed to dereference Image Import Descriptor Array.";
+    return false;
+  }
+
+  image_import_descriptor_block = image_import_descriptor.block();
+
+  ImageImportDescriptor iida;
+  if (!iida.Init(0, image_import_descriptor_block)) {
+    LOG(ERROR) << "Unable to cast Image Import Descriptor.";
+    return false;
+  }
+
+  // The array is NULL terminated with a potentially incomplete descriptor so
+  // we can't use ElementCount - 1.
+  DCHECK_GT(image_import_descriptor_block->size(), 0U);
+  size_t descriptor_count =
+      (common::AlignUp(image_import_descriptor_block->size(),
+                       sizeof(IMAGE_IMPORT_DESCRIPTOR)) /
+       sizeof(IMAGE_IMPORT_DESCRIPTOR)) - 1;
+
+  for (size_t iida_index = 0; iida_index < descriptor_count; ++iida_index) {
+    String ref_dll_name;
+    if (!iida.Dereference(iida[iida_index].Name, &ref_dll_name)) {
+      LOG(ERROR) << "Unable to dereference DLL name.";
+      return false;
+    }
+
+    size_t max_len = ref_dll_name.ElementCount();
+    if (base::strncasecmp(ref_dll_name->string, dll_name.data(),
+                          max_len) == 0) {
+      *has_import_entry = true;
+      break;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace pe
diff --git a/pe/pe_utils.h b/pe/pe_utils.h
index 0c1bff7..38757e4 100644
--- a/pe/pe_utils.h
+++ b/pe/pe_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2012 Google Inc.
+// Copyright 2012 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -114,6 +114,15 @@
 bool GetTlsInitializers(block_graph::BlockGraph::Block* dos_header_block,
                         EntryPointSet* entry_points);
 
+// Check if an image contains an import entry.
+// @param The image's header-block.
+// @param dll_name The name of the DLL.
+// @param contains_dependence Boolean to indicate if the image contains the
+//     import entry.
+// @returns true in case of success, false otherwise.
+bool HasImportEntry(block_graph::BlockGraph::Block* header_block,
+                    const base::StringPiece& dll_name,
+                    bool* has_import_entry);
 }  // namespace pe
 
 #endif  // SYZYGY_PE_PE_UTILS_H_
diff --git a/pe/pe_utils_unittest.cc b/pe/pe_utils_unittest.cc
index 345e9ca..7ad8369 100644
--- a/pe/pe_utils_unittest.cc
+++ b/pe/pe_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 Google Inc.
+// Copyright 2012 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,11 +16,13 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "syzygy/pe/transforms/add_imports_transform.h"
 
 namespace pe {
 
 using block_graph::BlockGraph;
 using core::RelativeAddress;
+using pe::transforms::AddImportsTransform;
 
 namespace {
 
@@ -304,4 +306,26 @@
   EXPECT_EQ(tls_initializer_block_, entry_points.begin()->first);
 }
 
+TEST_F(PEUtilsTest, HasImportEntry) {
+  // Creates an imported module.
+  AddImportsTransform::ImportedModule module("foo.dll");
+  const char* kFooFunc = "foo_func";
+  size_t function_foo = module.AddSymbol(kFooFunc);
+  ASSERT_EQ(kFooFunc, module.GetSymbolName(function_foo));
+
+  // Apply the transform to add this module import to the block-graph.
+  AddImportsTransform transform;
+  transform.AddModule(&module);
+  ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
+      &transform, &block_graph_, dos_header_block_));
+
+  // Ensure that we can find this module, and that we can't find a
+  // non-imported module.
+  bool has_import = false;
+  EXPECT_TRUE(HasImportEntry(dos_header_block_, "foo.dll", &has_import));
+  EXPECT_TRUE(has_import);
+  EXPECT_TRUE(HasImportEntry(dos_header_block_, "bar.dll", &has_import));
+  EXPECT_FALSE(has_import);
+}
+
 }  // namespace pe
diff --git a/pe/transforms/add_imports_transform.cc b/pe/transforms/add_imports_transform.cc
index 02d0c08..5c06a57 100644
--- a/pe/transforms/add_imports_transform.cc
+++ b/pe/transforms/add_imports_transform.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 Google Inc.
+// Copyright 2012 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -219,6 +219,7 @@
 
   // The array is NULL terminated with a potentially incomplete descriptor so
   // we can't use ElementCount - 1.
+  DCHECK_GT(iida_block->size(), 0U);
   size_t descriptor_count =
       (common::AlignUp(iida_block->size(), sizeof(IMAGE_IMPORT_DESCRIPTOR)) /
        sizeof(IMAGE_IMPORT_DESCRIPTOR)) - 1;
@@ -230,7 +231,7 @@
       return false;
     }
 
-    size_t max_len = dll_name.block()->size() - dll_name.offset();
+    size_t max_len = dll_name.ElementCount();
     if (base::strncasecmp(dll_name->string, module_name, max_len) == 0) {
       // This should never fail, but we sanity check it nonetheless.
       bool result = iid->Init(iida.OffsetOf(iida[iida_index]), iida.block());
@@ -487,7 +488,6 @@
 
 }  // namespace
 
-
 const char AddImportsTransform::kTransformName[] = "AddImportsTransform";
 
 AddImportsTransform::AddImportsTransform()