1// Copyright (c) 2012 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 <errno.h>
6
7#include <algorithm>
8#include <set>
9#include <vector>
10
11#include "sandbox/linux/seccomp-bpf/codegen.h"
12#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
13#include "sandbox/linux/tests/unit_tests.h"
14
15namespace sandbox {
16
17class SandboxUnittestHelper : public SandboxBPF {
18 public:
19  typedef SandboxBPF::Program Program;
20};
21
22// We want to access some of the private methods in the code generator. We
23// do so by defining a "friend" that makes these methods public for us.
24class CodeGenUnittestHelper : public CodeGen {
25 public:
26  void FindBranchTargets(const Instruction& instructions,
27                         BranchTargets* branch_targets) {
28    CodeGen::FindBranchTargets(instructions, branch_targets);
29  }
30
31  BasicBlock* CutGraphIntoBasicBlocks(Instruction* insns,
32                                      const BranchTargets& branch_targets,
33                                      TargetsToBlocks* blocks) {
34    return CodeGen::CutGraphIntoBasicBlocks(insns, branch_targets, blocks);
35  }
36
37  void MergeTails(TargetsToBlocks* blocks) { CodeGen::MergeTails(blocks); }
38};
39
40enum { NO_FLAGS = 0x0000, HAS_MERGEABLE_TAILS = 0x0001, };
41
42Instruction* SampleProgramOneInstruction(CodeGen* codegen, int* flags) {
43  // Create the most basic valid BPF program:
44  //    RET ERR_ALLOWED
45  *flags = NO_FLAGS;
46  return codegen->MakeInstruction(BPF_RET + BPF_K,
47                                  ErrorCode(ErrorCode::ERR_ALLOWED));
48}
49
50Instruction* SampleProgramSimpleBranch(CodeGen* codegen, int* flags) {
51  // Create a program with a single branch:
52  //    JUMP if eq 42 then $0 else $1
53  // 0: RET EPERM
54  // 1: RET ERR_ALLOWED
55  *flags = NO_FLAGS;
56  return codegen->MakeInstruction(
57      BPF_JMP + BPF_JEQ + BPF_K,
58      42,
59      codegen->MakeInstruction(BPF_RET + BPF_K, ErrorCode(EPERM)),
60      codegen->MakeInstruction(BPF_RET + BPF_K,
61                               ErrorCode(ErrorCode::ERR_ALLOWED)));
62}
63
64Instruction* SampleProgramAtypicalBranch(CodeGen* codegen, int* flags) {
65  // Create a program with a single branch:
66  //    JUMP if eq 42 then $0 else $0
67  // 0: RET ERR_ALLOWED
68
69  // N.B.: As the instructions in both sides of the branch are already
70  //       the same object, we do not actually have any "mergeable" branches.
71  //       This needs to be reflected in our choice of "flags".
72  *flags = NO_FLAGS;
73
74  Instruction* ret = codegen->MakeInstruction(
75      BPF_RET + BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED));
76  return codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
77}
78
79Instruction* SampleProgramComplex(CodeGen* codegen, int* flags) {
80  // Creates a basic BPF program that we'll use to test some of the code:
81  //    JUMP if eq 42 the $0 else $1     (insn6)
82  // 0: LD 23                            (insn5)
83  // 1: JUMP if eq 42 then $2 else $4    (insn4)
84  // 2: JUMP to $3                       (insn1)
85  // 3: LD 42                            (insn0)
86  //    RET ErrorCode(42)                (insn2)
87  // 4: LD 42                            (insn3)
88  //    RET ErrorCode(42)                (insn3+)
89  *flags = HAS_MERGEABLE_TAILS;
90
91  Instruction* insn0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42);
92  SANDBOX_ASSERT(insn0);
93  SANDBOX_ASSERT(insn0->code == BPF_LD + BPF_W + BPF_ABS);
94  SANDBOX_ASSERT(insn0->k == 42);
95  SANDBOX_ASSERT(insn0->next == NULL);
96
97  Instruction* insn1 = codegen->MakeInstruction(BPF_JMP + BPF_JA, 0, insn0);
98  SANDBOX_ASSERT(insn1);
99  SANDBOX_ASSERT(insn1->code == BPF_JMP + BPF_JA);
100  SANDBOX_ASSERT(insn1->jt_ptr == insn0);
101
102  Instruction* insn2 = codegen->MakeInstruction(BPF_RET + BPF_K, ErrorCode(42));
103  SANDBOX_ASSERT(insn2);
104  SANDBOX_ASSERT(insn2->code == BPF_RET + BPF_K);
105  SANDBOX_ASSERT(insn2->next == NULL);
106
107  // We explicitly duplicate instructions so that MergeTails() can coalesce
108  // them later.
109  Instruction* insn3 = codegen->MakeInstruction(
110      BPF_LD + BPF_W + BPF_ABS,
111      42,
112      codegen->MakeInstruction(BPF_RET + BPF_K, ErrorCode(42)));
113
114  Instruction* insn4 =
115      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn1, insn3);
116  SANDBOX_ASSERT(insn4);
117  SANDBOX_ASSERT(insn4->code == BPF_JMP + BPF_JEQ + BPF_K);
118  SANDBOX_ASSERT(insn4->k == 42);
119  SANDBOX_ASSERT(insn4->jt_ptr == insn1);
120  SANDBOX_ASSERT(insn4->jf_ptr == insn3);
121
122  codegen->JoinInstructions(insn0, insn2);
123  SANDBOX_ASSERT(insn0->next == insn2);
124
125  Instruction* insn5 =
126      codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
127  SANDBOX_ASSERT(insn5);
128  SANDBOX_ASSERT(insn5->code == BPF_LD + BPF_W + BPF_ABS);
129  SANDBOX_ASSERT(insn5->k == 23);
130  SANDBOX_ASSERT(insn5->next == insn4);
131
132  // Force a basic block that ends in neither a jump instruction nor a return
133  // instruction. It only contains "insn5". This exercises one of the less
134  // common code paths in the topo-sort algorithm.
135  // This also gives us a diamond-shaped pattern in our graph, which stresses
136  // another aspect of the topo-sort algorithm (namely, the ability to
137  // correctly count the incoming branches for subtrees that are not disjunct).
138  Instruction* insn6 =
139      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
140
141  return insn6;
142}
143
144Instruction* SampleProgramConfusingTails(CodeGen* codegen, int* flags) {
145  // This simple program demonstrates https://crbug.com/351103/
146  // The two "LOAD 0" instructions are blocks of their own. MergeTails() could
147  // be tempted to merge them since they are the same. However, they are
148  // not mergeable because they fall-through to non semantically equivalent
149  // blocks.
150  // Without the fix for this bug, this program should trigger the check in
151  // CompileAndCompare: the serialized graphs from the program and its compiled
152  // version will differ.
153  //
154  //  0) LOAD 1  // ???
155  //  1) if A == 0x1; then JMP 2 else JMP 3
156  //  2) LOAD 0  // System call number
157  //  3) if A == 0x2; then JMP 4 else JMP 5
158  //  4) LOAD 0  // System call number
159  //  5) if A == 0x1; then JMP 6 else JMP 7
160  //  6) RET 0x50000  // errno = 0
161  //  7) RET 0x50001  // errno = 1
162  *flags = NO_FLAGS;
163
164  Instruction* i7 = codegen->MakeInstruction(BPF_RET, ErrorCode(1));
165  Instruction* i6 = codegen->MakeInstruction(BPF_RET, ErrorCode(0));
166  Instruction* i5 =
167      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
168  Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
169  Instruction* i3 =
170      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
171  Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
172  Instruction* i1 =
173      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
174  Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
175
176  return i0;
177}
178
179Instruction* SampleProgramConfusingTailsBasic(CodeGen* codegen, int* flags) {
180  // Without the fix for https://crbug.com/351103/, (see
181  // SampleProgramConfusingTails()), this would generate a cyclic graph and
182  // crash as the two "LOAD 0" instructions would get merged.
183  //
184  // 0) LOAD 1  // ???
185  // 1) if A == 0x1; then JMP 2 else JMP 3
186  // 2) LOAD 0  // System call number
187  // 3) if A == 0x2; then JMP 4 else JMP 5
188  // 4) LOAD 0  // System call number
189  // 5) RET 0x50001  // errno = 1
190  *flags = NO_FLAGS;
191
192  Instruction* i5 = codegen->MakeInstruction(BPF_RET, ErrorCode(1));
193  Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
194  Instruction* i3 =
195      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
196  Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
197  Instruction* i1 =
198      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
199  Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
200
201  return i0;
202}
203
204Instruction* SampleProgramConfusingTailsMergeable(CodeGen* codegen,
205                                                  int* flags) {
206  // This is similar to SampleProgramConfusingTails(), except that
207  // instructions 2 and 4 are now RET instructions.
208  // In PointerCompare(), this exercises the path where two blocks are of the
209  // same length and identical and the last instruction is a JMP or RET, so the
210  // following blocks don't need to be looked at and the blocks are mergeable.
211  //
212  // 0) LOAD 1  // ???
213  // 1) if A == 0x1; then JMP 2 else JMP 3
214  // 2) RET 0x5002a  // errno = 42
215  // 3) if A == 0x2; then JMP 4 else JMP 5
216  // 4) RET 0x5002a  // errno = 42
217  // 5) if A == 0x1; then JMP 6 else JMP 7
218  // 6) RET 0x50000  // errno = 0
219  // 7) RET 0x50001  // errno = 1
220  *flags = HAS_MERGEABLE_TAILS;
221
222  Instruction* i7 = codegen->MakeInstruction(BPF_RET, ErrorCode(1));
223  Instruction* i6 = codegen->MakeInstruction(BPF_RET, ErrorCode(0));
224  Instruction* i5 =
225      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
226  Instruction* i4 = codegen->MakeInstruction(BPF_RET, ErrorCode(42));
227  Instruction* i3 =
228      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
229  Instruction* i2 = codegen->MakeInstruction(BPF_RET, ErrorCode(42));
230  Instruction* i1 =
231      codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
232  Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
233
234  return i0;
235}
236void ForAllPrograms(void (*test)(CodeGenUnittestHelper*, Instruction*, int)) {
237  Instruction* (*function_table[])(CodeGen* codegen, int* flags) = {
238    SampleProgramOneInstruction,
239    SampleProgramSimpleBranch,
240    SampleProgramAtypicalBranch,
241    SampleProgramComplex,
242    SampleProgramConfusingTails,
243    SampleProgramConfusingTailsBasic,
244    SampleProgramConfusingTailsMergeable,
245  };
246
247  for (size_t i = 0; i < arraysize(function_table); ++i) {
248    CodeGenUnittestHelper codegen;
249    int flags = NO_FLAGS;
250    Instruction *prg = function_table[i](&codegen, &flags);
251    test(&codegen, prg, flags);
252  }
253}
254
255void MakeInstruction(CodeGenUnittestHelper* codegen,
256                     Instruction* program, int) {
257  // Nothing to do here
258}
259
260SANDBOX_TEST(CodeGen, MakeInstruction) {
261  ForAllPrograms(MakeInstruction);
262}
263
264void FindBranchTargets(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
265  BranchTargets branch_targets;
266  codegen->FindBranchTargets(*prg, &branch_targets);
267
268  // Verifying the general properties that should be true for every
269  // well-formed BPF program.
270  // Perform a depth-first traversal of the BPF program an verify that all
271  // targets of BPF_JMP instructions are represented in the "branch_targets".
272  // At the same time, compute a set of both the branch targets and all the
273  // instructions in the program.
274  std::vector<Instruction*> stack;
275  std::set<Instruction*> all_instructions;
276  std::set<Instruction*> target_instructions;
277  BranchTargets::const_iterator end = branch_targets.end();
278  for (Instruction* insn = prg;;) {
279    all_instructions.insert(insn);
280    if (BPF_CLASS(insn->code) == BPF_JMP) {
281      target_instructions.insert(insn->jt_ptr);
282      SANDBOX_ASSERT(insn->jt_ptr != NULL);
283      SANDBOX_ASSERT(branch_targets.find(insn->jt_ptr) != end);
284      if (BPF_OP(insn->code) != BPF_JA) {
285        target_instructions.insert(insn->jf_ptr);
286        SANDBOX_ASSERT(insn->jf_ptr != NULL);
287        SANDBOX_ASSERT(branch_targets.find(insn->jf_ptr) != end);
288        stack.push_back(insn->jf_ptr);
289      }
290      insn = insn->jt_ptr;
291    } else if (BPF_CLASS(insn->code) == BPF_RET) {
292      SANDBOX_ASSERT(insn->next == NULL);
293      if (stack.empty()) {
294        break;
295      }
296      insn = stack.back();
297      stack.pop_back();
298    } else {
299      SANDBOX_ASSERT(insn->next != NULL);
300      insn = insn->next;
301    }
302  }
303  SANDBOX_ASSERT(target_instructions.size() == branch_targets.size());
304
305  // We can now subtract the set of the branch targets from the set of all
306  // instructions. This gives us a set with the instructions that nobody
307  // ever jumps to. Verify that they are no included in the
308  // "branch_targets" that FindBranchTargets() computed for us.
309  Instructions non_target_instructions(all_instructions.size() -
310                                       target_instructions.size());
311  set_difference(all_instructions.begin(),
312                 all_instructions.end(),
313                 target_instructions.begin(),
314                 target_instructions.end(),
315                 non_target_instructions.begin());
316  for (Instructions::const_iterator iter = non_target_instructions.begin();
317       iter != non_target_instructions.end();
318       ++iter) {
319    SANDBOX_ASSERT(branch_targets.find(*iter) == end);
320  }
321}
322
323SANDBOX_TEST(CodeGen, FindBranchTargets) { ForAllPrograms(FindBranchTargets); }
324
325void CutGraphIntoBasicBlocks(CodeGenUnittestHelper* codegen,
326                             Instruction* prg,
327                             int) {
328  BranchTargets branch_targets;
329  codegen->FindBranchTargets(*prg, &branch_targets);
330  TargetsToBlocks all_blocks;
331  BasicBlock* first_block =
332      codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
333  SANDBOX_ASSERT(first_block != NULL);
334  SANDBOX_ASSERT(first_block->instructions.size() > 0);
335  Instruction* first_insn = first_block->instructions[0];
336
337  // Basic blocks are supposed to start with a branch target and end with
338  // either a jump or a return instruction. It can also end, if the next
339  // instruction forms the beginning of a new basic block. There should be
340  // no other jumps or return instructions in the middle of a basic block.
341  for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin();
342       bb_iter != all_blocks.end();
343       ++bb_iter) {
344    BasicBlock* bb = bb_iter->second;
345    SANDBOX_ASSERT(bb != NULL);
346    SANDBOX_ASSERT(bb->instructions.size() > 0);
347    Instruction* insn = bb->instructions[0];
348    SANDBOX_ASSERT(insn == first_insn ||
349                   branch_targets.find(insn) != branch_targets.end());
350    for (Instructions::const_iterator insn_iter = bb->instructions.begin();;) {
351      insn = *insn_iter;
352      if (++insn_iter != bb->instructions.end()) {
353        SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_JMP);
354        SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_RET);
355      } else {
356        SANDBOX_ASSERT(BPF_CLASS(insn->code) == BPF_JMP ||
357                       BPF_CLASS(insn->code) == BPF_RET ||
358                       branch_targets.find(insn->next) != branch_targets.end());
359        break;
360      }
361      SANDBOX_ASSERT(branch_targets.find(*insn_iter) == branch_targets.end());
362    }
363  }
364}
365
366SANDBOX_TEST(CodeGen, CutGraphIntoBasicBlocks) {
367  ForAllPrograms(CutGraphIntoBasicBlocks);
368}
369
370void MergeTails(CodeGenUnittestHelper* codegen, Instruction* prg, int flags) {
371  BranchTargets branch_targets;
372  codegen->FindBranchTargets(*prg, &branch_targets);
373  TargetsToBlocks all_blocks;
374  BasicBlock* first_block =
375      codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
376
377  // The shape of our graph and thus the function of our program should
378  // still be unchanged after we run MergeTails(). We verify this by
379  // serializing the graph and verifying that it is still the same.
380  // We also verify that at least some of the edges changed because of
381  // tail merging.
382  std::string graph[2];
383  std::string edges[2];
384
385  // The loop executes twice. After the first run, we call MergeTails() on
386  // our graph.
387  for (int i = 0;;) {
388    // Traverse the entire program in depth-first order.
389    std::vector<BasicBlock*> stack;
390    for (BasicBlock* bb = first_block;;) {
391      // Serialize the instructions in this basic block. In general, we only
392      // need to serialize "code" and "k"; except for a BPF_JA instruction
393      // where "k" isn't set.
394      // The stream of instructions should be unchanged after MergeTails().
395      for (Instructions::const_iterator iter = bb->instructions.begin();
396           iter != bb->instructions.end();
397           ++iter) {
398        graph[i].append(reinterpret_cast<char*>(&(*iter)->code),
399                        sizeof((*iter)->code));
400        if (BPF_CLASS((*iter)->code) != BPF_JMP ||
401            BPF_OP((*iter)->code) != BPF_JA) {
402          graph[i].append(reinterpret_cast<char*>(&(*iter)->k),
403                          sizeof((*iter)->k));
404        }
405      }
406
407      // Also serialize the addresses the basic blocks as we encounter them.
408      // This will change as basic blocks are coalesed by MergeTails().
409      edges[i].append(reinterpret_cast<char*>(&bb), sizeof(bb));
410
411      // Depth-first traversal of the graph. We only ever need to look at the
412      // very last instruction in the basic block, as that is the only one that
413      // can change code flow.
414      Instruction* insn = bb->instructions.back();
415      if (BPF_CLASS(insn->code) == BPF_JMP) {
416        // For jump instructions, we need to remember the "false" branch while
417        // traversing the "true" branch. This is not necessary for BPF_JA which
418        // only has a single branch.
419        if (BPF_OP(insn->code) != BPF_JA) {
420          stack.push_back(all_blocks[insn->jf_ptr]);
421        }
422        bb = all_blocks[insn->jt_ptr];
423      } else if (BPF_CLASS(insn->code) == BPF_RET) {
424        // After a BPF_RET, see if we need to back track.
425        if (stack.empty()) {
426          break;
427        }
428        bb = stack.back();
429        stack.pop_back();
430      } else {
431        // For "normal" instructions, just follow to the next basic block.
432        bb = all_blocks[insn->next];
433      }
434    }
435
436    // Our loop runs exactly two times.
437    if (++i > 1) {
438      break;
439    }
440    codegen->MergeTails(&all_blocks);
441  }
442  SANDBOX_ASSERT(graph[0] == graph[1]);
443  if (flags & HAS_MERGEABLE_TAILS) {
444    SANDBOX_ASSERT(edges[0] != edges[1]);
445  } else {
446    SANDBOX_ASSERT(edges[0] == edges[1]);
447  }
448}
449
450SANDBOX_TEST(CodeGen, MergeTails) {
451  ForAllPrograms(MergeTails);
452}
453
454void CompileAndCompare(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
455  // TopoSortBasicBlocks() has internal checks that cause it to fail, if it
456  // detects a problem. Typically, if anything goes wrong, this looks to the
457  // TopoSort algorithm as if there had been cycles in the input data.
458  // This provides a pretty good unittest.
459  // We hand-crafted the program returned by SampleProgram() to exercise
460  // several of the more interesting code-paths. See comments in
461  // SampleProgram() for details.
462  // In addition to relying on the internal consistency checks in the compiler,
463  // we also serialize the graph and the resulting BPF program and compare
464  // them. With the exception of BPF_JA instructions that might have been
465  // inserted, both instruction streams should be equivalent.
466  // As Compile() modifies the instructions, we have to serialize the graph
467  // before calling Compile().
468  std::string source;
469  Instructions source_stack;
470  for (const Instruction* insn = prg, *next; insn; insn = next) {
471    if (BPF_CLASS(insn->code) == BPF_JMP) {
472      if (BPF_OP(insn->code) == BPF_JA) {
473        // Do not serialize BPF_JA instructions (see above).
474        next = insn->jt_ptr;
475        continue;
476      } else {
477        source_stack.push_back(insn->jf_ptr);
478        next = insn->jt_ptr;
479      }
480    } else if (BPF_CLASS(insn->code) == BPF_RET) {
481      if (source_stack.empty()) {
482        next = NULL;
483      } else {
484        next = source_stack.back();
485        source_stack.pop_back();
486      }
487    } else {
488      next = insn->next;
489    }
490    // Only serialize "code" and "k". That's all the information we need to
491    // compare. The rest of the information is encoded in the order of
492    // instructions.
493    source.append(reinterpret_cast<const char*>(&insn->code),
494                  sizeof(insn->code));
495    source.append(reinterpret_cast<const char*>(&insn->k), sizeof(insn->k));
496  }
497
498  // Compile the program
499  SandboxUnittestHelper::Program bpf;
500  codegen->Compile(prg, &bpf);
501
502  // Serialize the resulting BPF instructions.
503  std::string assembly;
504  std::vector<int> assembly_stack;
505  for (int idx = 0; idx >= 0;) {
506    SANDBOX_ASSERT(idx < (int)bpf.size());
507    struct sock_filter& insn = bpf[idx];
508    if (BPF_CLASS(insn.code) == BPF_JMP) {
509      if (BPF_OP(insn.code) == BPF_JA) {
510        // Do not serialize BPF_JA instructions (see above).
511        idx += insn.k + 1;
512        continue;
513      } else {
514        assembly_stack.push_back(idx + insn.jf + 1);
515        idx += insn.jt + 1;
516      }
517    } else if (BPF_CLASS(insn.code) == BPF_RET) {
518      if (assembly_stack.empty()) {
519        idx = -1;
520      } else {
521        idx = assembly_stack.back();
522        assembly_stack.pop_back();
523      }
524    } else {
525      ++idx;
526    }
527    // Serialize the same information that we serialized before compilation.
528    assembly.append(reinterpret_cast<char*>(&insn.code), sizeof(insn.code));
529    assembly.append(reinterpret_cast<char*>(&insn.k), sizeof(insn.k));
530  }
531  SANDBOX_ASSERT(source == assembly);
532}
533
534SANDBOX_TEST(CodeGen, All) {
535  ForAllPrograms(CompileAndCompare);
536}
537
538}  // namespace sandbox
539