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