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