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