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 |
deleted file mode 100644 |
index 8d4bf5df35f60f9445833fdccd419e66fa2b048a..0000000000000000000000000000000000000000 |
--- a/sandbox/linux/seccomp-bpf/codegen_unittest.cc |
+++ /dev/null |
@@ -1,403 +0,0 @@ |
-// 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 <linux/filter.h> |
- |
-#include <map> |
-#include <utility> |
-#include <vector> |
- |
-#include "base/macros.h" |
-#include "base/md5.h" |
-#include "base/strings/string_piece.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
- |
-namespace sandbox { |
-namespace { |
- |
-// 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: |
- 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); |
- } |
- |
- 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_); |
- } |
- |
- base::MD5Digest digest_; |
-}; |
- |
-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))); |
- } |
- |
- 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_(), 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(code, k, Lookup(jt), Lookup(jf)); |
- auto it = node_hashes_.insert(std::make_pair(res, digest)); |
- EXPECT_EQ(digest, it.first->second); |
- |
- return res; |
- } |
- |
- // 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: |
- 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_F(ProgramTest, OneInstruction) { |
- // Create the most basic valid BPF program: |
- // RET 0 |
- CodeGen::Node head = MakeInstruction(BPF_RET + BPF_K, 0); |
- RunTest(head); |
-} |
- |
-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 |
- 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_F(ProgramTest, AtypicalBranch) { |
- // Create a program with a single branch: |
- // JUMP if eq 42 then $0 else $0 |
- // 0: RET 0 |
- |
- 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); |
-} |
- |
-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) |
- // 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+) |
- CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42); |
- CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0); |
- CodeGen::Node insn2 = insn1; // Implicit JUMP |
- |
- // We explicitly duplicate instructions to test that they're merged. |
- CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, |
- MakeInstruction(BPF_RET + BPF_K, 42)); |
- EXPECT_EQ(insn2, insn3); |
- |
- CodeGen::Node insn4 = |
- MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3); |
- 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 |
- // 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). |
- CodeGen::Node insn6 = |
- MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4); |
- |
- RunTest(insn6); |
-} |
- |
-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 |
- // 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 |
- |
- 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); |
-} |
- |
-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. |
- // |
- // 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 |
- |
- 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); |
-} |
- |
-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 |
- // 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 |
- |
- 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); |
- |
- RunTest(i0); |
-} |
- |
-TEST_F(ProgramTest, InstructionFolding) { |
- // Check that simple instructions are folded as expected. |
- CodeGen::Node a = MakeInstruction(BPF_RET + BPF_K, 0); |
- EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0)); |
- CodeGen::Node b = MakeInstruction(BPF_RET + BPF_K, 1); |
- EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0)); |
- EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1)); |
- EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1)); |
- |
- // Check that complex sequences are folded too. |
- CodeGen::Node c = |
- MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, |
- MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b)); |
- EXPECT_EQ(c, MakeInstruction( |
- BPF_LD + BPF_W + BPF_ABS, 0, |
- MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b))); |
- |
- RunTest(c); |
-} |
- |
-TEST_F(ProgramTest, FarBranches) { |
- // BPF instructions use 8-bit fields for branch offsets, which means |
- // branch targets must be within 255 instructions of the branch |
- // instruction. CodeGen abstracts away this detail by inserting jump |
- // instructions as needed, which we test here by generating programs |
- // that should trigger any interesting boundary conditions. |
- |
- // Populate with 260 initial instruction nodes. |
- std::vector<CodeGen::Node> nodes; |
- nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0)); |
- for (size_t i = 1; i < 260; ++i) { |
- nodes.push_back( |
- MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back())); |
- } |
- |
- // Exhaustively test branch offsets near BPF's limits. |
- for (size_t jt = 250; jt < 260; ++jt) { |
- for (size_t jf = 250; jf < 260; ++jf) { |
- nodes.push_back(MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, |
- nodes.rbegin()[jt], nodes.rbegin()[jf])); |
- RunTest(nodes.back()); |
- } |
- } |
-} |
- |
-TEST_F(ProgramTest, JumpReuse) { |
- // As a code size optimization, we try to reuse jumps when possible |
- // instead of emitting new ones. Here we make sure that optimization |
- // is working as intended. |
- // |
- // NOTE: To simplify testing, we rely on implementation details |
- // about what CodeGen::Node values indicate (i.e., vector indices), |
- // but CodeGen users should treat them as opaque values. |
- |
- // Populate with 260 initial instruction nodes. |
- std::vector<CodeGen::Node> nodes; |
- nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0)); |
- for (size_t i = 1; i < 260; ++i) { |
- nodes.push_back( |
- MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back())); |
- } |
- |
- // Branching to nodes[0] and nodes[1] should require 3 new |
- // instructions: two far jumps plus the branch itself. |
- CodeGen::Node one = |
- MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, nodes[0], nodes[1]); |
- EXPECT_EQ(nodes.back() + 3, one); // XXX: Implementation detail! |
- RunTest(one); |
- |
- // Branching again to the same target nodes should require only one |
- // new instruction, as we can reuse the previous branch's jumps. |
- CodeGen::Node two = |
- MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, nodes[0], nodes[1]); |
- EXPECT_EQ(one + 1, two); // XXX: Implementation detail! |
- RunTest(two); |
-} |
- |
-} // namespace |
-} // namespace sandbox |