xref: /aosp_15_r20/external/zucchini/disassembler_ztf_unittest.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
1 // 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/disassembler_ztf.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <algorithm>
11 #include <map>
12 #include <set>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/cxx17_backports.h"
17 #include "base/strings/string_piece.h"
18 #include "components/zucchini/buffer_view.h"
19 #include "components/zucchini/element_detection.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 
22 namespace zucchini {
23 
24 namespace {
25 
26 constexpr char kNormalText[] = R"(ZTxt
27 Hello World!
28 This is an example of an absolute reference <<1,1>>
29 And {-01,+05} is an example of a relative ref
30 txTZ
31 TRAILING DATA)";
32 // -1 to exclude null byte.
33 constexpr size_t kNormalTextExtraBytes = base::size("TRAILING DATA") - 1;
34 
35 constexpr char kOutOfBoundsText[] = R"(ZTxt<1,1>
36 Hello World!
37 This is an example of an OOB absolute reference <890,605>
38 And {-050,+100} is an example of an OOB relative ref.
39 but [+00,+10] is valid at least. As is (1,5).
40 <1, 6> and { ,1} aren't nor is {4,5]
41 {7,6}<1,1><2,3>{+00,+00}{004,100}[+00,+60][+000,-100]<-000,-035>(-00,-00)txTZ
42 )";
43 
44 // Converts a raw string into data.
StrToData(base::StringPiece s)45 std::vector<uint8_t> StrToData(base::StringPiece s) {
46   return std::vector<uint8_t>(s.begin(), s.end());
47 }
48 
49 // Compare if |a.location < b.location| as references have unique locations.
50 struct ReferenceCompare {
operator ()zucchini::__anon5f0ed1f70111::ReferenceCompare51   bool operator()(const Reference& a, const Reference& b) const {
52     return a.location < b.location;
53   }
54 };
55 
56 using ReferenceKey =
57     std::pair<DisassemblerZtf::ReferencePool, DisassemblerZtf::ReferenceType>;
58 using ReferenceSets =
59     std::map<ReferenceKey, std::set<Reference, ReferenceCompare>>;
60 
61 // Write references in |refs_to_write| to |image|. Also validate the
62 // disassembler parses |image| such that it is of |expected_size|.
WriteReferences(MutableBufferView image,size_t expected_size,const ReferenceSets & refs_to_write)63 void WriteReferences(MutableBufferView image,
64                      size_t expected_size,
65                      const ReferenceSets& refs_to_write) {
66   EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
67   std::unique_ptr<DisassemblerZtf> dis =
68       Disassembler::Make<DisassemblerZtf>(image);
69   EXPECT_TRUE(dis);
70   EXPECT_EQ(expected_size, dis->size());
71   image.shrink(dis->size());
72   auto reference_groups = dis->MakeReferenceGroups();
73   for (const auto& group : reference_groups) {
74     auto writer = group.GetWriter(image, dis.get());
75     ReferenceKey key = {
76         static_cast<DisassemblerZtf::ReferencePool>(group.pool_tag().value()),
77         static_cast<DisassemblerZtf::ReferenceType>(group.type_tag().value())};
78     if (!refs_to_write.count(key))
79       continue;
80     for (const auto& ref : refs_to_write.at(key))
81       writer->PutNext(ref);
82   }
83 }
84 
85 // Read references in |refs_to_read| from |image|.  Once found
86 // the elements are removed from |refs_to_read|. Also validate the
87 // disassembler parses |image| such that it is of |expected_size|.
ReadReferences(ConstBufferView image,size_t expected_size,ReferenceSets * refs_to_read)88 void ReadReferences(ConstBufferView image,
89                     size_t expected_size,
90                     ReferenceSets* refs_to_read) {
91   EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
92   std::unique_ptr<DisassemblerZtf> dis =
93       Disassembler::Make<DisassemblerZtf>(image);
94   EXPECT_TRUE(dis);
95   EXPECT_EQ(expected_size, dis->size());
96   auto reference_groups = dis->MakeReferenceGroups();
97   for (const auto& group : reference_groups) {
98     auto reader = group.GetReader(dis.get());
99     ReferenceKey key = {
100         static_cast<DisassemblerZtf::ReferencePool>(group.pool_tag().value()),
101         static_cast<DisassemblerZtf::ReferenceType>(group.type_tag().value())};
102     if (!refs_to_read->count(key)) {
103       // No elements of this pool/type pair are expected so assert that none are
104       // found.
105       auto ref = reader->GetNext();
106       EXPECT_FALSE(ref.has_value());
107       continue;
108     }
109     // For each reference remove it from the set if it exists, error if
110     // unexpected references are found.
111     for (auto ref = reader->GetNext(); ref.has_value();
112          ref = reader->GetNext()) {
113       EXPECT_EQ(1UL, refs_to_read->at(key).erase(ref.value()));
114     }
115     EXPECT_EQ(0U, refs_to_read->at(key).size());
116   }
117 }
118 
TestTranslation(const ZtfTranslator & translator,offset_t expected_location,ztf::LineCol lc)119 void TestTranslation(const ZtfTranslator& translator,
120                      offset_t expected_location,
121                      ztf::LineCol lc) {
122   // Check the lc is translated to the expected location.
123   EXPECT_EQ(expected_location, translator.LineColToOffset(lc));
124   auto new_lc = translator.OffsetToLineCol(expected_location);
125   if (expected_location == kInvalidOffset) {
126     EXPECT_FALSE(translator.IsValid(lc));
127     EXPECT_FALSE(new_lc.has_value());
128   } else {
129     EXPECT_TRUE(translator.IsValid(lc));
130     // Check that the reverse is true. |ztf::LineCol{0, 0}| is a sentinel and
131     // should never be valid.
132     EXPECT_EQ(lc.line, new_lc->line);
133     EXPECT_EQ(lc.col, new_lc->col);
134   }
135 }
136 
137 template <typename T>
CountDistinct(const std::vector<T> & v)138 size_t CountDistinct(const std::vector<T>& v) {
139   return std::set<T>(v.begin(), v.end()).size();
140 }
141 
142 }  // namespace
143 
TEST(ZtfTranslatorTest,Translate)144 TEST(ZtfTranslatorTest, Translate) {
145   ztf::dim_t kMaxVal = INT16_MAX;
146   ztf::dim_t kMinVal = INT16_MIN;
147 
148   const std::vector<uint8_t> text(StrToData(kOutOfBoundsText));
149   ConstBufferView image(text.data(), text.size());
150   ZtfTranslator translator;
151   EXPECT_TRUE(translator.Init(image));
152 
153   // Absolute Translations:
154 
155   // Check a bunch of invalid locations.
156   TestTranslation(translator, kInvalidOffset, ztf::LineCol{50, 60});
157   TestTranslation(translator, kInvalidOffset, ztf::LineCol{0, 0});
158   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, 0});
159   TestTranslation(translator, kInvalidOffset, ztf::LineCol{0, 1});
160   TestTranslation(translator, kInvalidOffset, ztf::LineCol{0, 1});
161   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, -1});
162   TestTranslation(translator, kInvalidOffset, ztf::LineCol{-1, 1});
163   TestTranslation(translator, kInvalidOffset, ztf::LineCol{-1, -1});
164   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, kMaxVal});
165   TestTranslation(translator, kInvalidOffset, ztf::LineCol{kMaxVal, 1});
166   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, kMinVal});
167   TestTranslation(translator, kInvalidOffset, ztf::LineCol{kMinVal, 1});
168 
169   // Check the start of the file.
170   TestTranslation(translator, 0, ztf::LineCol{1, 1});
171   TestTranslation(translator, 1, ztf::LineCol{1, 2});
172 
173   // Check the boundary around a newline.
174   TestTranslation(translator, 9, ztf::LineCol{1, 10});
175   TestTranslation(translator, kInvalidOffset, ztf::LineCol{1, 11});
176   TestTranslation(translator, 10, ztf::LineCol{2, 1});
177   TestTranslation(translator, kInvalidOffset, ztf::LineCol{2, 0});
178 
179   // Check the end of the file.
180   TestTranslation(translator, kInvalidOffset, ztf::LineCol{8, 1});
181   TestTranslation(translator, kInvalidOffset, ztf::LineCol{7, 79});
182   // Need to subtract to account for the newline.
183   TestTranslation(translator, text.size() - 1, ztf::LineCol{7, 78});
184   TestTranslation(translator, text.size() - 2, ztf::LineCol{7, 77});
185 
186   // Delta Validity
187   // - Reminder! 0 -> 1:1
188 
189   // Common possible edge cases.
190   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{0, 0}));
191   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{0, 1}));
192   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{1, 0}));
193   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{-1, -1}));
194   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{-1, 0}));
195   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, -1}));
196   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, -1}));
197   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, kMaxVal}));
198   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{kMaxVal, 0}));
199   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, kMinVal}));
200   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{kMinVal, 0}));
201   EXPECT_FALSE(translator.IsValid(233, ztf::DeltaLineCol{0, kMaxVal}));
202   EXPECT_FALSE(translator.IsValid(233, ztf::DeltaLineCol{kMaxVal, 0}));
203   EXPECT_FALSE(translator.IsValid(233, ztf::DeltaLineCol{kMaxVal, kMaxVal}));
204 
205   // Newline area.
206   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{0, 9}));
207   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{0, 10}));
208   EXPECT_FALSE(translator.IsValid(9, ztf::DeltaLineCol{0, 1}));
209   EXPECT_FALSE(translator.IsValid(9, ztf::DeltaLineCol{-1, 0}));
210   EXPECT_FALSE(translator.IsValid(9, ztf::DeltaLineCol{1, -10}));
211   EXPECT_TRUE(translator.IsValid(9, ztf::DeltaLineCol{1, -9}));
212 
213   // End of file.
214   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{7, 78}));
215   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{7, 77}));
216   EXPECT_FALSE(translator.IsValid(0, ztf::DeltaLineCol{6, 78}));
217   EXPECT_TRUE(translator.IsValid(0, ztf::DeltaLineCol{6, 77}));
218   EXPECT_FALSE(translator.IsValid(text.size() - 1, ztf::DeltaLineCol{0, 1}));
219   EXPECT_FALSE(translator.IsValid(text.size() - 1, ztf::DeltaLineCol{1, 0}));
220   EXPECT_TRUE(translator.IsValid(text.size() - 2, ztf::DeltaLineCol{0, 1}));
221   EXPECT_FALSE(translator.IsValid(text.size() - 2, ztf::DeltaLineCol{1, 0}));
222 }
223 
224 // Ensures that ReferenceGroups from DisassemblerZtf::MakeReferenceGroups()
225 // cover each non-sentinel element in ReferenceType in order, exactly once. Also
226 // ensures that the ReferenceType elements are grouped by ReferencePool, and
227 // listed in increasing order.
TEST(DisassemblerZtfTest,ReferenceGroups)228 TEST(DisassemblerZtfTest, ReferenceGroups) {
229   std::vector<uint32_t> pool_list;
230   std::vector<uint32_t> type_list;
231   DisassemblerZtf dis;
232   for (ReferenceGroup group : dis.MakeReferenceGroups()) {
233     pool_list.push_back(static_cast<uint32_t>(group.pool_tag().value()));
234     type_list.push_back(static_cast<uint32_t>(group.type_tag().value()));
235   }
236 
237   // Check ReferenceByte coverage.
238   constexpr size_t kNumTypes = DisassemblerZtf::kNumTypes;
239   EXPECT_EQ(kNumTypes, type_list.size());
240   EXPECT_EQ(kNumTypes, CountDistinct(type_list));
241   EXPECT_TRUE(std::is_sorted(type_list.begin(), type_list.end()));
242 
243   // Check that ReferenceType elements are grouped by ReferencePool. Note that
244   // repeats can occur, and pools can be skipped.
245   EXPECT_TRUE(std::is_sorted(pool_list.begin(), pool_list.end()));
246 }
247 
TEST(DisassemblerZtfTest,BadMagic)248 TEST(DisassemblerZtfTest, BadMagic) {
249   // Test a case where there is no header so a disassembler cannot be created.
250   {
251     const std::vector<uint8_t> text(StrToData("foobarbaz bazbarfoo"));
252     ConstBufferView image(text.data(), text.size());
253     EXPECT_FALSE(DisassemblerZtf::QuickDetect(image));
254     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
255   }
256   // Test a case where there is no footer so a disassembler cannot be created.
257   {
258     const std::vector<uint8_t> text(StrToData("ZTxtfoobarbaz bazbarfootxTZ"));
259     ConstBufferView image(text.data(), text.size());
260     EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
261     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
262   }
263   // Test when the header is too short
264   {
265     const std::vector<uint8_t> text(StrToData("ZTxtxTZ\n"));
266     ConstBufferView image(text.data(), text.size());
267     EXPECT_FALSE(DisassemblerZtf::QuickDetect(image));
268     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
269   }
270 }
271 
TEST(DisassemblerZtfTest,ZtfSizeBound)272 TEST(DisassemblerZtfTest, ZtfSizeBound) {
273   {
274     std::vector<uint8_t> text(StrToData("ZTxt"));
275     std::fill_n(std::back_inserter(text), ztf::kMaxDimValue - 2, '\n');
276     text.insert(text.end(), {'t', 'x', 'T', 'Z', '\n'});
277     ConstBufferView image(text.data(), text.size());
278     EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
279     EXPECT_TRUE(Disassembler::Make<DisassemblerZtf>(image));
280   }
281   {
282     std::vector<uint8_t> text(StrToData("ZTxt"));
283     std::fill_n(std::back_inserter(text), ztf::kMaxDimValue - 1, '\n');
284     text.insert(text.end(), {'t', 'x', 'T', 'Z', '\n'});
285     ConstBufferView image(text.data(), text.size());
286     EXPECT_TRUE(DisassemblerZtf::QuickDetect(image));
287     EXPECT_FALSE(Disassembler::Make<DisassemblerZtf>(image));
288   }
289 }
290 
291 // Try reading from a well formed source.
TEST(DisassemblerZtfTest,NormalRead)292 TEST(DisassemblerZtfTest, NormalRead) {
293   const std::vector<uint8_t> text(StrToData(kNormalText));
294   ConstBufferView image(text.data(), text.size());
295   ReferenceSets expected_map = {
296       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1},
297        {Reference({63, 0})}},
298       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesRel2},
299        {Reference({74, 27})}},
300   };
301   ReadReferences(image, text.size() - kNormalTextExtraBytes, &expected_map);
302 }
303 
304 // Try writing to a well formed source and ensure that what is read back
305 // reflects what was written.
TEST(DisassemblerZtfTest,NormalWrite)306 TEST(DisassemblerZtfTest, NormalWrite) {
307   std::vector<uint8_t> mutable_text(StrToData(kNormalText));
308   MutableBufferView image(mutable_text.data(), mutable_text.size());
309   ReferenceSets change_map = {
310       {{DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
311        {Reference({63, 71})}},
312       {{DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel3},
313        {Reference({74, 4})}},
314   };
315   WriteReferences(image, mutable_text.size() - kNormalTextExtraBytes,
316                   change_map);
317 
318   // As a sanity check see if a disassembler can identify the same references.
319   ConstBufferView const_image(image);
320   ReadReferences(const_image, mutable_text.size() - kNormalTextExtraBytes,
321                  &change_map);
322 }
323 
324 // Try reading from a source rife with errors.
TEST(DisassemblerZtfTest,ReadOutOfBoundsRefs)325 TEST(DisassemblerZtfTest, ReadOutOfBoundsRefs) {
326   const std::vector<uint8_t> text(StrToData(kOutOfBoundsText));
327   ConstBufferView image(text.data(), text.size());
328   ReferenceSets expected_map = {
329       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1},
330        {Reference({4, 0}), Reference({223, 0}), Reference({228, 12})}},
331       {{DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel2},
332        {Reference({139, 149})}},
333       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesAbs1},
334        {Reference({218, 223})}},
335       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesRel2},
336        {Reference({233, 233})}},
337       {{DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
338        {Reference({174, 4})}},
339   };
340   ReadReferences(image, text.size(), &expected_map);
341 }
342 
343 // Try writing to a source rife with errors (malformed references or ones that
344 // reference non-existent locations. Some of the values written are also bad. To
345 // validate check if the expected set of references are read back.
TEST(DisassemblerZtfTest,WriteOutOfBoundsRefs)346 TEST(DisassemblerZtfTest, WriteOutOfBoundsRefs) {
347   // Replace |old_val| (provided for checking) with |new_val| in |set|.
348   auto update_set = [](Reference old_ref, Reference new_ref,
349                        std::set<Reference, ReferenceCompare>* set) {
350     auto it = set->find(old_ref);
351     EXPECT_NE(it, set->cend());
352     EXPECT_EQ(*it, old_ref);
353     set->erase(it);
354     set->insert(new_ref);
355   };
356 
357   // Replace |old_val| (provided for checking) with |new_val| in the set which
358   // is the value corresponding to |key| in |map|.
359   auto update_map =
360       [update_set](
361           ReferenceKey key, Reference old_ref, Reference new_ref,
362           std::map<ReferenceKey, std::set<Reference, ReferenceCompare>>* map) {
363         auto it = map->find(key);
364         EXPECT_NE(it, map->cend());
365         update_set(old_ref, new_ref, &(it->second));
366       };
367 
368   std::vector<uint8_t> mutable_text(StrToData(kOutOfBoundsText));
369   MutableBufferView image(mutable_text.data(), mutable_text.size());
370   ReferenceSets change_map = {
371       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1},
372        {Reference({223, 15}), Reference({228, 13})}},
373       {{DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs3},
374        {Reference({4, 50})}},  // This should fail to write.
375       {{DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel2},
376        {Reference({139, static_cast<offset_t>(
377                             mutable_text.size())})}},  // This should fail.
378       {{DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
379        {Reference({174, 21})}},  // This should fail.
380       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesAbs1},
381        {Reference({218, 219})}},
382       {{DisassemblerZtf::kBraces, DisassemblerZtf::kBracesRel2},
383        {Reference({233, 174})}},
384   };
385   WriteReferences(image, mutable_text.size(), change_map);
386 
387   // As a sanity check see if a disassembler can identify the same references
388   // (excluding the invalid ones).
389   change_map.erase(change_map.find(
390       {DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs3}));
391   change_map.at({DisassemblerZtf::kAngles, DisassemblerZtf::kAnglesAbs1})
392       .emplace(Reference{4, 0});
393   update_map({DisassemblerZtf::kBrackets, DisassemblerZtf::kBracketsRel2},
394              Reference({139, static_cast<offset_t>(mutable_text.size())}),
395              Reference({139, 149}), &change_map);
396   update_map({DisassemblerZtf::kParentheses, DisassemblerZtf::kParenthesesAbs1},
397              Reference({174, 21}), Reference({174, 4}), &change_map);
398   ConstBufferView const_image(image);
399   ReadReferences(const_image, mutable_text.size(), &change_map);
400 }
401 
402 }  // namespace zucchini
403