| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 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/instrument/transforms/filler_transform.h" |
| 16 |
| 17 #include <set> |
| 18 |
| 19 #include "base/memory/scoped_ptr.h" |
| 20 #include "gtest/gtest.h" |
| 21 #include "syzygy/assm/assembler_base.h" |
| 22 #include "syzygy/block_graph/basic_block.h" |
| 23 #include "syzygy/block_graph/basic_block_assembler.h" |
| 24 #include "syzygy/block_graph/basic_block_subgraph.h" |
| 25 #include "syzygy/block_graph/basic_block_test_util.h" |
| 26 #include "syzygy/block_graph/block_graph.h" |
| 27 #include "syzygy/instrument/transforms/unittest_util.h" |
| 28 |
| 29 namespace instrument { |
| 30 namespace transforms { |
| 31 namespace { |
| 32 |
| 33 using block_graph::BasicBlock; |
| 34 using block_graph::BlockGraph; |
| 35 using block_graph::BasicCodeBlock; |
| 36 |
| 37 const char kNopName[] = "NOP"; |
| 38 |
| 39 // Finds instruction indices and sizes of every NOP in @p instructions and |
| 40 // stores the result in @p nop_indices. Returns the @p instructions count. |
| 41 size_t FindAllNops(BasicBlock::Instructions* instructions, |
| 42 std::map<size_t, int>* nop_indices) { |
| 43 size_t index = 0LL; |
| 44 BasicBlock::Instructions::iterator inst_it = instructions->begin(); |
| 45 for (; inst_it != instructions->end(); ++inst_it, ++index) { |
| 46 if (!::strcmp(inst_it->GetName(), kNopName)) |
| 47 (*nop_indices)[index] = inst_it->size(); |
| 48 } |
| 49 return index; |
| 50 } |
| 51 |
| 52 class FillerBasicBlockTransformTest : public testing::Test { |
| 53 public: |
| 54 typedef testing::Test Super; |
| 55 typedef FillerBasicBlockTransform::NopSizes NopSizes; |
| 56 typedef FillerBasicBlockTransform::NopSpec NopSpec; |
| 57 |
| 58 // Creates a new length @p n instruction list for testing. |
| 59 void CreateInstructions(int n) { |
| 60 using assm::eax; |
| 61 using assm::ebp; |
| 62 using assm::esp; |
| 63 using assm::kSize32Bit; |
| 64 |
| 65 instructions_.reset(new BasicBlock::Instructions); |
| 66 if (n == 0) |
| 67 return; |
| 68 |
| 69 block_graph::BasicBlockAssembler |
| 70 assm(instructions_->begin(), instructions_.get()); |
| 71 bool setup_stack = false; |
| 72 --n; // Reserve for ret. |
| 73 if (n > 3) { |
| 74 setup_stack = true; |
| 75 n -= 3; // Reserve for stack frame setup. |
| 76 } |
| 77 if (setup_stack) { |
| 78 assm.push(ebp); |
| 79 assm.mov(ebp, esp); |
| 80 } |
| 81 for (; n > 0; --n) |
| 82 assm.mov(eax, block_graph::Immediate(n, kSize32Bit)); |
| 83 if (setup_stack) |
| 84 assm.pop(ebp); |
| 85 assm.ret(0); |
| 86 } |
| 87 |
| 88 protected: |
| 89 scoped_ptr<BasicBlock::Instructions> instructions_; |
| 90 }; |
| 91 |
| 92 class TestFillerTransform : public FillerTransform { |
| 93 public: |
| 94 explicit TestFillerTransform(const std::vector<std::string>& target_list) |
| 95 : FillerTransform(target_list) { } |
| 96 }; |
| 97 |
| 98 class FillerTransformTest : public testing::TestDllTransformTest { |
| 99 public: |
| 100 typedef testing::TestDllTransformTest Super; |
| 101 typedef BlockGraph::Block::SourceRange SourceRange; |
| 102 |
| 103 // Finds all blocks whose names appear in @p target_list, and writes the |
| 104 // mapping fron name to block in @p result. Returns true iff all names in |
| 105 // @p target_list are found. |
| 106 static bool FindAllBlocks( |
| 107 BlockGraph* block_graph, |
| 108 const std::vector<std::string>& target_list, |
| 109 std::map<std::string, BlockGraph::Block*>* result) { |
| 110 DCHECK(result && result->empty()); |
| 111 std::set<std::string> name_set(target_list.begin(), target_list.end()); |
| 112 for (auto& it : block_graph->blocks_mutable()) { |
| 113 std::string block_name = it.second.name(); |
| 114 if (name_set.find(block_name) != name_set.end()) { |
| 115 (*result)[block_name] = &it.second; |
| 116 name_set.erase(block_name); |
| 117 } |
| 118 } |
| 119 return name_set.empty(); |
| 120 } |
| 121 |
| 122 // Verifies that @p instructions contains all expected NOPs except for NOPs |
| 123 // that would be inserted beyond the last instruction. |
| 124 static void CheckNops(BasicBlock::Instructions* instructions) { |
| 125 std::map<size_t, int> nop_indices; |
| 126 size_t num_inst = FindAllNops(instructions, &nop_indices); |
| 127 // The checks here depend on how NopSpec is initialized in |
| 128 // FillerBasicBlockTransform. |
| 129 EXPECT_TRUE(1 >= num_inst || nop_indices[1] == 1); |
| 130 } |
| 131 |
| 132 // Verifies that all contiguous NOPs in @p instuctions are followed by a |
| 133 // non-NOP instruction, which shares the same source range as the NOP run. |
| 134 static void CheckNopSourceRange(BasicBlock::Instructions* instructions) { |
| 135 std::vector<SourceRange> range_queue; |
| 136 BasicBlock::Instructions::iterator inst_it = instructions->begin(); |
| 137 for (; inst_it != instructions->end(); ++inst_it) { |
| 138 if (!::strcmp(inst_it->GetName(), kNopName)) { |
| 139 // Found NOP: add to queue. |
| 140 range_queue.push_back(inst_it->source_range()); |
| 141 } else { |
| 142 // Found non-NOP: verify stored ranges in the queue and then clear it. |
| 143 for (SourceRange& nop_source_range : range_queue) { |
| 144 EXPECT_EQ(inst_it->source_range(), nop_source_range); |
| 145 } |
| 146 range_queue.clear(); |
| 147 } |
| 148 } |
| 149 // Expect there's no trailing NOP. |
| 150 EXPECT_TRUE(range_queue.empty()); |
| 151 } |
| 152 }; |
| 153 |
| 154 } // namespace |
| 155 |
| 156 // Sanity check for helper CreateInstructions(). |
| 157 TEST_F(FillerBasicBlockTransformTest, CreateInstructions) { |
| 158 for (int i = 0; i < 10; ++i) { |
| 159 CreateInstructions(i); |
| 160 size_t count = 0; |
| 161 for (auto inst : *instructions_.get()) |
| 162 ++count; |
| 163 EXPECT_EQ(i, count); |
| 164 } |
| 165 } |
| 166 |
| 167 TEST_F(FillerBasicBlockTransformTest, InjectRegular) { |
| 168 CreateInstructions(8); |
| 169 NopSpec nop_spec = { |
| 170 {1, NopSizes::NOP3}, |
| 171 {2, NopSizes::NOP1}, |
| 172 {3, NopSizes::NOP4}, |
| 173 {6, NopSizes::NOP1}, |
| 174 {8, NopSizes::NOP5}, |
| 175 {9, NopSizes::NOP9}}; |
| 176 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 177 std::map<size_t, int> nop_indices; |
| 178 EXPECT_EQ(8U + 6U, FindAllNops(instructions_.get(), &nop_indices)); |
| 179 EXPECT_EQ(6U, nop_indices.size()); |
| 180 EXPECT_EQ(3U, nop_indices[1]); |
| 181 EXPECT_EQ(1U, nop_indices[2]); |
| 182 EXPECT_EQ(4U, nop_indices[3]); |
| 183 EXPECT_EQ(1U, nop_indices[6]); |
| 184 EXPECT_EQ(5U, nop_indices[8]); |
| 185 EXPECT_EQ(9U, nop_indices[9]); |
| 186 } |
| 187 |
| 188 TEST_F(FillerBasicBlockTransformTest, InjectToStart) { |
| 189 CreateInstructions(5); |
| 190 NopSpec nop_spec1 = {{0, NopSizes::NOP4}}; |
| 191 FillerBasicBlockTransform::InjectNop(nop_spec1, false, instructions_.get()); |
| 192 std::map<size_t, int> nop_indices1; |
| 193 EXPECT_EQ(5U + 1U, FindAllNops(instructions_.get(), &nop_indices1)); |
| 194 EXPECT_EQ(1U, nop_indices1.size()); |
| 195 EXPECT_EQ(4U, nop_indices1[0]); |
| 196 |
| 197 // Inject another NOP, when a NOP already exists. |
| 198 NopSpec nop_spec2 = {{0, NopSizes::NOP1}}; |
| 199 FillerBasicBlockTransform::InjectNop(nop_spec2, false, instructions_.get()); |
| 200 std::map<size_t, int> nop_indices2; |
| 201 EXPECT_EQ(5U + 1U + 1U, FindAllNops(instructions_.get(), &nop_indices2)); |
| 202 EXPECT_EQ(2U, nop_indices2.size()); // New + existing. |
| 203 EXPECT_EQ(1U, nop_indices2[0]); // From |nop_spec2|. |
| 204 EXPECT_EQ(4U, nop_indices2[1]); // From |nop_spec1|. |
| 205 } |
| 206 |
| 207 TEST_F(FillerBasicBlockTransformTest, InjectToBeforeEnd) { |
| 208 CreateInstructions(7); |
| 209 NopSpec nop_spec = {{6, NopSizes::NOP2}}; |
| 210 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 211 std::map<size_t, int> nop_indices; |
| 212 EXPECT_EQ(7U + 1U, FindAllNops(instructions_.get(), &nop_indices)); |
| 213 EXPECT_EQ(1U, nop_indices.size()); |
| 214 EXPECT_EQ(2U, nop_indices[6]); |
| 215 } |
| 216 |
| 217 TEST_F(FillerBasicBlockTransformTest, CannotInjectBeyondEnd) { |
| 218 CreateInstructions(7); |
| 219 NopSpec nop_spec = {{7, NopSizes::NOP1}, {17, NopSizes::NOP1}}; |
| 220 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 221 std::map<size_t, int> nop_indices; |
| 222 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_indices)); |
| 223 EXPECT_EQ(0U, nop_indices.size()); |
| 224 } |
| 225 |
| 226 TEST_F(FillerBasicBlockTransformTest, InjectToEmpty) { |
| 227 CreateInstructions(0); |
| 228 NopSpec nop_spec = {{0, NopSizes::NOP1}, {1, NopSizes::NOP2}}; |
| 229 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 230 std::map<size_t, int> nop_indices; |
| 231 EXPECT_EQ(0U, FindAllNops(instructions_.get(), &nop_indices)); |
| 232 EXPECT_EQ(0U, nop_indices.size()); |
| 233 } |
| 234 |
| 235 TEST_F(FillerBasicBlockTransformTest, InjectToSingle) { |
| 236 CreateInstructions(1); |
| 237 NopSpec nop_spec = { |
| 238 {0, NopSizes::NOP5}, |
| 239 {1, NopSizes::NOP8}, |
| 240 {3, NopSizes::NOP2}}; // Gets ignored. |
| 241 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 242 std::map<size_t, int> nop_indices; |
| 243 EXPECT_EQ(1U + 2U, FindAllNops(instructions_.get(), &nop_indices)); |
| 244 EXPECT_EQ(2U, nop_indices.size()); |
| 245 EXPECT_EQ(5U, nop_indices[0]); |
| 246 EXPECT_EQ(8U, nop_indices[1]); |
| 247 } |
| 248 |
| 249 TEST_F(FillerBasicBlockTransformTest, InjectNone) { |
| 250 CreateInstructions(7); |
| 251 NopSpec nop_spec = {}; |
| 252 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 253 std::map<size_t, int> nop_indices; |
| 254 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_indices)); |
| 255 EXPECT_EQ(0U, nop_indices.size()); |
| 256 } |
| 257 |
| 258 TEST_F(FillerBasicBlockTransformTest, InjectConsecutive) { |
| 259 CreateInstructions(4); |
| 260 NopSpec nop_spec = { |
| 261 {0, NopSizes::NOP1}, |
| 262 {1, NopSizes::NOP2}, |
| 263 {2, NopSizes::NOP3}, |
| 264 {3, NopSizes::NOP5}, |
| 265 {4, NopSizes::NOP7}, |
| 266 {5, NopSizes::NOP11}}; |
| 267 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 268 std::map<size_t, int> nop_indices; |
| 269 EXPECT_EQ(4U + 6U, FindAllNops(instructions_.get(), &nop_indices)); |
| 270 EXPECT_EQ(6U, nop_indices.size()); |
| 271 EXPECT_EQ(1U, nop_indices[0]); |
| 272 EXPECT_EQ(2U, nop_indices[1]); |
| 273 EXPECT_EQ(3U, nop_indices[2]); |
| 274 EXPECT_EQ(5U, nop_indices[3]); |
| 275 EXPECT_EQ(7U, nop_indices[4]); |
| 276 EXPECT_EQ(11U, nop_indices[5]); |
| 277 } |
| 278 |
| 279 TEST_F(FillerBasicBlockTransformTest, InjectAlternate) { |
| 280 CreateInstructions(4); |
| 281 NopSpec nop_spec = { |
| 282 {0, NopSizes::NOP10}, |
| 283 {2, NopSizes::NOP9}, |
| 284 {4, NopSizes::NOP8}, |
| 285 {6, NopSizes::NOP7}, |
| 286 {8, NopSizes::NOP6}, // Gets ignored. |
| 287 {10, NopSizes::NOP5}}; // Gets ignored. |
| 288 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get()); |
| 289 std::map<size_t, int> nop_indices; |
| 290 EXPECT_EQ(4U + 4U, FindAllNops(instructions_.get(), &nop_indices)); |
| 291 EXPECT_EQ(4U, nop_indices.size()); |
| 292 EXPECT_EQ(10U, nop_indices[0]); |
| 293 EXPECT_EQ(9U, nop_indices[2]); |
| 294 EXPECT_EQ(8U, nop_indices[4]); |
| 295 EXPECT_EQ(7U, nop_indices[6]); |
| 296 } |
| 297 |
| 298 TEST_F(FillerTransformTest, Apply) { |
| 299 std::vector<std::string> target_list = { |
| 300 "Used::M", |
| 301 "TestUnusedFuncs" |
| 302 }; |
| 303 |
| 304 ASSERT_NO_FATAL_FAILURE(DecomposeTestDll()); |
| 305 |
| 306 // Apply the transform. |
| 307 TestFillerTransform tx(target_list); |
| 308 tx.set_debug_friendly(true); // Copy source ranges to injected NOPs. |
| 309 ASSERT_TRUE(block_graph::ApplyBlockGraphTransform( |
| 310 &tx, policy_, &block_graph_, header_block_)); |
| 311 |
| 312 // Check results. First find and store target blocks. |
| 313 EXPECT_EQ(2U, tx.num_targets_updated()); |
| 314 std::map<std::string, BlockGraph::Block*> target_map; |
| 315 ASSERT_TRUE(FindAllBlocks(&block_graph_, target_list, &target_map)); |
| 316 ASSERT_TRUE(target_map.size() == target_list.size()); |
| 317 |
| 318 // Check the block for each target. |
| 319 for (auto& it : target_map) { |
| 320 BlockGraph::Block* target_block = it.second; |
| 321 |
| 322 // Decompose target block to subgraph. |
| 323 block_graph::BasicBlockSubGraph subgraph; |
| 324 block_graph::BasicBlockDecomposer bb_decomposer(target_block, &subgraph); |
| 325 ASSERT_TRUE(bb_decomposer.Decompose()); |
| 326 |
| 327 // Examine each basic code block. |
| 328 block_graph::BasicBlockSubGraph::BBCollection& basic_blocks = |
| 329 subgraph.basic_blocks(); |
| 330 for (auto bb = basic_blocks.begin(); bb != basic_blocks.end(); ++bb) { |
| 331 BasicCodeBlock* bc_block = BasicCodeBlock::Cast(*bb); |
| 332 if (bc_block != nullptr) { |
| 333 CheckNops(&bc_block->instructions()); |
| 334 CheckNopSourceRange(&bc_block->instructions()); |
| 335 } |
| 336 } |
| 337 } |
| 338 } |
| 339 |
| 340 } // namespace transforms |
| 341 } // namespace instrument |
| OLD | NEW |