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 "components/zucchini/imposed_ensemble_matcher.h"
6*a03ca8b9SKrzysztof Kosiński
7*a03ca8b9SKrzysztof Kosiński #include <algorithm>
8*a03ca8b9SKrzysztof Kosiński #include <sstream>
9*a03ca8b9SKrzysztof Kosiński #include <utility>
10*a03ca8b9SKrzysztof Kosiński
11*a03ca8b9SKrzysztof Kosiński #include "base/bind.h"
12*a03ca8b9SKrzysztof Kosiński #include "base/logging.h"
13*a03ca8b9SKrzysztof Kosiński #include "components/zucchini/io_utils.h"
14*a03ca8b9SKrzysztof Kosiński
15*a03ca8b9SKrzysztof Kosiński namespace zucchini {
16*a03ca8b9SKrzysztof Kosiński
17*a03ca8b9SKrzysztof Kosiński /******** ImposedMatchParser ********/
18*a03ca8b9SKrzysztof Kosiński
19*a03ca8b9SKrzysztof Kosiński ImposedMatchParser::ImposedMatchParser() = default;
20*a03ca8b9SKrzysztof Kosiński
21*a03ca8b9SKrzysztof Kosiński ImposedMatchParser::~ImposedMatchParser() = default;
22*a03ca8b9SKrzysztof Kosiński
Parse(std::string imposed_matches,ConstBufferView old_image,ConstBufferView new_image,ElementDetector && detector)23*a03ca8b9SKrzysztof Kosiński ImposedMatchParser::Status ImposedMatchParser::Parse(
24*a03ca8b9SKrzysztof Kosiński std::string imposed_matches,
25*a03ca8b9SKrzysztof Kosiński ConstBufferView old_image,
26*a03ca8b9SKrzysztof Kosiński ConstBufferView new_image,
27*a03ca8b9SKrzysztof Kosiński ElementDetector&& detector) {
28*a03ca8b9SKrzysztof Kosiński CHECK(matches_.empty());
29*a03ca8b9SKrzysztof Kosiński CHECK(bad_matches_.empty());
30*a03ca8b9SKrzysztof Kosiński
31*a03ca8b9SKrzysztof Kosiński // Parse |imposed_matches| and check bounds.
32*a03ca8b9SKrzysztof Kosiński std::istringstream iss(std::move(imposed_matches));
33*a03ca8b9SKrzysztof Kosiński bool first = true;
34*a03ca8b9SKrzysztof Kosiński iss.peek(); // Makes empty |iss| realize EOF is reached.
35*a03ca8b9SKrzysztof Kosiński while (iss && !iss.eof()) {
36*a03ca8b9SKrzysztof Kosiński // Eat delimiter.
37*a03ca8b9SKrzysztof Kosiński if (first) {
38*a03ca8b9SKrzysztof Kosiński first = false;
39*a03ca8b9SKrzysztof Kosiński } else if (!(iss >> EatChar(','))) {
40*a03ca8b9SKrzysztof Kosiński return kInvalidDelimiter;
41*a03ca8b9SKrzysztof Kosiński }
42*a03ca8b9SKrzysztof Kosiński // Extract parameters for one imposed match.
43*a03ca8b9SKrzysztof Kosiński offset_t old_offset = 0U;
44*a03ca8b9SKrzysztof Kosiński size_t old_size = 0U;
45*a03ca8b9SKrzysztof Kosiński offset_t new_offset = 0U;
46*a03ca8b9SKrzysztof Kosiński size_t new_size = 0U;
47*a03ca8b9SKrzysztof Kosiński if (!(iss >> StrictUInt<offset_t>(old_offset) >> EatChar('+') >>
48*a03ca8b9SKrzysztof Kosiński StrictUInt<size_t>(old_size) >> EatChar('=') >>
49*a03ca8b9SKrzysztof Kosiński StrictUInt<offset_t>(new_offset) >> EatChar('+') >>
50*a03ca8b9SKrzysztof Kosiński StrictUInt<size_t>(new_size))) {
51*a03ca8b9SKrzysztof Kosiński return kParseError;
52*a03ca8b9SKrzysztof Kosiński }
53*a03ca8b9SKrzysztof Kosiński // Check bounds.
54*a03ca8b9SKrzysztof Kosiński if (old_size == 0 || new_size == 0 ||
55*a03ca8b9SKrzysztof Kosiński !old_image.covers({old_offset, old_size}) ||
56*a03ca8b9SKrzysztof Kosiński !new_image.covers({new_offset, new_size})) {
57*a03ca8b9SKrzysztof Kosiński return kOutOfBound;
58*a03ca8b9SKrzysztof Kosiński }
59*a03ca8b9SKrzysztof Kosiński matches_.push_back(
60*a03ca8b9SKrzysztof Kosiński {{{old_offset, old_size}, kExeTypeUnknown}, // Assign type later.
61*a03ca8b9SKrzysztof Kosiński {{new_offset, new_size}, kExeTypeUnknown}}); // Assign type later.
62*a03ca8b9SKrzysztof Kosiński }
63*a03ca8b9SKrzysztof Kosiński // Sort matches by "new" file offsets. This helps with overlap checks.
64*a03ca8b9SKrzysztof Kosiński std::sort(matches_.begin(), matches_.end(),
65*a03ca8b9SKrzysztof Kosiński [](const ElementMatch& match_a, const ElementMatch& match_b) {
66*a03ca8b9SKrzysztof Kosiński return match_a.new_element.offset < match_b.new_element.offset;
67*a03ca8b9SKrzysztof Kosiński });
68*a03ca8b9SKrzysztof Kosiński
69*a03ca8b9SKrzysztof Kosiński // Check for overlaps in "new" file.
70*a03ca8b9SKrzysztof Kosiński if (std::adjacent_find(
71*a03ca8b9SKrzysztof Kosiński matches_.begin(), matches_.end(),
72*a03ca8b9SKrzysztof Kosiński [](const ElementMatch& match1, const ElementMatch& match2) {
73*a03ca8b9SKrzysztof Kosiński return match1.new_element.hi() > match2.new_element.lo();
74*a03ca8b9SKrzysztof Kosiński }) != matches_.end()) {
75*a03ca8b9SKrzysztof Kosiński return kOverlapInNew;
76*a03ca8b9SKrzysztof Kosiński }
77*a03ca8b9SKrzysztof Kosiński
78*a03ca8b9SKrzysztof Kosiński // Compute types and verify consistency. Remove identical matches and matches
79*a03ca8b9SKrzysztof Kosiński // where any sub-image has an unknown type.
80*a03ca8b9SKrzysztof Kosiński size_t write_idx = 0;
81*a03ca8b9SKrzysztof Kosiński for (size_t read_idx = 0; read_idx < matches_.size(); ++read_idx) {
82*a03ca8b9SKrzysztof Kosiński ConstBufferView old_sub_image(
83*a03ca8b9SKrzysztof Kosiński old_image[matches_[read_idx].old_element.region()]);
84*a03ca8b9SKrzysztof Kosiński ConstBufferView new_sub_image(
85*a03ca8b9SKrzysztof Kosiński new_image[matches_[read_idx].new_element.region()]);
86*a03ca8b9SKrzysztof Kosiński // Remove identical match.
87*a03ca8b9SKrzysztof Kosiński if (old_sub_image.equals(new_sub_image)) {
88*a03ca8b9SKrzysztof Kosiński ++num_identical_;
89*a03ca8b9SKrzysztof Kosiński continue;
90*a03ca8b9SKrzysztof Kosiński }
91*a03ca8b9SKrzysztof Kosiński // Check executable types of sub-images.
92*a03ca8b9SKrzysztof Kosiński std::optional<Element> old_element = detector.Run(old_sub_image);
93*a03ca8b9SKrzysztof Kosiński std::optional<Element> new_element = detector.Run(new_sub_image);
94*a03ca8b9SKrzysztof Kosiński if (!old_element || !new_element) {
95*a03ca8b9SKrzysztof Kosiński // Skip unknown types, including those mixed with known types.
96*a03ca8b9SKrzysztof Kosiński bad_matches_.push_back(matches_[read_idx]);
97*a03ca8b9SKrzysztof Kosiński continue;
98*a03ca8b9SKrzysztof Kosiński } else if (old_element->exe_type != new_element->exe_type) {
99*a03ca8b9SKrzysztof Kosiński // Error if types are known, but inconsistent.
100*a03ca8b9SKrzysztof Kosiński return kTypeMismatch;
101*a03ca8b9SKrzysztof Kosiński }
102*a03ca8b9SKrzysztof Kosiński
103*a03ca8b9SKrzysztof Kosiński // Keep match and remove gaps.
104*a03ca8b9SKrzysztof Kosiński matches_[read_idx].old_element.exe_type = old_element->exe_type;
105*a03ca8b9SKrzysztof Kosiński matches_[read_idx].new_element.exe_type = new_element->exe_type;
106*a03ca8b9SKrzysztof Kosiński if (write_idx < read_idx)
107*a03ca8b9SKrzysztof Kosiński matches_[write_idx] = matches_[read_idx];
108*a03ca8b9SKrzysztof Kosiński ++write_idx;
109*a03ca8b9SKrzysztof Kosiński }
110*a03ca8b9SKrzysztof Kosiński matches_.resize(write_idx);
111*a03ca8b9SKrzysztof Kosiński return kSuccess;
112*a03ca8b9SKrzysztof Kosiński }
113*a03ca8b9SKrzysztof Kosiński
114*a03ca8b9SKrzysztof Kosiński /******** ImposedEnsembleMatcher ********/
115*a03ca8b9SKrzysztof Kosiński
ImposedEnsembleMatcher(const std::string & imposed_matches)116*a03ca8b9SKrzysztof Kosiński ImposedEnsembleMatcher::ImposedEnsembleMatcher(
117*a03ca8b9SKrzysztof Kosiński const std::string& imposed_matches)
118*a03ca8b9SKrzysztof Kosiński : imposed_matches_(imposed_matches) {}
119*a03ca8b9SKrzysztof Kosiński
120*a03ca8b9SKrzysztof Kosiński ImposedEnsembleMatcher::~ImposedEnsembleMatcher() = default;
121*a03ca8b9SKrzysztof Kosiński
RunMatch(ConstBufferView old_image,ConstBufferView new_image)122*a03ca8b9SKrzysztof Kosiński bool ImposedEnsembleMatcher::RunMatch(ConstBufferView old_image,
123*a03ca8b9SKrzysztof Kosiński ConstBufferView new_image) {
124*a03ca8b9SKrzysztof Kosiński DCHECK(matches_.empty());
125*a03ca8b9SKrzysztof Kosiński LOG(INFO) << "Start matching.";
126*a03ca8b9SKrzysztof Kosiński ImposedMatchParser parser;
127*a03ca8b9SKrzysztof Kosiński ImposedMatchParser::Status status =
128*a03ca8b9SKrzysztof Kosiński parser.Parse(std::move(imposed_matches_), old_image, new_image,
129*a03ca8b9SKrzysztof Kosiński base::BindRepeating(DetectElementFromDisassembler));
130*a03ca8b9SKrzysztof Kosiński // Print all warnings first.
131*a03ca8b9SKrzysztof Kosiński for (const ElementMatch& bad_match : *parser.mutable_bad_matches())
132*a03ca8b9SKrzysztof Kosiński LOG(WARNING) << "Skipped match with unknown type: " << bad_match.ToString();
133*a03ca8b9SKrzysztof Kosiński if (status != ImposedMatchParser::kSuccess) {
134*a03ca8b9SKrzysztof Kosiński LOG(ERROR) << "Imposed match failed with error code " << status << ".";
135*a03ca8b9SKrzysztof Kosiński return false;
136*a03ca8b9SKrzysztof Kosiński }
137*a03ca8b9SKrzysztof Kosiński num_identical_ = parser.num_identical();
138*a03ca8b9SKrzysztof Kosiński matches_ = std::move(*parser.mutable_matches());
139*a03ca8b9SKrzysztof Kosiński Trim();
140*a03ca8b9SKrzysztof Kosiński return true;
141*a03ca8b9SKrzysztof Kosiński }
142*a03ca8b9SKrzysztof Kosiński
143*a03ca8b9SKrzysztof Kosiński } // namespace zucchini
144