| 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 <linux/filter.h> | 7 #include <linux/filter.h> |
| 8 | 8 |
| 9 #include <map> | 9 #include <map> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 | 109 |
| 110 // MakeInstruction calls CodeGen::MakeInstruction() and associated | 110 // MakeInstruction calls CodeGen::MakeInstruction() and associated |
| 111 // the returned address with a hash of the instruction. | 111 // the returned address with a hash of the instruction. |
| 112 CodeGen::Node MakeInstruction(uint16_t code, | 112 CodeGen::Node MakeInstruction(uint16_t code, |
| 113 uint32_t k, | 113 uint32_t k, |
| 114 CodeGen::Node jt = CodeGen::kNullNode, | 114 CodeGen::Node jt = CodeGen::kNullNode, |
| 115 CodeGen::Node jf = CodeGen::kNullNode) { | 115 CodeGen::Node jf = CodeGen::kNullNode) { |
| 116 CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf); | 116 CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf); |
| 117 EXPECT_NE(CodeGen::kNullNode, res); | 117 EXPECT_NE(CodeGen::kNullNode, res); |
| 118 | 118 |
| 119 Hash digest; | 119 Hash digest(code, k, Lookup(jt), Lookup(jf)); |
| 120 if (code == BPF_JMP + BPF_JA) { | |
| 121 // TODO(mdempsky): Disallow use of JA. | |
| 122 digest = Lookup(jt); | |
| 123 } else { | |
| 124 digest = Hash(code, k, Lookup(jt), Lookup(jf)); | |
| 125 } | |
| 126 auto it = node_hashes_.insert(std::make_pair(res, digest)); | 120 auto it = node_hashes_.insert(std::make_pair(res, digest)); |
| 127 EXPECT_EQ(digest, it.first->second); | 121 EXPECT_EQ(digest, it.first->second); |
| 128 | 122 |
| 129 return res; | 123 return res; |
| 130 } | 124 } |
| 131 | 125 |
| 132 // RunTest compiles the program and verifies that the output matches | 126 // RunTest compiles the program and verifies that the output matches |
| 133 // what is expected. It should be called at the end of each program | 127 // what is expected. It should be called at the end of each program |
| 134 // test case. | 128 // test case. |
| 135 void RunTest(CodeGen::Node head) { | 129 void RunTest(CodeGen::Node head) { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 // JUMP if eq 42 the $0 else $1 (insn6) | 211 // JUMP if eq 42 the $0 else $1 (insn6) |
| 218 // 0: LD 23 (insn5) | 212 // 0: LD 23 (insn5) |
| 219 // 1: JUMP if eq 42 then $2 else $4 (insn4) | 213 // 1: JUMP if eq 42 then $2 else $4 (insn4) |
| 220 // 2: JUMP to $3 (insn2) | 214 // 2: JUMP to $3 (insn2) |
| 221 // 3: LD 42 (insn1) | 215 // 3: LD 42 (insn1) |
| 222 // RET 42 (insn0) | 216 // RET 42 (insn0) |
| 223 // 4: LD 42 (insn3) | 217 // 4: LD 42 (insn3) |
| 224 // RET 42 (insn3+) | 218 // RET 42 (insn3+) |
| 225 CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42); | 219 CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42); |
| 226 CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0); | 220 CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0); |
| 227 CodeGen::Node insn2 = MakeInstruction(BPF_JMP + BPF_JA, 0, insn1); | 221 CodeGen::Node insn2 = insn1; // Implicit JUMP |
| 228 | 222 |
| 229 // We explicitly duplicate instructions so that MergeTails() can coalesce | 223 // We explicitly duplicate instructions to test that they're merged. |
| 230 // them later. | |
| 231 CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, | 224 CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, |
| 232 MakeInstruction(BPF_RET + BPF_K, 42)); | 225 MakeInstruction(BPF_RET + BPF_K, 42)); |
| 226 EXPECT_EQ(insn2, insn3); |
| 233 | 227 |
| 234 CodeGen::Node insn4 = | 228 CodeGen::Node insn4 = |
| 235 MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3); | 229 MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3); |
| 236 CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4); | 230 CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4); |
| 237 | 231 |
| 238 // Force a basic block that ends in neither a jump instruction nor a return | 232 // Force a basic block that ends in neither a jump instruction nor a return |
| 239 // instruction. It only contains "insn5". This exercises one of the less | 233 // instruction. It only contains "insn5". This exercises one of the less |
| 240 // common code paths in the topo-sort algorithm. | 234 // common code paths in the topo-sort algorithm. |
| 241 // This also gives us a diamond-shaped pattern in our graph, which stresses | 235 // This also gives us a diamond-shaped pattern in our graph, which stresses |
| 242 // another aspect of the topo-sort algorithm (namely, the ability to | 236 // another aspect of the topo-sort algorithm (namely, the ability to |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); | 315 CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); |
| 322 CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42); | 316 CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42); |
| 323 CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); | 317 CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); |
| 324 CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42); | 318 CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42); |
| 325 CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); | 319 CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); |
| 326 CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); | 320 CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); |
| 327 | 321 |
| 328 RunTest(i0); | 322 RunTest(i0); |
| 329 } | 323 } |
| 330 | 324 |
| 325 TEST_F(ProgramTest, InstructionFolding) { |
| 326 // Check that simple instructions are folded as expected. |
| 327 CodeGen::Node a = MakeInstruction(BPF_RET + BPF_K, 0); |
| 328 EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0)); |
| 329 CodeGen::Node b = MakeInstruction(BPF_RET + BPF_K, 1); |
| 330 EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0)); |
| 331 EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1)); |
| 332 EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1)); |
| 333 |
| 334 // Check that complex sequences are folded too. |
| 335 CodeGen::Node c = |
| 336 MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, |
| 337 MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b)); |
| 338 EXPECT_EQ(c, MakeInstruction( |
| 339 BPF_LD + BPF_W + BPF_ABS, 0, |
| 340 MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b))); |
| 341 |
| 342 RunTest(c); |
| 343 } |
| 344 |
| 345 TEST_F(ProgramTest, FarBranches) { |
| 346 // BPF instructions use 8-bit fields for branch offsets, which means |
| 347 // branch targets must be within 255 instructions of the branch |
| 348 // instruction. CodeGen abstracts away this detail by inserting jump |
| 349 // instructions as needed, which we test here by generating programs |
| 350 // that should trigger any interesting boundary conditions. |
| 351 |
| 352 // Populate with 260 initial instruction nodes. |
| 353 std::vector<CodeGen::Node> nodes; |
| 354 nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0)); |
| 355 for (size_t i = 1; i < 260; ++i) { |
| 356 nodes.push_back( |
| 357 MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back())); |
| 358 } |
| 359 |
| 360 // Exhaustively test branch offsets near BPF's limits. |
| 361 for (size_t jt = 250; jt < 260; ++jt) { |
| 362 for (size_t jf = 250; jf < 260; ++jf) { |
| 363 nodes.push_back(MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, |
| 364 nodes.rbegin()[jt], nodes.rbegin()[jf])); |
| 365 RunTest(nodes.back()); |
| 366 } |
| 367 } |
| 368 } |
| 369 |
| 331 } // namespace | 370 } // namespace |
| 332 } // namespace sandbox | 371 } // namespace sandbox |
| OLD | NEW |