Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(915)

Unified Diff: sandbox/linux/seccomp-bpf/codegen_unittest.cc

Issue 732423002: Update from chromium https://crrev.com/304586 (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sandbox/linux/seccomp-bpf/codegen.cc ('k') | sandbox/linux/syscall_broker/broker_process.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
index 77e22381395c501062bb07785b83798d99e81c48..3f1a04bd710c5329df43bba2349234b3f3098063 100644
--- a/sandbox/linux/seccomp-bpf/codegen_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/codegen_unittest.cc
@@ -6,116 +6,213 @@
#include <linux/filter.h>
-#include <set>
-#include <string>
+#include <map>
+#include <utility>
#include <vector>
-#include "sandbox/linux/seccomp-bpf/basicblock.h"
-#include "sandbox/linux/seccomp-bpf/errorcode.h"
-#include "sandbox/linux/seccomp-bpf/instruction.h"
+#include "base/macros.h"
+#include "base/md5.h"
+#include "base/strings/string_piece.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace sandbox {
+namespace {
-// 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 {
+// Hash provides an abstraction for building "hash trees" from BPF
+// control flow graphs, and efficiently identifying equivalent graphs.
+//
+// For simplicity, we use MD5, because base happens to provide a
+// convenient API for its use. However, any collision-resistant hash
+// should suffice.
+class Hash {
public:
- using CodeGen::CutGraphIntoBasicBlocks;
- using CodeGen::FindBranchTargets;
- using CodeGen::MergeTails;
-};
+ static const Hash kZero;
+
+ Hash() : digest_() {}
+
+ Hash(uint16_t code,
+ uint32_t k,
+ const Hash& jt = kZero,
+ const Hash& jf = kZero)
+ : digest_() {
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ HashValue(&ctx, code);
+ HashValue(&ctx, k);
+ HashValue(&ctx, jt);
+ HashValue(&ctx, jf);
+ base::MD5Final(&digest_, &ctx);
+ }
-namespace {
+ Hash(const Hash& hash) = default;
+ Hash& operator=(const Hash& rhs) = default;
+
+ friend bool operator==(const Hash& lhs, const Hash& rhs) {
+ return lhs.Base16() == rhs.Base16();
+ }
+ friend bool operator!=(const Hash& lhs, const Hash& rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ template <typename T>
+ void HashValue(base::MD5Context* ctx, const T& value) {
+ base::MD5Update(ctx,
+ base::StringPiece(reinterpret_cast<const char*>(&value),
+ sizeof(value)));
+ }
+
+ std::string Base16() const {
+ return MD5DigestToBase16(digest_);
+ }
-enum {
- NO_FLAGS = 0x0000,
- HAS_MERGEABLE_TAILS = 0x0001,
+ base::MD5Digest digest_;
};
-using ProgramTestFunc = void (*)(CodeGenUnittestHelper* gen,
- Instruction* head,
- int flags);
+const Hash Hash::kZero;
+
+// Sanity check that equality and inequality work on Hash as required.
+TEST(CodeGen, HashSanity) {
+ std::vector<Hash> hashes;
+
+ // Push a bunch of logically distinct hashes.
+ hashes.push_back(Hash::kZero);
+ for (int i = 0; i < 4; ++i) {
+ hashes.push_back(Hash(i & 1, i & 2));
+ }
+ for (int i = 0; i < 16; ++i) {
+ hashes.push_back(Hash(i & 1, i & 2, Hash(i & 4, i & 8)));
+ }
+ for (int i = 0; i < 64; ++i) {
+ hashes.push_back(
+ Hash(i & 1, i & 2, Hash(i & 4, i & 8), Hash(i & 16, i & 32)));
+ }
-class ProgramTest : public ::testing::TestWithParam<ProgramTestFunc> {
+ for (const Hash& a : hashes) {
+ for (const Hash& b : hashes) {
+ // Hashes should equal themselves, but not equal all others.
+ if (&a == &b) {
+ EXPECT_EQ(a, b);
+ } else {
+ EXPECT_NE(a, b);
+ }
+ }
+ }
+}
+
+// ProgramTest provides a fixture for writing compiling sample
+// programs with CodeGen and verifying the linearized output matches
+// the input DAG.
+class ProgramTest : public ::testing::Test {
protected:
- ProgramTest() : gen_() {}
-
- // RunTest runs the test function argument. It should be called at
- // the end of each program test case.
- void RunTest(Instruction* head, int flags) { GetParam()(&gen_, head, flags); }
-
- Instruction* MakeInstruction(uint16_t code,
- uint32_t k,
- Instruction* next = nullptr) {
- Instruction* ret = gen_.MakeInstruction(code, k, next);
- EXPECT_NE(nullptr, ret);
- EXPECT_EQ(code, ret->code);
- EXPECT_EQ(k, ret->k);
+ ProgramTest() : gen_(), node_hashes_() {}
+
+ // MakeInstruction calls CodeGen::MakeInstruction() and associated
+ // the returned address with a hash of the instruction.
+ CodeGen::Node MakeInstruction(uint16_t code,
+ uint32_t k,
+ CodeGen::Node jt = CodeGen::kNullNode,
+ CodeGen::Node jf = CodeGen::kNullNode) {
+ CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf);
+ EXPECT_NE(CodeGen::kNullNode, res);
+
+ Hash digest;
if (code == BPF_JMP + BPF_JA) {
- // Annoying inconsistency.
- EXPECT_EQ(nullptr, ret->next);
- EXPECT_EQ(next, ret->jt_ptr);
+ // TODO(mdempsky): Disallow use of JA.
+ digest = Lookup(jt);
} else {
- EXPECT_EQ(next, ret->next);
- EXPECT_EQ(nullptr, ret->jt_ptr);
+ digest = Hash(code, k, Lookup(jt), Lookup(jf));
}
- EXPECT_EQ(nullptr, ret->jf_ptr);
- return ret;
+ auto it = node_hashes_.insert(std::make_pair(res, digest));
+ EXPECT_EQ(digest, it.first->second);
+
+ return res;
}
- Instruction* MakeInstruction(uint16_t code,
- uint32_t k,
- Instruction* jt,
- Instruction* jf) {
- Instruction* ret = gen_.MakeInstruction(code, k, jt, jf);
- EXPECT_NE(nullptr, ret);
- EXPECT_EQ(code, ret->code);
- EXPECT_EQ(k, ret->k);
- EXPECT_EQ(nullptr, ret->next);
- EXPECT_EQ(jt, ret->jt_ptr);
- EXPECT_EQ(jf, ret->jf_ptr);
- return ret;
+ // RunTest compiles the program and verifies that the output matches
+ // what is expected. It should be called at the end of each program
+ // test case.
+ void RunTest(CodeGen::Node head) {
+ // Compile the program
+ CodeGen::Program program;
+ gen_.Compile(head, &program);
+
+ // Walk the program backwards, and compute the hash for each instruction.
+ std::vector<Hash> prog_hashes(program.size());
+ for (size_t i = program.size(); i > 0; --i) {
+ const sock_filter& insn = program.at(i - 1);
+ Hash& hash = prog_hashes.at(i - 1);
+
+ if (BPF_CLASS(insn.code) == BPF_JMP) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ // The compiler adds JA instructions as needed, so skip them.
+ hash = prog_hashes.at(i + insn.k);
+ } else {
+ hash = Hash(insn.code, insn.k, prog_hashes.at(i + insn.jt),
+ prog_hashes.at(i + insn.jf));
+ }
+ } else if (BPF_CLASS(insn.code) == BPF_RET) {
+ hash = Hash(insn.code, insn.k);
+ } else {
+ hash = Hash(insn.code, insn.k, prog_hashes.at(i));
+ }
+ }
+
+ EXPECT_EQ(Lookup(head), prog_hashes.at(0));
}
private:
- CodeGenUnittestHelper gen_;
+ const Hash& Lookup(CodeGen::Node next) const {
+ if (next == CodeGen::kNullNode) {
+ return Hash::kZero;
+ }
+ auto it = node_hashes_.find(next);
+ if (it == node_hashes_.end()) {
+ ADD_FAILURE() << "No hash found for node " << next;
+ return Hash::kZero;
+ }
+ return it->second;
+ }
+
+ CodeGen gen_;
+ std::map<CodeGen::Node, Hash> node_hashes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProgramTest);
};
-TEST_P(ProgramTest, OneInstruction) {
+TEST_F(ProgramTest, OneInstruction) {
// Create the most basic valid BPF program:
// RET 0
- Instruction* head = MakeInstruction(BPF_RET + BPF_K, 0);
- RunTest(head, NO_FLAGS);
+ CodeGen::Node head = MakeInstruction(BPF_RET + BPF_K, 0);
+ RunTest(head);
}
-TEST_P(ProgramTest, SimpleBranch) {
+TEST_F(ProgramTest, SimpleBranch) {
// Create a program with a single branch:
// JUMP if eq 42 then $0 else $1
// 0: RET 1
// 1: RET 0
- Instruction* head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
- 42,
- MakeInstruction(BPF_RET + BPF_K, 1),
- MakeInstruction(BPF_RET + BPF_K, 0));
- RunTest(head, NO_FLAGS);
+ CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42,
+ MakeInstruction(BPF_RET + BPF_K, 1),
+ MakeInstruction(BPF_RET + BPF_K, 0));
+ RunTest(head);
}
-TEST_P(ProgramTest, AtypicalBranch) {
+TEST_F(ProgramTest, AtypicalBranch) {
// Create a program with a single branch:
// JUMP if eq 42 then $0 else $0
// 0: RET 0
- Instruction* ret = MakeInstruction(BPF_RET + BPF_K, 0);
- Instruction* head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
+ CodeGen::Node ret = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
// 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".
- RunTest(head, NO_FLAGS);
+ RunTest(head);
}
-TEST_P(ProgramTest, Complex) {
+TEST_F(ProgramTest, Complex) {
// 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)
@@ -125,18 +222,18 @@ TEST_P(ProgramTest, Complex) {
// RET 42 (insn0)
// 4: LD 42 (insn3)
// RET 42 (insn3+)
- Instruction* insn0 = MakeInstruction(BPF_RET + BPF_K, 42);
- Instruction* insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
- Instruction* insn2 = MakeInstruction(BPF_JMP + BPF_JA, 0, insn1);
+ CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
+ CodeGen::Node insn2 = MakeInstruction(BPF_JMP + BPF_JA, 0, insn1);
// We explicitly duplicate instructions so that MergeTails() can coalesce
// them later.
- Instruction* insn3 = MakeInstruction(
- BPF_LD + BPF_W + BPF_ABS, 42, MakeInstruction(BPF_RET + BPF_K, 42));
+ CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42,
+ MakeInstruction(BPF_RET + BPF_K, 42));
- Instruction* insn4 =
+ CodeGen::Node insn4 =
MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3);
- Instruction* insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
+ CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, 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
@@ -144,13 +241,13 @@ TEST_P(ProgramTest, Complex) {
// 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::Node insn6 =
MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
- RunTest(insn6, HAS_MERGEABLE_TAILS);
+ RunTest(insn6);
}
-TEST_P(ProgramTest, ConfusingTails) {
+TEST_F(ProgramTest, ConfusingTails) {
// 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
@@ -169,19 +266,19 @@ TEST_P(ProgramTest, ConfusingTails) {
// 6) RET 0
// 7) RET 1
- Instruction* i7 = MakeInstruction(BPF_RET + BPF_K, 1);
- Instruction* i6 = MakeInstruction(BPF_RET + BPF_K, 0);
- Instruction* i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
- Instruction* i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
- Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
- Instruction* i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
- Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
- Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+ CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
- RunTest(i0, NO_FLAGS);
+ RunTest(i0);
}
-TEST_P(ProgramTest, ConfusingTailsBasic) {
+TEST_F(ProgramTest, ConfusingTailsBasic) {
// 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.
@@ -193,17 +290,17 @@ TEST_P(ProgramTest, ConfusingTailsBasic) {
// 4) LOAD 0 // System call number
// 5) RET 1
- Instruction* i5 = MakeInstruction(BPF_RET + BPF_K, 1);
- Instruction* i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
- Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
- Instruction* i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
- Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
- Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+ CodeGen::Node i5 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
- RunTest(i0, NO_FLAGS);
+ RunTest(i0);
}
-TEST_P(ProgramTest, ConfusingTailsMergeable) {
+TEST_F(ProgramTest, ConfusingTailsMergeable) {
// 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
@@ -219,295 +316,17 @@ TEST_P(ProgramTest, ConfusingTailsMergeable) {
// 6) RET 0
// 7) RET 1
- Instruction* i7 = MakeInstruction(BPF_RET + BPF_K, 1);
- Instruction* i6 = MakeInstruction(BPF_RET + BPF_K, 0);
- Instruction* i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
- Instruction* i4 = MakeInstruction(BPF_RET + BPF_K, 42);
- Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
- Instruction* i2 = MakeInstruction(BPF_RET + BPF_K, 42);
- Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
- Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
-
- RunTest(i0, HAS_MERGEABLE_TAILS);
-}
+ CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
-void MakeInstruction(CodeGenUnittestHelper* codegen,
- Instruction* program, int) {
- // Nothing to do here
+ RunTest(i0);
}
-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);
- ASSERT_TRUE(insn->jt_ptr != NULL);
- ASSERT_TRUE(branch_targets.find(insn->jt_ptr) != end);
- if (BPF_OP(insn->code) != BPF_JA) {
- target_instructions.insert(insn->jf_ptr);
- ASSERT_TRUE(insn->jf_ptr != NULL);
- ASSERT_TRUE(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) {
- ASSERT_TRUE(insn->next == NULL);
- if (stack.empty()) {
- break;
- }
- insn = stack.back();
- stack.pop_back();
- } else {
- ASSERT_TRUE(insn->next != NULL);
- insn = insn->next;
- }
- }
- ASSERT_TRUE(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) {
- ASSERT_TRUE(branch_targets.find(*iter) == end);
- }
-}
-
-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);
- ASSERT_TRUE(first_block != NULL);
- ASSERT_TRUE(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;
- ASSERT_TRUE(bb != NULL);
- ASSERT_TRUE(bb->instructions.size() > 0);
- Instruction* insn = bb->instructions[0];
- ASSERT_TRUE(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()) {
- ASSERT_TRUE(BPF_CLASS(insn->code) != BPF_JMP);
- ASSERT_TRUE(BPF_CLASS(insn->code) != BPF_RET);
- } else {
- ASSERT_TRUE(BPF_CLASS(insn->code) == BPF_JMP ||
- BPF_CLASS(insn->code) == BPF_RET ||
- branch_targets.find(insn->next) != branch_targets.end());
- break;
- }
- ASSERT_TRUE(branch_targets.find(*insn_iter) == branch_targets.end());
- }
- }
-}
-
-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);
- }
- ASSERT_TRUE(graph[0] == graph[1]);
- if (flags & HAS_MERGEABLE_TAILS) {
- ASSERT_TRUE(edges[0] != edges[1]);
- } else {
- ASSERT_TRUE(edges[0] == edges[1]);
- }
-}
-
-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;) {
- ASSERT_TRUE(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));
- }
- ASSERT_TRUE(source == assembly);
-}
-
-const ProgramTestFunc kProgramTestFuncs[] = {
- MakeInstruction,
- FindBranchTargets,
- CutGraphIntoBasicBlocks,
- MergeTails,
- CompileAndCompare,
-};
-
-INSTANTIATE_TEST_CASE_P(CodeGen,
- ProgramTest,
- ::testing::ValuesIn(kProgramTestFuncs));
-
} // namespace
-
} // namespace sandbox
« no previous file with comments | « sandbox/linux/seccomp-bpf/codegen.cc ('k') | sandbox/linux/syscall_broker/broker_process.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698