| 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
|
|
|