| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ |
| #define SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <map> |
| #include <vector> |
| |
| #include "base/macros.h" |
| #include "base/tuple.h" |
| #include "sandbox/sandbox_export.h" |
| |
| struct sock_filter; |
| |
| namespace sandbox { |
| |
| // The code generator implements a basic assembler that can convert a |
| // graph of BPF instructions into a well-formed array of BPF |
| // instructions. Most notably, it ensures that jumps are always |
| // forward and don't exceed the limit of 255 instructions imposed by |
| // the instruction set. |
| // |
| // Callers would typically create a new CodeGen object and then use it |
| // to build a DAG of instruction nodes. They'll eventually call |
| // Compile() to convert this DAG to a Program. |
| // |
| // CodeGen gen; |
| // CodeGen::Node allow, branch, dag; |
| // |
| // allow = |
| // gen.MakeInstruction(BPF_RET+BPF_K, |
| // ErrorCode(ErrorCode::ERR_ALLOWED).err())); |
| // branch = |
| // gen.MakeInstruction(BPF_JMP+BPF_EQ+BPF_K, __NR_getpid, |
| // Trap(GetPidHandler, NULL), allow); |
| // dag = |
| // gen.MakeInstruction(BPF_LD+BPF_W+BPF_ABS, |
| // offsetof(struct arch_seccomp_data, nr), branch); |
| // |
| // // Simplified code follows; in practice, it is important to avoid calling |
| // // any C++ destructors after starting the sandbox. |
| // CodeGen::Program program = gen.Compile(dag); |
| // const struct sock_fprog prog = { |
| // static_cast<unsigned short>(program.size()), &program[0] }; |
| // prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); |
| // |
| class SANDBOX_EXPORT CodeGen { |
| public: |
| // A vector of BPF instructions that need to be installed as a filter |
| // program in the kernel. |
| typedef std::vector<struct sock_filter> Program; |
| |
| // Node represents a node within the instruction DAG being compiled. |
| using Node = Program::size_type; |
| |
| // kNullNode represents the "null" node; i.e., the reserved node |
| // value guaranteed to not equal any actual nodes. |
| static const Node kNullNode = -1; |
| |
| CodeGen(); |
| ~CodeGen(); |
| |
| // MakeInstruction creates a node representing the specified |
| // instruction, or returns and existing equivalent node if one |
| // exists. For details on the possible parameters refer to |
| // https://www.kernel.org/doc/Documentation/networking/filter.txt. |
| // TODO(mdempsky): Reconsider using default arguments here. |
| Node MakeInstruction(uint16_t code, |
| uint32_t k, |
| Node jt = kNullNode, |
| Node jf = kNullNode); |
| |
| // Compile linearizes the instruction DAG rooted at |head| into a |
| // program that can be executed by a BPF virtual machine. |
| Program Compile(Node head); |
| |
| private: |
| using MemoKey = base::Tuple<uint16_t, uint32_t, Node, Node>; |
| struct MemoKeyLess { |
| bool operator()(const MemoKey& lhs, const MemoKey& rhs) const; |
| }; |
| |
| // AppendInstruction adds a new instruction, ensuring that |jt| and |
| // |jf| are within range as necessary for |code|. |
| Node AppendInstruction(uint16_t code, uint32_t k, Node jt, Node jf); |
| |
| // WithinRange returns a node equivalent to |next| that is at most |
| // |range| instructions away from the (logical) beginning of the |
| // program. |
| Node WithinRange(Node next, size_t range); |
| |
| // Append appends a new instruction to the physical end (i.e., |
| // logical beginning) of |program_|. |
| Node Append(uint16_t code, uint32_t k, size_t jt, size_t jf); |
| |
| // Offset returns how many instructions exist in |program_| after |target|. |
| size_t Offset(Node target) const; |
| |
| // NOTE: program_ is the compiled program in *reverse*, so that |
| // indices remain stable as we add instructions. |
| Program program_; |
| |
| // equivalent_ stores the most recent semantically-equivalent node for each |
| // instruction in program_. A node is defined as semantically-equivalent to N |
| // if it has the same instruction code and constant as N and its successor |
| // nodes (if any) are semantically-equivalent to N's successor nodes, or |
| // if it's an unconditional jump to a node semantically-equivalent to N. |
| std::vector<Node> equivalent_; |
| |
| std::map<MemoKey, Node, MemoKeyLess> memos_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CodeGen); |
| }; |
| |
| } // namespace sandbox |
| |
| #endif // SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ |