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