imposed_ensemble_matcher_unittest.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "components/zucchini/imposed_ensemble_matcher.h"
  10. #include "base/bind.h"
  11. #include "base/callback_helpers.h"
  12. #include "base/check_op.h"
  13. #include "components/zucchini/buffer_view.h"
  14. #include "components/zucchini/disassembler.h"
  15. #include "components/zucchini/element_detection.h"
  16. #include "components/zucchini/image_utils.h"
  17. #include "testing/gtest/include/gtest/gtest.h"
  18. #include "third_party/abseil-cpp/absl/types/optional.h"
  19. namespace zucchini {
  20. namespace {
  21. // This test uses a mock archive format where regions are determined by their
  22. // consecutive byte values rather than parsing real executables. In fact, since
  23. // elements are imposed, only the first byte of the element is used to specify
  24. // executable type of the mock data:
  25. // - 'W' and 'w' specify kExeTypeWin32X86.
  26. // - 'E' and 'e' specify kExeTypeElfX86.
  27. // - Everything else specify kExeTypeUnknown.
  28. class TestElementDetector {
  29. public:
  30. TestElementDetector() {}
  31. absl::optional<Element> Run(ConstBufferView image) const {
  32. DCHECK_GT(image.size(), 0U);
  33. char first_char = *image.begin();
  34. if (first_char == 'W' || first_char == 'w')
  35. return Element(image.local_region(), kExeTypeWin32X86);
  36. if (first_char == 'E' || first_char == 'e')
  37. return Element(image.local_region(), kExeTypeElfX86);
  38. return absl::nullopt;
  39. }
  40. };
  41. } // namespace
  42. TEST(ImposedMatchParserTest, ImposedMatchParser) {
  43. std::vector<uint8_t> old_data;
  44. std::vector<uint8_t> new_data;
  45. auto populate = [](const std::string& s, std::vector<uint8_t>* data) {
  46. for (char ch : s)
  47. data->push_back(static_cast<uint8_t>(ch));
  48. };
  49. // Pos: 11111111
  50. // 012345678901234567
  51. populate("1WW222EEEE", &old_data);
  52. populate("33eee2222222wwww44", &new_data);
  53. ConstBufferView old_image(&old_data[0], old_data.size());
  54. ConstBufferView new_image(&new_data[0], new_data.size());
  55. TestElementDetector detector;
  56. // Reusable output values.
  57. std::string prev_imposed_matches;
  58. ImposedMatchParser::Status status;
  59. size_t num_identical;
  60. std::vector<ElementMatch> matches;
  61. std::vector<ElementMatch> bad_matches;
  62. auto run_test = [&](const std::string& imposed_matches) -> bool {
  63. prev_imposed_matches = imposed_matches;
  64. status = ImposedMatchParser::kSuccess;
  65. num_identical = 0;
  66. matches.clear();
  67. bad_matches.clear();
  68. ImposedMatchParser parser;
  69. status = parser.Parse(imposed_matches, old_image, new_image,
  70. base::BindRepeating(&TestElementDetector::Run,
  71. base::Unretained(&detector)));
  72. num_identical = parser.num_identical();
  73. matches = std::move(*parser.mutable_matches());
  74. bad_matches = std::move(*parser.mutable_bad_matches());
  75. return status == ImposedMatchParser::kSuccess;
  76. };
  77. auto run_check = [&](const ElementMatch& match, ExecutableType exe_type,
  78. offset_t old_offset, size_t old_size,
  79. offset_t new_offset, size_t new_size) {
  80. EXPECT_EQ(exe_type, match.exe_type()) << prev_imposed_matches;
  81. EXPECT_EQ(exe_type, match.old_element.exe_type) << prev_imposed_matches;
  82. EXPECT_EQ(old_offset, match.old_element.offset) << prev_imposed_matches;
  83. EXPECT_EQ(old_size, match.old_element.size) << prev_imposed_matches;
  84. EXPECT_EQ(exe_type, match.new_element.exe_type) << prev_imposed_matches;
  85. EXPECT_EQ(new_offset, match.new_element.offset) << prev_imposed_matches;
  86. EXPECT_EQ(new_size, match.new_element.size) << prev_imposed_matches;
  87. };
  88. // Empty string: Vacuous but valid.
  89. EXPECT_TRUE(run_test(""));
  90. EXPECT_EQ(0U, num_identical);
  91. EXPECT_EQ(0U, matches.size());
  92. EXPECT_EQ(0U, bad_matches.size());
  93. // Full matches. Different permutations give same result.
  94. for (const std::string& imposed_matches :
  95. {"1+2=12+4,4+2=5+2,6+4=2+3", "1+2=12+4,6+4=2+3,4+2=5+2",
  96. "4+2=5+2,1+2=12+4,6+4=2+3", "4+2=5+2,6+4=2+3,1+2=12+4",
  97. "6+4=2+3,1+2=12+4,4+2=5+2", "6+4=2+3,1+2=12+4,4+2=5+2"}) {
  98. EXPECT_TRUE(run_test(imposed_matches));
  99. EXPECT_EQ(1U, num_identical); // "4+2=5+2"
  100. EXPECT_EQ(2U, matches.size());
  101. // Results are sorted by "new" offsets.
  102. run_check(matches[0], kExeTypeElfX86, 6, 4, 2, 3);
  103. run_check(matches[1], kExeTypeWin32X86, 1, 2, 12, 4);
  104. EXPECT_EQ(0U, bad_matches.size());
  105. }
  106. // Single subregion match.
  107. EXPECT_TRUE(run_test("1+2=12+4"));
  108. EXPECT_EQ(0U, num_identical);
  109. EXPECT_EQ(1U, matches.size());
  110. run_check(matches[0], kExeTypeWin32X86, 1, 2, 12, 4);
  111. EXPECT_EQ(0U, bad_matches.size());
  112. // Single subregion match. We're lax with redundant 0.
  113. EXPECT_TRUE(run_test("6+04=02+10"));
  114. EXPECT_EQ(0U, num_identical);
  115. EXPECT_EQ(1U, matches.size());
  116. run_check(matches[0], kExeTypeElfX86, 6, 4, 2, 10);
  117. EXPECT_EQ(0U, bad_matches.size());
  118. // Successive elements, no overlap.
  119. EXPECT_TRUE(run_test("1+1=12+1,2+1=13+1"));
  120. EXPECT_EQ(0U, num_identical);
  121. EXPECT_EQ(2U, matches.size());
  122. run_check(matches[0], kExeTypeWin32X86, 1, 1, 12, 1);
  123. run_check(matches[1], kExeTypeWin32X86, 2, 1, 13, 1);
  124. EXPECT_EQ(0U, bad_matches.size());
  125. // Overlap in "old" file is okay.
  126. EXPECT_TRUE(run_test("1+2=12+2,1+2=14+2"));
  127. EXPECT_EQ(0U, num_identical);
  128. EXPECT_EQ(2U, matches.size());
  129. run_check(matches[0], kExeTypeWin32X86, 1, 2, 12, 2);
  130. run_check(matches[1], kExeTypeWin32X86, 1, 2, 14, 2);
  131. EXPECT_EQ(0U, bad_matches.size());
  132. // Entire files: Have unknown type, so are recognized as such, and ignored.
  133. EXPECT_TRUE(run_test("0+10=0+18"));
  134. EXPECT_EQ(0U, num_identical);
  135. EXPECT_EQ(0U, matches.size());
  136. EXPECT_EQ(1U, bad_matches.size());
  137. run_check(bad_matches[0], kExeTypeUnknown, 0, 10, 0, 18);
  138. // Forgive matches that mix known type with unknown type.
  139. EXPECT_TRUE(run_test("1+2=0+18"));
  140. EXPECT_EQ(0U, num_identical);
  141. EXPECT_EQ(0U, matches.size());
  142. EXPECT_EQ(1U, bad_matches.size());
  143. run_check(bad_matches[0], kExeTypeUnknown, 1, 2, 0, 18);
  144. EXPECT_TRUE(run_test("0+10=12+4"));
  145. EXPECT_EQ(0U, num_identical);
  146. EXPECT_EQ(0U, matches.size());
  147. EXPECT_EQ(1U, bad_matches.size());
  148. run_check(bad_matches[0], kExeTypeUnknown, 0, 10, 12, 4);
  149. // Test invalid delimiter.
  150. for (const std::string& imposed_matches :
  151. {"1+2=12+4,4+2=5+2x", "1+2=12+4 4+2=5+2", "1+2=12+4,4+2=5+2 ",
  152. "1+2=12+4 "}) {
  153. EXPECT_FALSE(run_test(imposed_matches));
  154. EXPECT_EQ(ImposedMatchParser::kInvalidDelimiter, status);
  155. }
  156. // Test parse errors, including uint32_t overflow.
  157. for (const std::string& imposed_matches :
  158. {"x1+2=12+4,4+2=5+2,6+4=2+3", "x1+2=12+4,4+2=5+2,6+4=2+3x", ",", " ",
  159. "+2=12+4", "1+2+12+4", "1=2+12+4", " 1+2=12+4", "1+2= 12+4", "1", "1+2",
  160. "1+2=", "1+2=12", "1+2=12+", "4294967296+2=12+4"}) {
  161. EXPECT_FALSE(run_test(imposed_matches));
  162. EXPECT_EQ(ImposedMatchParser::kParseError, status);
  163. }
  164. // Test bound errors, include 0-size.
  165. for (const std::string& imposed_matches :
  166. {"1+10=12+4", "1+2=12+7", "0+11=0+18", "0+12=0+17", "10+1=0+18",
  167. "0+10=18+1", "0+0=0+18", "0+10=0+0", "1000000000+1=0+1000000000"}) {
  168. EXPECT_FALSE(run_test(imposed_matches));
  169. EXPECT_EQ(ImposedMatchParser::kOutOfBound, status);
  170. }
  171. // Test overlap errors. Matches that get ignored are still tested.
  172. for (const std::string& imposed_matches :
  173. {"1+2=12+4,4+2=5+2,6+4=2+4", "0+10=0+18,1+2=12+4", "6+4=2+10,3+2=5+2"}) {
  174. EXPECT_FALSE(run_test(imposed_matches));
  175. EXPECT_EQ(ImposedMatchParser::kOverlapInNew, status);
  176. }
  177. // Test type mismatch errors.
  178. EXPECT_FALSE(run_test("1+2=2+3"));
  179. EXPECT_EQ(ImposedMatchParser::kTypeMismatch, status);
  180. EXPECT_FALSE(run_test("6+4=12+4"));
  181. EXPECT_EQ(ImposedMatchParser::kTypeMismatch, status);
  182. }
  183. } // namespace zucchini