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

Side by Side Diff: sandbox/linux/seccomp-bpf/codegen_unittest.cc

Issue 687813003: CodeGen: refactor unit tests using parameterized tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698