Index: sandbox/linux/seccomp-bpf/codegen_unittest.cc |
diff --git a/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/sandbox/linux/seccomp-bpf/codegen_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d4760a56077ca5e4dcdb4d2f1fdeedf729dc784c |
--- /dev/null |
+++ b/sandbox/linux/seccomp-bpf/codegen_unittest.cc |
@@ -0,0 +1,534 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "sandbox/linux/seccomp-bpf/codegen.h" |
+ |
+#include <errno.h> |
+#include <linux/filter.h> |
+ |
+#include <set> |
+#include <string> |
+#include <vector> |
+ |
+#include "sandbox/linux/seccomp-bpf/basicblock.h" |
+#include "sandbox/linux/seccomp-bpf/errorcode.h" |
+#include "sandbox/linux/seccomp-bpf/instruction.h" |
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
+#include "sandbox/linux/tests/unit_tests.h" |
+ |
+namespace sandbox { |
+ |
+// We want to access some of the private methods in the code generator. We |
+// do so by defining a "friend" that makes these methods public for us. |
+class CodeGenUnittestHelper : public CodeGen { |
+ public: |
+ void FindBranchTargets(const Instruction& instructions, |
+ BranchTargets* branch_targets) { |
+ CodeGen::FindBranchTargets(instructions, branch_targets); |
+ } |
+ |
+ BasicBlock* CutGraphIntoBasicBlocks(Instruction* insns, |
+ const BranchTargets& branch_targets, |
+ TargetsToBlocks* blocks) { |
+ return CodeGen::CutGraphIntoBasicBlocks(insns, branch_targets, blocks); |
+ } |
+ |
+ void MergeTails(TargetsToBlocks* blocks) { CodeGen::MergeTails(blocks); } |
+}; |
+ |
+enum { NO_FLAGS = 0x0000, HAS_MERGEABLE_TAILS = 0x0001, }; |
+ |
+Instruction* SampleProgramOneInstruction(CodeGen* codegen, int* flags) { |
+ // Create the most basic valid BPF program: |
+ // RET 0 |
+ *flags = NO_FLAGS; |
+ return codegen->MakeInstruction(BPF_RET + BPF_K, 0); |
+} |
+ |
+Instruction* SampleProgramSimpleBranch(CodeGen* codegen, int* flags) { |
+ // Create a program with a single branch: |
+ // JUMP if eq 42 then $0 else $1 |
+ // 0: RET 1 |
+ // 1: RET 0 |
+ *flags = NO_FLAGS; |
+ return codegen->MakeInstruction( |
+ BPF_JMP + BPF_JEQ + BPF_K, |
+ 42, |
+ codegen->MakeInstruction(BPF_RET + BPF_K, 1), |
+ codegen->MakeInstruction(BPF_RET + BPF_K, 0)); |
+} |
+ |
+Instruction* SampleProgramAtypicalBranch(CodeGen* codegen, int* flags) { |
+ // Create a program with a single branch: |
+ // JUMP if eq 42 then $0 else $0 |
+ // 0: RET 0 |
+ |
+ // N.B.: As the instructions in both sides of the branch are already |
+ // the same object, we do not actually have any "mergeable" branches. |
+ // This needs to be reflected in our choice of "flags". |
+ *flags = NO_FLAGS; |
+ |
+ Instruction* ret = codegen->MakeInstruction( |
+ BPF_RET + BPF_K, 0); |
+ return codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret); |
+} |
+ |
+Instruction* SampleProgramComplex(CodeGen* codegen, int* flags) { |
+ // Creates a basic BPF program that we'll use to test some of the code: |
+ // JUMP if eq 42 the $0 else $1 (insn6) |
+ // 0: LD 23 (insn5) |
+ // 1: JUMP if eq 42 then $2 else $4 (insn4) |
+ // 2: JUMP to $3 (insn2) |
+ // 3: LD 42 (insn1) |
+ // RET 42 (insn0) |
+ // 4: LD 42 (insn3) |
+ // RET 42 (insn3+) |
+ *flags = HAS_MERGEABLE_TAILS; |
+ |
+ Instruction* insn0 = codegen->MakeInstruction(BPF_RET + BPF_K, 42); |
+ SANDBOX_ASSERT(insn0); |
+ SANDBOX_ASSERT(insn0->code == BPF_RET + BPF_K); |
+ SANDBOX_ASSERT(insn0->next == NULL); |
+ |
+ Instruction* insn1 = |
+ codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0); |
+ SANDBOX_ASSERT(insn1); |
+ SANDBOX_ASSERT(insn1->code == BPF_LD + BPF_W + BPF_ABS); |
+ SANDBOX_ASSERT(insn1->k == 42); |
+ SANDBOX_ASSERT(insn1->next == insn0); |
+ |
+ Instruction* insn2 = codegen->MakeInstruction(BPF_JMP + BPF_JA, 0, insn1); |
+ SANDBOX_ASSERT(insn2); |
+ SANDBOX_ASSERT(insn2->code == BPF_JMP + BPF_JA); |
+ SANDBOX_ASSERT(insn2->jt_ptr == insn1); |
+ |
+ // We explicitly duplicate instructions so that MergeTails() can coalesce |
+ // them later. |
+ Instruction* insn3 = codegen->MakeInstruction( |
+ BPF_LD + BPF_W + BPF_ABS, |
+ 42, |
+ codegen->MakeInstruction(BPF_RET + BPF_K, 42)); |
+ |
+ Instruction* insn4 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3); |
+ SANDBOX_ASSERT(insn4); |
+ SANDBOX_ASSERT(insn4->code == BPF_JMP + BPF_JEQ + BPF_K); |
+ SANDBOX_ASSERT(insn4->k == 42); |
+ SANDBOX_ASSERT(insn4->jt_ptr == insn2); |
+ SANDBOX_ASSERT(insn4->jf_ptr == insn3); |
+ |
+ Instruction* insn5 = |
+ codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4); |
+ SANDBOX_ASSERT(insn5); |
+ SANDBOX_ASSERT(insn5->code == BPF_LD + BPF_W + BPF_ABS); |
+ SANDBOX_ASSERT(insn5->k == 23); |
+ SANDBOX_ASSERT(insn5->next == insn4); |
+ |
+ // Force a basic block that ends in neither a jump instruction nor a return |
+ // instruction. It only contains "insn5". This exercises one of the less |
+ // common code paths in the topo-sort algorithm. |
+ // This also gives us a diamond-shaped pattern in our graph, which stresses |
+ // another aspect of the topo-sort algorithm (namely, the ability to |
+ // correctly count the incoming branches for subtrees that are not disjunct). |
+ Instruction* insn6 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4); |
+ |
+ return insn6; |
+} |
+ |
+Instruction* SampleProgramConfusingTails(CodeGen* codegen, int* flags) { |
+ // This simple program demonstrates https://crbug.com/351103/ |
+ // The two "LOAD 0" instructions are blocks of their own. MergeTails() could |
+ // be tempted to merge them since they are the same. However, they are |
+ // not mergeable because they fall-through to non semantically equivalent |
+ // blocks. |
+ // Without the fix for this bug, this program should trigger the check in |
+ // CompileAndCompare: the serialized graphs from the program and its compiled |
+ // version will differ. |
+ // |
+ // 0) LOAD 1 // ??? |
+ // 1) if A == 0x1; then JMP 2 else JMP 3 |
+ // 2) LOAD 0 // System call number |
+ // 3) if A == 0x2; then JMP 4 else JMP 5 |
+ // 4) LOAD 0 // System call number |
+ // 5) if A == 0x1; then JMP 6 else JMP 7 |
+ // 6) RET 0 |
+ // 7) RET 1 |
+ *flags = NO_FLAGS; |
+ |
+ Instruction* i7 = codegen->MakeInstruction(BPF_RET + BPF_K, 1); |
+ Instruction* i6 = codegen->MakeInstruction(BPF_RET + BPF_K, 0); |
+ Instruction* i5 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); |
+ Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5); |
+ Instruction* i3 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); |
+ Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3); |
+ Instruction* i1 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); |
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); |
+ |
+ return i0; |
+} |
+ |
+Instruction* SampleProgramConfusingTailsBasic(CodeGen* codegen, int* flags) { |
+ // Without the fix for https://crbug.com/351103/, (see |
+ // SampleProgramConfusingTails()), this would generate a cyclic graph and |
+ // crash as the two "LOAD 0" instructions would get merged. |
+ // |
+ // 0) LOAD 1 // ??? |
+ // 1) if A == 0x1; then JMP 2 else JMP 3 |
+ // 2) LOAD 0 // System call number |
+ // 3) if A == 0x2; then JMP 4 else JMP 5 |
+ // 4) LOAD 0 // System call number |
+ // 5) RET 1 |
+ *flags = NO_FLAGS; |
+ |
+ Instruction* i5 = codegen->MakeInstruction(BPF_RET + BPF_K, 1); |
+ Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5); |
+ Instruction* i3 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); |
+ Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3); |
+ Instruction* i1 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); |
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); |
+ |
+ return i0; |
+} |
+ |
+Instruction* SampleProgramConfusingTailsMergeable(CodeGen* codegen, |
+ int* flags) { |
+ // This is similar to SampleProgramConfusingTails(), except that |
+ // instructions 2 and 4 are now RET instructions. |
+ // In PointerCompare(), this exercises the path where two blocks are of the |
+ // same length and identical and the last instruction is a JMP or RET, so the |
+ // following blocks don't need to be looked at and the blocks are mergeable. |
+ // |
+ // 0) LOAD 1 // ??? |
+ // 1) if A == 0x1; then JMP 2 else JMP 3 |
+ // 2) RET 42 |
+ // 3) if A == 0x2; then JMP 4 else JMP 5 |
+ // 4) RET 42 |
+ // 5) if A == 0x1; then JMP 6 else JMP 7 |
+ // 6) RET 0 |
+ // 7) RET 1 |
+ *flags = HAS_MERGEABLE_TAILS; |
+ |
+ Instruction* i7 = codegen->MakeInstruction(BPF_RET + BPF_K, 1); |
+ Instruction* i6 = codegen->MakeInstruction(BPF_RET + BPF_K, 0); |
+ Instruction* i5 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); |
+ Instruction* i4 = codegen->MakeInstruction(BPF_RET + BPF_K, 42); |
+ Instruction* i3 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); |
+ Instruction* i2 = codegen->MakeInstruction(BPF_RET + BPF_K, 42); |
+ Instruction* i1 = |
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); |
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); |
+ |
+ return i0; |
+} |
+void ForAllPrograms(void (*test)(CodeGenUnittestHelper*, Instruction*, int)) { |
+ Instruction* (*function_table[])(CodeGen* codegen, int* flags) = { |
+ SampleProgramOneInstruction, |
+ SampleProgramSimpleBranch, |
+ SampleProgramAtypicalBranch, |
+ SampleProgramComplex, |
+ SampleProgramConfusingTails, |
+ SampleProgramConfusingTailsBasic, |
+ SampleProgramConfusingTailsMergeable, |
+ }; |
+ |
+ for (size_t i = 0; i < arraysize(function_table); ++i) { |
+ CodeGenUnittestHelper codegen; |
+ int flags = NO_FLAGS; |
+ Instruction *prg = function_table[i](&codegen, &flags); |
+ test(&codegen, prg, flags); |
+ } |
+} |
+ |
+void MakeInstruction(CodeGenUnittestHelper* codegen, |
+ Instruction* program, int) { |
+ // Nothing to do here |
+} |
+ |
+SANDBOX_TEST(CodeGen, MakeInstruction) { |
+ ForAllPrograms(MakeInstruction); |
+} |
+ |
+void FindBranchTargets(CodeGenUnittestHelper* codegen, Instruction* prg, int) { |
+ BranchTargets branch_targets; |
+ codegen->FindBranchTargets(*prg, &branch_targets); |
+ |
+ // Verifying the general properties that should be true for every |
+ // well-formed BPF program. |
+ // Perform a depth-first traversal of the BPF program an verify that all |
+ // targets of BPF_JMP instructions are represented in the "branch_targets". |
+ // At the same time, compute a set of both the branch targets and all the |
+ // instructions in the program. |
+ std::vector<Instruction*> stack; |
+ std::set<Instruction*> all_instructions; |
+ std::set<Instruction*> target_instructions; |
+ BranchTargets::const_iterator end = branch_targets.end(); |
+ for (Instruction* insn = prg;;) { |
+ all_instructions.insert(insn); |
+ if (BPF_CLASS(insn->code) == BPF_JMP) { |
+ target_instructions.insert(insn->jt_ptr); |
+ SANDBOX_ASSERT(insn->jt_ptr != NULL); |
+ SANDBOX_ASSERT(branch_targets.find(insn->jt_ptr) != end); |
+ if (BPF_OP(insn->code) != BPF_JA) { |
+ target_instructions.insert(insn->jf_ptr); |
+ SANDBOX_ASSERT(insn->jf_ptr != NULL); |
+ SANDBOX_ASSERT(branch_targets.find(insn->jf_ptr) != end); |
+ stack.push_back(insn->jf_ptr); |
+ } |
+ insn = insn->jt_ptr; |
+ } else if (BPF_CLASS(insn->code) == BPF_RET) { |
+ SANDBOX_ASSERT(insn->next == NULL); |
+ if (stack.empty()) { |
+ break; |
+ } |
+ insn = stack.back(); |
+ stack.pop_back(); |
+ } else { |
+ SANDBOX_ASSERT(insn->next != NULL); |
+ insn = insn->next; |
+ } |
+ } |
+ SANDBOX_ASSERT(target_instructions.size() == branch_targets.size()); |
+ |
+ // We can now subtract the set of the branch targets from the set of all |
+ // instructions. This gives us a set with the instructions that nobody |
+ // ever jumps to. Verify that they are no included in the |
+ // "branch_targets" that FindBranchTargets() computed for us. |
+ Instructions non_target_instructions(all_instructions.size() - |
+ target_instructions.size()); |
+ set_difference(all_instructions.begin(), |
+ all_instructions.end(), |
+ target_instructions.begin(), |
+ target_instructions.end(), |
+ non_target_instructions.begin()); |
+ for (Instructions::const_iterator iter = non_target_instructions.begin(); |
+ iter != non_target_instructions.end(); |
+ ++iter) { |
+ SANDBOX_ASSERT(branch_targets.find(*iter) == end); |
+ } |
+} |
+ |
+SANDBOX_TEST(CodeGen, FindBranchTargets) { ForAllPrograms(FindBranchTargets); } |
+ |
+void CutGraphIntoBasicBlocks(CodeGenUnittestHelper* codegen, |
+ Instruction* prg, |
+ int) { |
+ BranchTargets branch_targets; |
+ codegen->FindBranchTargets(*prg, &branch_targets); |
+ TargetsToBlocks all_blocks; |
+ BasicBlock* first_block = |
+ codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks); |
+ SANDBOX_ASSERT(first_block != NULL); |
+ SANDBOX_ASSERT(first_block->instructions.size() > 0); |
+ Instruction* first_insn = first_block->instructions[0]; |
+ |
+ // Basic blocks are supposed to start with a branch target and end with |
+ // either a jump or a return instruction. It can also end, if the next |
+ // instruction forms the beginning of a new basic block. There should be |
+ // no other jumps or return instructions in the middle of a basic block. |
+ for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin(); |
+ bb_iter != all_blocks.end(); |
+ ++bb_iter) { |
+ BasicBlock* bb = bb_iter->second; |
+ SANDBOX_ASSERT(bb != NULL); |
+ SANDBOX_ASSERT(bb->instructions.size() > 0); |
+ Instruction* insn = bb->instructions[0]; |
+ SANDBOX_ASSERT(insn == first_insn || |
+ branch_targets.find(insn) != branch_targets.end()); |
+ for (Instructions::const_iterator insn_iter = bb->instructions.begin();;) { |
+ insn = *insn_iter; |
+ if (++insn_iter != bb->instructions.end()) { |
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_JMP); |
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_RET); |
+ } else { |
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) == BPF_JMP || |
+ BPF_CLASS(insn->code) == BPF_RET || |
+ branch_targets.find(insn->next) != branch_targets.end()); |
+ break; |
+ } |
+ SANDBOX_ASSERT(branch_targets.find(*insn_iter) == branch_targets.end()); |
+ } |
+ } |
+} |
+ |
+SANDBOX_TEST(CodeGen, CutGraphIntoBasicBlocks) { |
+ ForAllPrograms(CutGraphIntoBasicBlocks); |
+} |
+ |
+void MergeTails(CodeGenUnittestHelper* codegen, Instruction* prg, int flags) { |
+ BranchTargets branch_targets; |
+ codegen->FindBranchTargets(*prg, &branch_targets); |
+ TargetsToBlocks all_blocks; |
+ BasicBlock* first_block = |
+ codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks); |
+ |
+ // The shape of our graph and thus the function of our program should |
+ // still be unchanged after we run MergeTails(). We verify this by |
+ // serializing the graph and verifying that it is still the same. |
+ // We also verify that at least some of the edges changed because of |
+ // tail merging. |
+ std::string graph[2]; |
+ std::string edges[2]; |
+ |
+ // The loop executes twice. After the first run, we call MergeTails() on |
+ // our graph. |
+ for (int i = 0;;) { |
+ // Traverse the entire program in depth-first order. |
+ std::vector<BasicBlock*> stack; |
+ for (BasicBlock* bb = first_block;;) { |
+ // Serialize the instructions in this basic block. In general, we only |
+ // need to serialize "code" and "k"; except for a BPF_JA instruction |
+ // where "k" isn't set. |
+ // The stream of instructions should be unchanged after MergeTails(). |
+ for (Instructions::const_iterator iter = bb->instructions.begin(); |
+ iter != bb->instructions.end(); |
+ ++iter) { |
+ graph[i].append(reinterpret_cast<char*>(&(*iter)->code), |
+ sizeof((*iter)->code)); |
+ if (BPF_CLASS((*iter)->code) != BPF_JMP || |
+ BPF_OP((*iter)->code) != BPF_JA) { |
+ graph[i].append(reinterpret_cast<char*>(&(*iter)->k), |
+ sizeof((*iter)->k)); |
+ } |
+ } |
+ |
+ // Also serialize the addresses the basic blocks as we encounter them. |
+ // This will change as basic blocks are coalesed by MergeTails(). |
+ edges[i].append(reinterpret_cast<char*>(&bb), sizeof(bb)); |
+ |
+ // Depth-first traversal of the graph. We only ever need to look at the |
+ // very last instruction in the basic block, as that is the only one that |
+ // can change code flow. |
+ Instruction* insn = bb->instructions.back(); |
+ if (BPF_CLASS(insn->code) == BPF_JMP) { |
+ // For jump instructions, we need to remember the "false" branch while |
+ // traversing the "true" branch. This is not necessary for BPF_JA which |
+ // only has a single branch. |
+ if (BPF_OP(insn->code) != BPF_JA) { |
+ stack.push_back(all_blocks[insn->jf_ptr]); |
+ } |
+ bb = all_blocks[insn->jt_ptr]; |
+ } else if (BPF_CLASS(insn->code) == BPF_RET) { |
+ // After a BPF_RET, see if we need to back track. |
+ if (stack.empty()) { |
+ break; |
+ } |
+ bb = stack.back(); |
+ stack.pop_back(); |
+ } else { |
+ // For "normal" instructions, just follow to the next basic block. |
+ bb = all_blocks[insn->next]; |
+ } |
+ } |
+ |
+ // Our loop runs exactly two times. |
+ if (++i > 1) { |
+ break; |
+ } |
+ codegen->MergeTails(&all_blocks); |
+ } |
+ SANDBOX_ASSERT(graph[0] == graph[1]); |
+ if (flags & HAS_MERGEABLE_TAILS) { |
+ SANDBOX_ASSERT(edges[0] != edges[1]); |
+ } else { |
+ SANDBOX_ASSERT(edges[0] == edges[1]); |
+ } |
+} |
+ |
+SANDBOX_TEST(CodeGen, MergeTails) { |
+ ForAllPrograms(MergeTails); |
+} |
+ |
+void CompileAndCompare(CodeGenUnittestHelper* codegen, Instruction* prg, int) { |
+ // TopoSortBasicBlocks() has internal checks that cause it to fail, if it |
+ // detects a problem. Typically, if anything goes wrong, this looks to the |
+ // TopoSort algorithm as if there had been cycles in the input data. |
+ // This provides a pretty good unittest. |
+ // We hand-crafted the program returned by SampleProgram() to exercise |
+ // several of the more interesting code-paths. See comments in |
+ // SampleProgram() for details. |
+ // In addition to relying on the internal consistency checks in the compiler, |
+ // we also serialize the graph and the resulting BPF program and compare |
+ // them. With the exception of BPF_JA instructions that might have been |
+ // inserted, both instruction streams should be equivalent. |
+ // As Compile() modifies the instructions, we have to serialize the graph |
+ // before calling Compile(). |
+ std::string source; |
+ Instructions source_stack; |
+ for (const Instruction* insn = prg, *next; insn; insn = next) { |
+ if (BPF_CLASS(insn->code) == BPF_JMP) { |
+ if (BPF_OP(insn->code) == BPF_JA) { |
+ // Do not serialize BPF_JA instructions (see above). |
+ next = insn->jt_ptr; |
+ continue; |
+ } else { |
+ source_stack.push_back(insn->jf_ptr); |
+ next = insn->jt_ptr; |
+ } |
+ } else if (BPF_CLASS(insn->code) == BPF_RET) { |
+ if (source_stack.empty()) { |
+ next = NULL; |
+ } else { |
+ next = source_stack.back(); |
+ source_stack.pop_back(); |
+ } |
+ } else { |
+ next = insn->next; |
+ } |
+ // Only serialize "code" and "k". That's all the information we need to |
+ // compare. The rest of the information is encoded in the order of |
+ // instructions. |
+ source.append(reinterpret_cast<const char*>(&insn->code), |
+ sizeof(insn->code)); |
+ source.append(reinterpret_cast<const char*>(&insn->k), sizeof(insn->k)); |
+ } |
+ |
+ // Compile the program |
+ CodeGen::Program bpf; |
+ codegen->Compile(prg, &bpf); |
+ |
+ // Serialize the resulting BPF instructions. |
+ std::string assembly; |
+ std::vector<int> assembly_stack; |
+ for (int idx = 0; idx >= 0;) { |
+ SANDBOX_ASSERT(idx < (int)bpf.size()); |
+ struct sock_filter& insn = bpf[idx]; |
+ if (BPF_CLASS(insn.code) == BPF_JMP) { |
+ if (BPF_OP(insn.code) == BPF_JA) { |
+ // Do not serialize BPF_JA instructions (see above). |
+ idx += insn.k + 1; |
+ continue; |
+ } else { |
+ assembly_stack.push_back(idx + insn.jf + 1); |
+ idx += insn.jt + 1; |
+ } |
+ } else if (BPF_CLASS(insn.code) == BPF_RET) { |
+ if (assembly_stack.empty()) { |
+ idx = -1; |
+ } else { |
+ idx = assembly_stack.back(); |
+ assembly_stack.pop_back(); |
+ } |
+ } else { |
+ ++idx; |
+ } |
+ // Serialize the same information that we serialized before compilation. |
+ assembly.append(reinterpret_cast<char*>(&insn.code), sizeof(insn.code)); |
+ assembly.append(reinterpret_cast<char*>(&insn.k), sizeof(insn.k)); |
+ } |
+ SANDBOX_ASSERT(source == assembly); |
+} |
+ |
+SANDBOX_TEST(CodeGen, All) { |
+ ForAllPrograms(CompileAndCompare); |
+} |
+ |
+} // namespace sandbox |