| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "sandbox/linux/seccomp-bpf/codegen.h" | |
| 6 | |
| 7 #include <linux/filter.h> | |
| 8 | |
| 9 #include <limits> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/logging.h" | |
| 13 | |
| 14 // This CodeGen implementation strives for simplicity while still | |
| 15 // generating acceptable BPF programs under typical usage patterns | |
| 16 // (e.g., by PolicyCompiler). | |
| 17 // | |
| 18 // The key to its simplicity is that BPF programs only support forward | |
| 19 // jumps/branches, which allows constraining the DAG construction API | |
| 20 // to make instruction nodes immutable. Immutable nodes admits a | |
| 21 // simple greedy approach of emitting new instructions as needed and | |
| 22 // then reusing existing ones that have already been emitted. This | |
| 23 // cleanly avoids any need to compute basic blocks or apply | |
| 24 // topological sorting because the API effectively sorts instructions | |
| 25 // for us (e.g., before MakeInstruction() can be called to emit a | |
| 26 // branch instruction, it must have already been called for each | |
| 27 // branch path). | |
| 28 // | |
| 29 // This greedy algorithm is not without (theoretical) weakness though: | |
| 30 // | |
| 31 // 1. In the general case, we don't eliminate dead code. If needed, | |
| 32 // we could trace back through the program in Compile() and elide | |
| 33 // any unneeded instructions, but in practice we only emit live | |
| 34 // instructions anyway. | |
| 35 // | |
| 36 // 2. By not dividing instructions into basic blocks and sorting, we | |
| 37 // lose an opportunity to move non-branch/non-return instructions | |
| 38 // adjacent to their successor instructions, which means we might | |
| 39 // need to emit additional jumps. But in practice, they'll | |
| 40 // already be nearby as long as callers don't go out of their way | |
| 41 // to interleave MakeInstruction() calls for unrelated code | |
| 42 // sequences. | |
| 43 | |
| 44 namespace sandbox { | |
| 45 | |
| 46 // kBranchRange is the maximum value that can be stored in | |
| 47 // sock_filter's 8-bit jt and jf fields. | |
| 48 const size_t kBranchRange = std::numeric_limits<uint8_t>::max(); | |
| 49 | |
| 50 const CodeGen::Node CodeGen::kNullNode; | |
| 51 | |
| 52 CodeGen::CodeGen() : program_(), equivalent_(), memos_() { | |
| 53 } | |
| 54 | |
| 55 CodeGen::~CodeGen() { | |
| 56 } | |
| 57 | |
| 58 void CodeGen::Compile(CodeGen::Node head, Program* out) { | |
| 59 DCHECK(out); | |
| 60 out->assign(program_.rbegin() + Offset(head), program_.rend()); | |
| 61 } | |
| 62 | |
| 63 CodeGen::Node CodeGen::MakeInstruction(uint16_t code, | |
| 64 uint32_t k, | |
| 65 Node jt, | |
| 66 Node jf) { | |
| 67 // To avoid generating redundant code sequences, we memoize the | |
| 68 // results from AppendInstruction(). | |
| 69 auto res = memos_.insert(std::make_pair(MemoKey(code, k, jt, jf), kNullNode)); | |
| 70 CodeGen::Node* node = &res.first->second; | |
| 71 if (res.second) { // Newly inserted memo entry. | |
| 72 *node = AppendInstruction(code, k, jt, jf); | |
| 73 } | |
| 74 return *node; | |
| 75 } | |
| 76 | |
| 77 CodeGen::Node CodeGen::AppendInstruction(uint16_t code, | |
| 78 uint32_t k, | |
| 79 Node jt, | |
| 80 Node jf) { | |
| 81 if (BPF_CLASS(code) == BPF_JMP) { | |
| 82 CHECK_NE(BPF_JA, BPF_OP(code)) << "CodeGen inserts JAs as needed"; | |
| 83 | |
| 84 // Optimally adding jumps is rather tricky, so we use a quick | |
| 85 // approximation: by artificially reducing |jt|'s range, |jt| will | |
| 86 // stay within its true range even if we add a jump for |jf|. | |
| 87 jt = WithinRange(jt, kBranchRange - 1); | |
| 88 jf = WithinRange(jf, kBranchRange); | |
| 89 return Append(code, k, Offset(jt), Offset(jf)); | |
| 90 } | |
| 91 | |
| 92 CHECK_EQ(kNullNode, jf) << "Non-branch instructions shouldn't provide jf"; | |
| 93 if (BPF_CLASS(code) == BPF_RET) { | |
| 94 CHECK_EQ(kNullNode, jt) << "Return instructions shouldn't provide jt"; | |
| 95 } else { | |
| 96 // For non-branch/non-return instructions, execution always | |
| 97 // proceeds to the next instruction; so we need to arrange for | |
| 98 // that to be |jt|. | |
| 99 jt = WithinRange(jt, 0); | |
| 100 CHECK_EQ(0U, Offset(jt)) << "ICE: Failed to setup next instruction"; | |
| 101 } | |
| 102 return Append(code, k, 0, 0); | |
| 103 } | |
| 104 | |
| 105 CodeGen::Node CodeGen::WithinRange(Node target, size_t range) { | |
| 106 // Just use |target| if it's already within range. | |
| 107 if (Offset(target) <= range) { | |
| 108 return target; | |
| 109 } | |
| 110 | |
| 111 // Alternatively, look for an equivalent instruction within range. | |
| 112 if (Offset(equivalent_.at(target)) <= range) { | |
| 113 return equivalent_.at(target); | |
| 114 } | |
| 115 | |
| 116 // Otherwise, fall back to emitting a jump instruction. | |
| 117 Node jump = Append(BPF_JMP | BPF_JA, Offset(target), 0, 0); | |
| 118 equivalent_.at(target) = jump; | |
| 119 return jump; | |
| 120 } | |
| 121 | |
| 122 CodeGen::Node CodeGen::Append(uint16_t code, uint32_t k, size_t jt, size_t jf) { | |
| 123 if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) { | |
| 124 CHECK_LE(jt, kBranchRange); | |
| 125 CHECK_LE(jf, kBranchRange); | |
| 126 } else { | |
| 127 CHECK_EQ(0U, jt); | |
| 128 CHECK_EQ(0U, jf); | |
| 129 } | |
| 130 | |
| 131 CHECK_LT(program_.size(), static_cast<size_t>(BPF_MAXINSNS)); | |
| 132 CHECK_EQ(program_.size(), equivalent_.size()); | |
| 133 | |
| 134 Node res = program_.size(); | |
| 135 program_.push_back(sock_filter{code, jt, jf, k}); | |
| 136 equivalent_.push_back(res); | |
| 137 return res; | |
| 138 } | |
| 139 | |
| 140 size_t CodeGen::Offset(Node target) const { | |
| 141 CHECK_LT(target, program_.size()) << "Bogus offset target node"; | |
| 142 return (program_.size() - 1) - target; | |
| 143 } | |
| 144 | |
| 145 // TODO(mdempsky): Move into a general base::Tuple helper library. | |
| 146 bool CodeGen::MemoKeyLess::operator()(const MemoKey& lhs, | |
| 147 const MemoKey& rhs) const { | |
| 148 if (get<0>(lhs) != get<0>(rhs)) | |
| 149 return get<0>(lhs) < get<0>(rhs); | |
| 150 if (get<1>(lhs) != get<1>(rhs)) | |
| 151 return get<1>(lhs) < get<1>(rhs); | |
| 152 if (get<2>(lhs) != get<2>(rhs)) | |
| 153 return get<2>(lhs) < get<2>(rhs); | |
| 154 if (get<3>(lhs) != get<3>(rhs)) | |
| 155 return get<3>(lhs) < get<3>(rhs); | |
| 156 return false; | |
| 157 } | |
| 158 | |
| 159 } // namespace sandbox | |
| OLD | NEW |