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