Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / sandbox / linux / bpf_dsl / codegen.cc
blob793d95d8ac6b84200049811ed2b7215c84cf36fb
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.
5 #include "sandbox/linux/bpf_dsl/codegen.h"
7 #include <linux/filter.h>
9 #include <limits>
10 #include <utility>
12 #include "base/logging.h"
14 // This CodeGen implementation strives for simplicity while still
15 // generating acceptable BPF programs under typical usage patterns
16 // (e.g., by PolicyCompiler).
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).
29 // This greedy algorithm is not without (theoretical) weakness though:
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.
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.
44 namespace sandbox {
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();
50 const CodeGen::Node CodeGen::kNullNode;
52 CodeGen::CodeGen() : program_(), equivalent_(), memos_() {
55 CodeGen::~CodeGen() {
58 void CodeGen::Compile(CodeGen::Node head, Program* out) {
59 DCHECK(out);
60 out->assign(program_.rbegin() + Offset(head), program_.rend());
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);
74 return *node;
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";
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));
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";
102 return Append(code, k, 0, 0);
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;
111 // Alternatively, look for an equivalent instruction within range.
112 if (Offset(equivalent_.at(target)) <= range) {
113 return equivalent_.at(target);
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;
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);
131 CHECK_LT(program_.size(), static_cast<size_t>(BPF_MAXINSNS));
132 CHECK_EQ(program_.size(), equivalent_.size());
134 Node res = program_.size();
135 program_.push_back(sock_filter{code, jt, jf, k});
136 equivalent_.push_back(res);
137 return res;
140 size_t CodeGen::Offset(Node target) const {
141 CHECK_LT(target, program_.size()) << "Bogus offset target node";
142 return (program_.size() - 1) - target;
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;
159 } // namespace sandbox