blob: b446b412e9ca591b2f8362ebbddb16a46ccbae51 [file] [log] [blame]
Luis Hector Chavez1ac9eca2018-12-04 21:28:52 -08001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2018 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17"""Tools to interact with BPF programs."""
18
19import abc
20import collections
21import struct
22
23# This comes from syscall(2). Most architectures only support passing 6 args to
24# syscalls, but ARM supports passing 7.
25MAX_SYSCALL_ARGUMENTS = 7
26
27# The following fields were copied from <linux/bpf_common.h>:
28
29# Instruction classes
30BPF_LD = 0x00
31BPF_LDX = 0x01
32BPF_ST = 0x02
33BPF_STX = 0x03
34BPF_ALU = 0x04
35BPF_JMP = 0x05
36BPF_RET = 0x06
37BPF_MISC = 0x07
38
39# LD/LDX fields.
40# Size
41BPF_W = 0x00
42BPF_H = 0x08
43BPF_B = 0x10
44# Mode
45BPF_IMM = 0x00
46BPF_ABS = 0x20
47BPF_IND = 0x40
48BPF_MEM = 0x60
49BPF_LEN = 0x80
50BPF_MSH = 0xa0
51
52# JMP fields.
53BPF_JA = 0x00
54BPF_JEQ = 0x10
55BPF_JGT = 0x20
56BPF_JGE = 0x30
57BPF_JSET = 0x40
58
59# Source
60BPF_K = 0x00
61BPF_X = 0x08
62
63BPF_MAXINSNS = 4096
64
65# The following fields were copied from <linux/seccomp.h>:
66
67SECCOMP_RET_KILL_PROCESS = 0x80000000
68SECCOMP_RET_KILL_THREAD = 0x00000000
69SECCOMP_RET_TRAP = 0x00030000
70SECCOMP_RET_ERRNO = 0x00050000
71SECCOMP_RET_TRACE = 0x7ff00000
72SECCOMP_RET_LOG = 0x7ffc0000
73SECCOMP_RET_ALLOW = 0x7fff0000
74
75SECCOMP_RET_DATA = 0x0000ffff
76
77
78class SockFilter(
79 collections.namedtuple('SockFilter', ['code', 'jt', 'jf', 'k'])):
80 """A representation of struct sock_filter."""
81
82 __slots__ = ()
83
84 def encode(self):
85 """Return an encoded version of the SockFilter."""
86 return struct.pack('HBBI', self.code, self.jt, self.jf, self.k)
87
88
89class AbstractBlock(abc.ABC):
90 """A class that implements the visitor pattern."""
91
92 def __init__(self):
93 super().__init__()
94
95 @abc.abstractmethod
96 def accept(self, visitor):
97 pass
98
99
100class BasicBlock(AbstractBlock):
101 """A concrete implementation of AbstractBlock that has been compiled."""
102
103 def __init__(self, instructions):
104 super().__init__()
105 self._instructions = instructions
106
107 def accept(self, visitor):
108 visitor.visit(self)
109
110 @property
111 def instructions(self):
112 return self._instructions
113
114 @property
115 def opcodes(self):
116 return b''.join(i.encode() for i in self._instructions)
117
118 def __eq__(self, o):
119 if not isinstance(o, BasicBlock):
120 return False
121 return self._instructions == o._instructions
122
123
124class KillProcess(BasicBlock):
125 """A BasicBlock that unconditionally returns KILL_PROCESS."""
126
127 def __init__(self):
128 super().__init__(
129 [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_KILL_PROCESS)])
130
131
132class KillThread(BasicBlock):
133 """A BasicBlock that unconditionally returns KILL_THREAD."""
134
135 def __init__(self):
136 super().__init__(
137 [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_KILL_THREAD)])
138
139
140class Trap(BasicBlock):
141 """A BasicBlock that unconditionally returns TRAP."""
142
143 def __init__(self):
144 super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_TRAP)])
145
146
147class Trace(BasicBlock):
148 """A BasicBlock that unconditionally returns TRACE."""
149
150 def __init__(self):
151 super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_TRACE)])
152
153
154class Log(BasicBlock):
155 """A BasicBlock that unconditionally returns LOG."""
156
157 def __init__(self):
158 super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_LOG)])
159
160
161class ReturnErrno(BasicBlock):
162 """A BasicBlock that unconditionally returns the specified errno."""
163
164 def __init__(self, errno):
165 super().__init__([
166 SockFilter(BPF_RET, 0x00, 0x00,
167 SECCOMP_RET_ERRNO | (errno & SECCOMP_RET_DATA))
168 ])
169 self.errno = errno
170
171
172class Allow(BasicBlock):
173 """A BasicBlock that unconditionally returns ALLOW."""
174
175 def __init__(self):
176 super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_ALLOW)])