xref: /aosp_15_r20/external/zucchini/imposed_ensemble_matcher.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 "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