xref: /aosp_15_r20/external/google-breakpad/src/processor/postfix_evaluator_unittest.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2010 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 // postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator.
30 //
31 // Author: Mark Mentovai
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>  // Must come first
35 #endif
36 
37 #include <assert.h>
38 #include <stdio.h>
39 
40 #include <map>
41 #include <string>
42 
43 #include "processor/postfix_evaluator-inl.h"
44 
45 #include "common/using_std_string.h"
46 #include "google_breakpad/common/breakpad_types.h"
47 #include "google_breakpad/processor/memory_region.h"
48 #include "processor/logging.h"
49 
50 
51 namespace {
52 
53 
54 using std::map;
55 using google_breakpad::MemoryRegion;
56 using google_breakpad::PostfixEvaluator;
57 
58 
59 // FakeMemoryRegion is used to test PostfixEvaluator's dereference (^)
60 // operator.  The result of dereferencing a value is one greater than
61 // the value.
62 class FakeMemoryRegion : public MemoryRegion {
63  public:
GetBase() const64   virtual uint64_t GetBase() const { return 0; }
GetSize() const65   virtual uint32_t GetSize() const { return 0; }
GetMemoryAtAddress(uint64_t address,uint8_t * value) const66   virtual bool GetMemoryAtAddress(uint64_t address, uint8_t*  value) const {
67     *value = address + 1;
68     return true;
69   }
GetMemoryAtAddress(uint64_t address,uint16_t * value) const70   virtual bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const {
71     *value = address + 1;
72     return true;
73   }
GetMemoryAtAddress(uint64_t address,uint32_t * value) const74   virtual bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const {
75     *value = address + 1;
76     return true;
77   }
GetMemoryAtAddress(uint64_t address,uint64_t * value) const78   virtual bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const {
79     *value = address + 1;
80     return true;
81   }
Print() const82   virtual void Print() const {
83     assert(false);
84   }
85 };
86 
87 
88 struct EvaluateTest {
89   // Expression passed to PostfixEvaluator::Evaluate.
90   const string expression;
91 
92   // True if the expression is expected to be evaluable, false if evaluation
93   // is expected to fail.
94   bool evaluable;
95 };
96 
97 
98 struct EvaluateTestSet {
99   // The dictionary used for all tests in the set.
100   PostfixEvaluator<unsigned int>::DictionaryType* dictionary;
101 
102   // The list of tests.
103   const EvaluateTest* evaluate_tests;
104 
105   // The number of tests.
106   unsigned int evaluate_test_count;
107 
108   // Identifiers and their expected values upon completion of the Evaluate
109   // tests in the set.
110   map<string, unsigned int>* validate_data;
111 };
112 
113 
114 struct EvaluateForValueTest {
115   // Expression passed to PostfixEvaluator::Evaluate.
116   const string expression;
117 
118   // True if the expression is expected to be evaluable, false if evaluation
119   // is expected to fail.
120   bool evaluable;
121 
122   // If evaluable, the value we expect it to yield.
123   unsigned int value;
124 };
125 
RunTests()126 static bool RunTests() {
127   // The first test set checks the basic operations and failure modes.
128   PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
129   const EvaluateTest evaluate_tests_0[] = {
130     { "$rAdd 2 2 + =",     true },   // $rAdd = 2 + 2 = 4
131     { "$rAdd $rAdd 2 + =", true },   // $rAdd = $rAdd + 2 = 6
132     { "$rAdd 2 $rAdd + =", true },   // $rAdd = 2 + $rAdd = 8
133     { "99",                false },  // put some junk on the stack...
134     { "$rAdd2 2 2 + =",    true },   // ...and make sure things still work
135     { "$rAdd2\t2\n2 + =",  true },   // same but with different whitespace
136     { "$rAdd2 2 2 + = ",   true },   // trailing whitespace
137     { " $rAdd2 2 2 + =",   true },   // leading whitespace
138     { "$rAdd2  2 2 +   =", true },   // extra whitespace
139     { "$T0 2 = +",         false },  // too few operands for add
140     { "2 + =",             false },  // too few operands for add
141     { "2 +",               false },  // too few operands for add
142     { "+",                 false },  // too few operands for add
143     { "^",                 false },  // too few operands for dereference
144     { "=",                 false },  // too few operands for assignment
145     { "2 =",               false },  // too few operands for assignment
146     { "2 2 + =",           false },  // too few operands for assignment
147     { "2 2 =",             false },  // can't assign into a literal
148     { "k 2 =",             false },  // can't assign into a constant
149     { "2",                 false },  // leftover data on stack
150     { "2 2 +",             false },  // leftover data on stack
151     { "$rAdd",             false },  // leftover data on stack
152     { "0 $T1 0 0 + =",     false },  // leftover data on stack
153     { "$T2 $T2 2 + =",     false },  // can't operate on an undefined value
154     { "$rMul 9 6 * =",     true },   // $rMul = 9 * 6 = 54
155     { "$rSub 9 6 - =",     true },   // $rSub = 9 - 6 = 3
156     { "$rDivQ 9 6 / =",    true },   // $rDivQ = 9 / 6 = 1
157     { "$rDivM 9 6 % =",    true },   // $rDivM = 9 % 6 = 3
158     { "$rDeref 9 ^ =",     true },   // $rDeref = ^9 = 10 (FakeMemoryRegion)
159     { "$rAlign 36 8 @ =",  true },   // $rAlign = 36 @ 8
160     { "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization
161   };
162   map<string, unsigned int> validate_data_0;
163   validate_data_0["$rAdd"]   = 8;
164   validate_data_0["$rAdd2"]  = 4;
165   validate_data_0["$rSub"]   = 3;
166   validate_data_0["$rMul"]   = 54;
167   validate_data_0["$rDivQ"]  = 1;
168   validate_data_0["$rDivM"]  = 3;
169   validate_data_0["$rDeref"] = 10;
170   validate_data_0["$rAlign"] = 32;
171   validate_data_0["$rAdd3"]  = 4;
172   validate_data_0["$rMul2"]  = 54;
173 
174   // The second test set simulates a couple of MSVC program strings.
175   // The data is fudged a little bit because the tests use FakeMemoryRegion
176   // instead of a real stack snapshot, but the program strings are real and
177   // the implementation doesn't know or care that the data is not real.
178   PostfixEvaluator<unsigned int>::DictionaryType dictionary_1;
179   dictionary_1["$ebp"] = 0xbfff0010;
180   dictionary_1["$eip"] = 0x10000000;
181   dictionary_1["$esp"] = 0xbfff0000;
182   dictionary_1[".cbSavedRegs"] = 4;
183   dictionary_1[".cbParams"] = 4;
184   dictionary_1[".raSearchStart"] = 0xbfff0020;
185   const EvaluateTest evaluate_tests_1[] = {
186     { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
187       "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", true },
188     // Intermediate state: $T0  = 0xbfff0010, $eip = 0xbfff0015,
189     //                     $ebp = 0xbfff0011, $esp = 0xbfff0018,
190     //                     $L   = 0xbfff000c, $P   = 0xbfff001c
191     { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
192       "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =",
193       true },
194     // Intermediate state: $T0  = 0xbfff0011, $eip = 0xbfff0016,
195     //                     $ebp = 0xbfff0012, $esp = 0xbfff0019,
196     //                     $L   = 0xbfff000d, $P   = 0xbfff001d,
197     //                     $ebx = 0xbffefff6
198     { "$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = "
199       "$esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = "
200       "$ebx $T0 28 - ^ =",
201       true }
202   };
203   map<string, unsigned int> validate_data_1;
204   validate_data_1["$T0"]  = 0xbfff0012;
205   validate_data_1["$T1"]  = 0xbfff0020;
206   validate_data_1["$T2"]  = 0xbfff0019;
207   validate_data_1["$eip"] = 0xbfff0021;
208   validate_data_1["$ebp"] = 0xbfff0012;
209   validate_data_1["$esp"] = 0xbfff0024;
210   validate_data_1["$L"]   = 0xbfff000e;
211   validate_data_1["$P"]   = 0xbfff0028;
212   validate_data_1["$ebx"] = 0xbffefff7;
213   validate_data_1[".cbSavedRegs"] = 4;
214   validate_data_1[".cbParams"] = 4;
215 
216   EvaluateTestSet evaluate_test_sets[] = {
217     { &dictionary_0, evaluate_tests_0,
218           sizeof(evaluate_tests_0) / sizeof(EvaluateTest), &validate_data_0 },
219     { &dictionary_1, evaluate_tests_1,
220           sizeof(evaluate_tests_1) / sizeof(EvaluateTest), &validate_data_1 },
221   };
222 
223   unsigned int evaluate_test_set_count = sizeof(evaluate_test_sets) /
224                                          sizeof(EvaluateTestSet);
225 
226   FakeMemoryRegion fake_memory;
227   PostfixEvaluator<unsigned int> postfix_evaluator =
228       PostfixEvaluator<unsigned int>(NULL, &fake_memory);
229 
230   for (unsigned int evaluate_test_set_index = 0;
231        evaluate_test_set_index < evaluate_test_set_count;
232        ++evaluate_test_set_index) {
233     EvaluateTestSet* evaluate_test_set =
234         &evaluate_test_sets[evaluate_test_set_index];
235     const EvaluateTest* evaluate_tests = evaluate_test_set->evaluate_tests;
236     unsigned int evaluate_test_count = evaluate_test_set->evaluate_test_count;
237 
238     // The same dictionary will be used for each test in the set.  Earlier
239     // tests can affect the state of the dictionary for later tests.
240     postfix_evaluator.set_dictionary(evaluate_test_set->dictionary);
241 
242     // Use a new validity dictionary for each test set.
243     PostfixEvaluator<unsigned int>::DictionaryValidityType assigned;
244 
245     for (unsigned int evaluate_test_index = 0;
246          evaluate_test_index < evaluate_test_count;
247          ++evaluate_test_index) {
248       const EvaluateTest* evaluate_test = &evaluate_tests[evaluate_test_index];
249 
250       // Do the test.
251       bool result = postfix_evaluator.Evaluate(evaluate_test->expression,
252                                                &assigned);
253       if (result != evaluate_test->evaluable) {
254         fprintf(stderr, "FAIL: evaluate set %d/%d, test %d/%d, "
255                         "expression \"%s\", expected %s, observed %s\n",
256                 evaluate_test_set_index, evaluate_test_set_count,
257                 evaluate_test_index, evaluate_test_count,
258                 evaluate_test->expression.c_str(),
259                 evaluate_test->evaluable ? "evaluable" : "not evaluable",
260                 result ? "evaluted" : "not evaluated");
261         return false;
262       }
263     }
264 
265     // Validate the results.
266     for (map<string, unsigned int>::const_iterator validate_iterator =
267             evaluate_test_set->validate_data->begin();
268         validate_iterator != evaluate_test_set->validate_data->end();
269         ++validate_iterator) {
270       const string identifier = validate_iterator->first;
271       unsigned int expected_value = validate_iterator->second;
272 
273       map<string, unsigned int>::const_iterator dictionary_iterator =
274           evaluate_test_set->dictionary->find(identifier);
275 
276       // The identifier must exist in the dictionary.
277       if (dictionary_iterator == evaluate_test_set->dictionary->end()) {
278         fprintf(stderr, "FAIL: evaluate test set %d/%d, "
279                         "validate identifier \"%s\", "
280                         "expected %d, observed not found\n",
281                 evaluate_test_set_index, evaluate_test_set_count,
282                 identifier.c_str(), expected_value);
283         return false;
284       }
285 
286       // The value in the dictionary must be the same as the expected value.
287       unsigned int observed_value = dictionary_iterator->second;
288       if (expected_value != observed_value) {
289         fprintf(stderr, "FAIL: evaluate test set %d/%d, "
290                         "validate identifier \"%s\", "
291                         "expected %d, observed %d\n",
292                 evaluate_test_set_index, evaluate_test_set_count,
293                 identifier.c_str(), expected_value, observed_value);
294         return false;
295       }
296 
297       // The value must be set in the "assigned" dictionary if it was a
298       // variable.  It must not have been assigned if it was a constant.
299       bool expected_assigned = identifier[0] == '$';
300       bool observed_assigned = false;
301       PostfixEvaluator<unsigned int>::DictionaryValidityType::const_iterator
302           iterator_assigned = assigned.find(identifier);
303       if (iterator_assigned != assigned.end()) {
304         observed_assigned = iterator_assigned->second;
305       }
306       if (expected_assigned != observed_assigned) {
307         fprintf(stderr, "FAIL: evaluate test set %d/%d, "
308                         "validate assignment of \"%s\", "
309                         "expected %d, observed %d\n",
310                 evaluate_test_set_index, evaluate_test_set_count,
311                 identifier.c_str(), expected_assigned, observed_assigned);
312         return false;
313       }
314     }
315   }
316 
317   // EvaluateForValue tests.
318   PostfixEvaluator<unsigned int>::DictionaryType dictionary_2;
319   dictionary_2["$ebp"] = 0xbfff0010;
320   dictionary_2["$eip"] = 0x10000000;
321   dictionary_2["$esp"] = 0xbfff0000;
322   dictionary_2[".cbSavedRegs"] = 4;
323   dictionary_2[".cbParams"] = 4;
324   dictionary_2[".raSearchStart"] = 0xbfff0020;
325   const EvaluateForValueTest evaluate_for_value_tests_2[] = {
326     { "28907223",               true,  28907223 },      // simple constant
327     { "89854293 40010015 +",    true,  89854293 + 40010015 }, // arithmetic
328     { "-870245 8769343 +",      true,  7899098 },       // negative constants
329     { "$ebp $esp - $eip +",     true,  0x10000010 },    // variable references
330     { "18929794 34015074",      false, 0 },             // too many values
331     { "$ebp $ebp 4 - =",        false, 0 },             // too few values
332     { "$new $eip = $new",       true,  0x10000000 },    // make new variable
333     { "$new 4 +",               true,  0x10000004 },    // see prior assignments
334     { ".cfa 42 = 10",           false, 0 }              // can't set constants
335   };
336   const int evaluate_for_value_tests_2_size
337       = (sizeof (evaluate_for_value_tests_2)
338          / sizeof (evaluate_for_value_tests_2[0]));
339   map<string, unsigned int> validate_data_2;
340   validate_data_2["$eip"] = 0x10000000;
341   validate_data_2["$ebp"] = 0xbfff000c;
342   validate_data_2["$esp"] = 0xbfff0000;
343   validate_data_2["$new"] = 0x10000000;
344   validate_data_2[".cbSavedRegs"] = 4;
345   validate_data_2[".cbParams"] = 4;
346   validate_data_2[".raSearchStart"] = 0xbfff0020;
347 
348   postfix_evaluator.set_dictionary(&dictionary_2);
349   for (int i = 0; i < evaluate_for_value_tests_2_size; i++) {
350     const EvaluateForValueTest* test = &evaluate_for_value_tests_2[i];
351     unsigned int result;
352     if (postfix_evaluator.EvaluateForValue(test->expression, &result)
353         != test->evaluable) {
354       fprintf(stderr, "FAIL: evaluate for value test %d, "
355               "expected evaluation to %s, but it %s\n",
356               i, test->evaluable ? "succeed" : "fail",
357               test->evaluable ? "failed" : "succeeded");
358       return false;
359     }
360     if (test->evaluable && result != test->value) {
361       fprintf(stderr, "FAIL: evaluate for value test %d, "
362               "expected value to be 0x%x, but it was 0x%x\n",
363               i, test->value, result);
364       return false;
365     }
366   }
367 
368   for (map<string, unsigned int>::iterator v = validate_data_2.begin();
369        v != validate_data_2.end(); v++) {
370     map<string, unsigned int>::iterator a = dictionary_2.find(v->first);
371     if (a == dictionary_2.end()) {
372       fprintf(stderr, "FAIL: evaluate for value dictionary check: "
373               "expected dict[\"%s\"] to be 0x%x, but it was unset\n",
374               v->first.c_str(), v->second);
375       return false;
376     } else if (a->second != v->second) {
377       fprintf(stderr, "FAIL: evaluate for value dictionary check: "
378               "expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n",
379               v->first.c_str(), v->second, a->second);
380       return false;
381     }
382     dictionary_2.erase(a);
383   }
384 
385   map<string, unsigned int>::iterator remaining = dictionary_2.begin();
386   if (remaining != dictionary_2.end()) {
387     fprintf(stderr, "FAIL: evaluation of test expressions put unexpected "
388             "values in dictionary:\n");
389     for (; remaining != dictionary_2.end(); remaining++)
390       fprintf(stderr, "    dict[\"%s\"] == 0x%x\n",
391               remaining->first.c_str(), remaining->second);
392     return false;
393   }
394 
395   return true;
396 }
397 
398 
399 }  // namespace
400 
401 
main(int argc,char ** argv)402 int main(int argc, char** argv) {
403   BPLOG_INIT(&argc, &argv);
404 
405   return RunTests() ? 0 : 1;
406 }
407