blob: ce754a716a8cc1113d54ad7cc5b67bc5086c097b [file] [log] [blame]
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -07001/* Copyright (c) 2012 The Chromium OS 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
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "bpf.h"
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -040012#include "util.h"
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070013
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -070014/* Architecture validation. */
15size_t bpf_validate_arch(struct sock_filter *filter)
16{
17 struct sock_filter *curr_block = filter;
18 set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, arch_nr);
19 set_bpf_jump(curr_block++,
20 BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, SKIP, NEXT);
21 set_bpf_ret_kill(curr_block++);
22 return curr_block - filter;
23}
24
25/* Syscall number eval functions. */
26size_t bpf_allow_syscall(struct sock_filter *filter, int nr)
27{
28 struct sock_filter *curr_block = filter;
29 set_bpf_jump(curr_block++, BPF_JMP+BPF_JEQ+BPF_K, nr, NEXT, SKIP);
30 set_bpf_stmt(curr_block++, BPF_RET+BPF_K, SECCOMP_RET_ALLOW);
31 return curr_block - filter;
32}
33
34size_t bpf_allow_syscall_args(struct sock_filter *filter,
35 int nr, unsigned int id)
36{
37 struct sock_filter *curr_block = filter;
38 set_bpf_jump(curr_block++, BPF_JMP+BPF_JEQ+BPF_K, nr, NEXT, SKIP);
39 set_bpf_jump_lbl(curr_block++, id);
40 return curr_block - filter;
41}
42
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070043/* Size-aware arg loaders. */
44#if defined(BITS32)
45size_t bpf_load_arg(struct sock_filter *filter, int argidx)
46{
47 set_bpf_stmt(filter, BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
48 return 1U;
49}
50#elif defined(BITS64)
51size_t bpf_load_arg(struct sock_filter *filter, int argidx)
52{
53 struct sock_filter *curr_block = filter;
54 set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
55 set_bpf_stmt(curr_block++, BPF_ST, 0); /* lo -> M[0] */
56 set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, HI_ARG(argidx));
57 set_bpf_stmt(curr_block++, BPF_ST, 1); /* hi -> M[1] */
58 return curr_block - filter;
59}
60#endif
61
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -080062/* Size-aware equality comparison. */
Jorge Lucangeli Obesedb1d8e2012-04-26 10:05:09 -070063size_t bpf_comp_jeq32(struct sock_filter *filter, unsigned long c,
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070064 unsigned char jt, unsigned char jf)
65{
Jorge Lucangeli Obesedb1d8e2012-04-26 10:05:09 -070066 unsigned int lo = (unsigned int)(c & 0xFFFFFFFF);
67 set_bpf_jump(filter, BPF_JMP+BPF_JEQ+BPF_K, lo, jt, jf);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070068 return 1U;
69}
70
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -080071/*
72 * On 64 bits, we have to do two 32-bit comparisons.
73 * We jump true when *both* comparisons are true.
74 */
Jorge Lucangeli Obes8a56ec22013-02-04 10:03:43 -080075#if defined(BITS64)
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070076size_t bpf_comp_jeq64(struct sock_filter *filter, uint64_t c,
77 unsigned char jt, unsigned char jf)
78{
79 unsigned int lo = (unsigned int)(c & 0xFFFFFFFF);
80 unsigned int hi = (unsigned int)(c >> 32);
81
82 struct sock_filter *curr_block = filter;
83
84 /* bpf_load_arg leaves |hi| in A */
85 curr_block += bpf_comp_jeq32(curr_block, hi, NEXT, SKIPN(2) + jf);
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -080086 set_bpf_stmt(curr_block++, BPF_LD+BPF_MEM, 0); /* swap in |lo| */
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070087 curr_block += bpf_comp_jeq32(curr_block, lo, jt, jf);
88
89 return curr_block - filter;
90}
Jorge Lucangeli Obes8a56ec22013-02-04 10:03:43 -080091#endif
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070092
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -080093/* Size-aware bitwise AND. */
94size_t bpf_comp_jset32(struct sock_filter *filter, unsigned long mask,
95 unsigned char jt, unsigned char jf)
96{
97 unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF);
98 set_bpf_jump(filter, BPF_JMP+BPF_JSET+BPF_K, mask_lo, jt, jf);
99 return 1U;
100}
101
102/*
103 * On 64 bits, we have to do two 32-bit bitwise ANDs.
104 * We jump true when *either* bitwise AND is true (non-zero).
105 */
Jorge Lucangeli Obes8a56ec22013-02-04 10:03:43 -0800106#if defined(BITS64)
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800107size_t bpf_comp_jset64(struct sock_filter *filter, uint64_t mask,
108 unsigned char jt, unsigned char jf)
109{
110 unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF);
111 unsigned int mask_hi = (unsigned int)(mask >> 32);
112
113 struct sock_filter *curr_block = filter;
114
115 /* bpf_load_arg leaves |hi| in A */
116 curr_block += bpf_comp_jset32(curr_block, mask_hi, SKIPN(2) + jt, NEXT);
117 set_bpf_stmt(curr_block++, BPF_LD+BPF_MEM, 0); /* swap in |lo| */
118 curr_block += bpf_comp_jset32(curr_block, mask_lo, jt, jf);
119
120 return curr_block - filter;
121}
Jorge Lucangeli Obes8a56ec22013-02-04 10:03:43 -0800122#endif
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700123
124size_t bpf_arg_comp(struct sock_filter **pfilter,
125 int op, int argidx, unsigned long c, unsigned int label_id)
126{
127 struct sock_filter *filter = calloc(BPF_ARG_COMP_LEN + 1,
128 sizeof(struct sock_filter));
129 struct sock_filter *curr_block = filter;
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800130 size_t (*comp_function)(struct sock_filter *filter, unsigned long k,
131 unsigned char jt, unsigned char jf);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700132 int flip = 0;
133
134 /* Load arg */
135 curr_block += bpf_load_arg(curr_block, argidx);
136
137 /* Jump type */
138 switch (op) {
139 case EQ:
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800140 comp_function = bpf_comp_jeq;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700141 flip = 0;
142 break;
143 case NE:
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800144 comp_function = bpf_comp_jeq;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700145 flip = 1;
146 break;
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800147 case SET:
148 comp_function = bpf_comp_jset;
149 flip = 0;
150 break;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700151 default:
152 *pfilter = NULL;
153 return 0;
154 }
155
156 /*
157 * It's easier for the rest of the code to have the true branch
158 * skip and the false branch fall through.
159 */
160 unsigned char jt = flip ? NEXT : SKIP;
161 unsigned char jf = flip ? SKIP : NEXT;
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800162 curr_block += comp_function(curr_block, c, jt, jf);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700163 curr_block += set_bpf_jump_lbl(curr_block, label_id);
164
165 *pfilter = filter;
166 return curr_block - filter;
167}
168
169void dump_bpf_filter(struct sock_filter *filter, unsigned short len)
170{
171 int i = 0;
172
173 printf("len == %d\n", len);
174 printf("filter:\n");
175 for (i = 0; i < len; i++) {
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400176 printf("%d: \t{ code=%#x, jt=%u, jf=%u, k=%#x \t}\n", i,
177 filter[i].code, filter[i].jt, filter[i].jf, filter[i].k);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700178 }
179}
180
181void dump_bpf_prog(struct sock_fprog *fprog)
182{
183 struct sock_filter *filter = fprog->filter;
184 unsigned short len = fprog->len;
185 dump_bpf_filter(filter, len);
186}
187
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400188int bpf_resolve_jumps(struct bpf_labels *labels, struct sock_filter *filter,
189 size_t len)
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700190{
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400191 struct sock_filter *instr;
192 size_t i, offset;
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400193
194 if (len > BPF_MAXINSNS)
195 return -1;
196
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700197 /*
198 * Walk it once, backwards, to build the label table and do fixups.
199 * Since backward jumps are disallowed by BPF, this is easy.
200 */
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400201 for (i = 0; i < len; i++) {
202 offset = len - i - 1;
203 instr = &filter[offset];
204 if (instr->code != (BPF_JMP + BPF_JA))
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700205 continue;
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400206 switch ((instr->jt << 8) | instr->jf) {
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400207 case (JUMP_JT << 8) | JUMP_JF:
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400208 if (instr->k >= labels->count) {
209 warn("nonexistent label id: %u", instr->k);
210 return -1;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700211 }
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400212 if (labels->labels[instr->k].location == 0xffffffff) {
213 warn("unresolved label: '%s'",
214 labels->labels[instr->k].label);
215 return -1;
216 }
217 instr->k =
218 labels->labels[instr->k].location - (offset + 1);
219 instr->jt = 0;
220 instr->jf = 0;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700221 continue;
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400222 case (LABEL_JT << 8) | LABEL_JF:
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400223 if (labels->labels[instr->k].location != 0xffffffff) {
224 warn("duplicate label: '%s'",
225 labels->labels[instr->k].label);
226 return -1;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700227 }
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400228 labels->labels[instr->k].location = offset;
229 instr->k = 0; /* Fall through. */
230 instr->jt = 0;
231 instr->jf = 0;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700232 continue;
233 }
234 }
235 return 0;
236}
237
238/* Simple lookup table for labels. */
239int bpf_label_id(struct bpf_labels *labels, const char *label)
240{
241 struct __bpf_label *begin = labels->labels, *end;
242 int id;
243 if (labels->count == 0) {
244 begin->label = strndup(label, MAX_BPF_LABEL_LEN);
245 if (!begin->label) {
246 return -1;
247 }
248 begin->location = 0xffffffff;
249 labels->count++;
250 return 0;
251 }
252 end = begin + labels->count;
253 for (id = 0; begin < end; ++begin, ++id) {
254 if (!strcmp(label, begin->label))
255 return id;
256 }
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400257
258 /* The label wasn't found. Insert it only if there's space. */
259 if (labels->count == BPF_LABELS_MAX) {
260 return -1;
261 }
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700262 begin->label = strndup(label, MAX_BPF_LABEL_LEN);
263 if (!begin->label) {
264 return -1;
265 }
266 begin->location = 0xffffffff;
267 labels->count++;
268 return id;
269}
270
271/* Free label strings. */
272void free_label_strings(struct bpf_labels *labels)
273{
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -0700274 if (labels->count == 0)
275 return;
276
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700277 struct __bpf_label *begin = labels->labels, *end;
278
279 end = begin + labels->count;
280 for (; begin < end; ++begin) {
281 if (begin->label)
282 free((void*)(begin->label));
283 }
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400284
285 labels->count = 0;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700286}