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