blob: 03c3b236ef643d0516516f904514b8bd2957aef6 [file] [log] [blame]
// 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__