blob: 8f5a08d814813076649a90acf9675375fa115c04 [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;
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040018 set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, arch_nr);
19 set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, SKIP,
20 NEXT);
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -070021 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;
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040029 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);
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -070031 return curr_block - filter;
32}
33
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040034size_t bpf_allow_syscall_args(struct sock_filter *filter, int nr,
35 unsigned int id)
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -070036{
37 struct sock_filter *curr_block = filter;
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040038 set_bpf_jump(curr_block++, BPF_JMP + BPF_JEQ + BPF_K, nr, NEXT, SKIP);
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -070039 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{
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040047 set_bpf_stmt(filter, BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070048 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;
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040054 set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070055 set_bpf_stmt(curr_block++, BPF_ST, 0); /* lo -> M[0] */
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040056 set_bpf_stmt(curr_block++, BPF_LD + BPF_W + BPF_ABS, HI_ARG(argidx));
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070057 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 Obesfd6f8e32016-10-12 11:19:28 -040064 unsigned char jt, unsigned char jf)
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070065{
Jorge Lucangeli Obesedb1d8e2012-04-26 10:05:09 -070066 unsigned int lo = (unsigned int)(c & 0xFFFFFFFF);
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040067 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 Obesfd6f8e32016-10-12 11:19:28 -040076size_t bpf_comp_jeq64(struct sock_filter *filter, uint64_t c, unsigned char jt,
77 unsigned char jf)
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -070078{
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 Obesfd6f8e32016-10-12 11:19:28 -040086 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,
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040095 unsigned char jt, unsigned char jf)
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -080096{
97 unsigned int mask_lo = (unsigned int)(mask & 0xFFFFFFFF);
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -040098 set_bpf_jump(filter, BPF_JMP + BPF_JSET + BPF_K, mask_lo, jt, jf);
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -080099 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,
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400108 unsigned char jt, unsigned char jf)
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800109{
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);
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400117 set_bpf_stmt(curr_block++, BPF_LD + BPF_MEM, 0); /* swap in |lo| */
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800118 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
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400124size_t bpf_comp_jin(struct sock_filter *filter, unsigned long mask,
125 unsigned char jt, unsigned char jf)
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700126{
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400127 unsigned long negative_mask = ~mask;
128 /*
129 * The mask is negated, so the comparison will be true when the argument
130 * includes a flag that wasn't listed in the original (non-negated)
131 * mask. This would be the failure case, so we switch |jt| and |jf|.
132 */
133 return bpf_comp_jset(filter, negative_mask, jf, jt);
134}
135
136size_t bpf_arg_comp(struct sock_filter **pfilter, int op, int argidx,
137 unsigned long c, unsigned int label_id)
138{
139 struct sock_filter *filter =
140 calloc(BPF_ARG_COMP_LEN + 1, sizeof(struct sock_filter));
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700141 struct sock_filter *curr_block = filter;
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400142 size_t (*comp_function)(struct sock_filter * filter, unsigned long k,
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800143 unsigned char jt, unsigned char jf);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700144 int flip = 0;
145
146 /* Load arg */
147 curr_block += bpf_load_arg(curr_block, argidx);
148
149 /* Jump type */
150 switch (op) {
151 case EQ:
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800152 comp_function = bpf_comp_jeq;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700153 flip = 0;
154 break;
155 case NE:
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800156 comp_function = bpf_comp_jeq;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700157 flip = 1;
158 break;
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800159 case SET:
160 comp_function = bpf_comp_jset;
161 flip = 0;
162 break;
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400163 case IN:
164 comp_function = bpf_comp_jin;
165 flip = 0;
166 break;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700167 default:
168 *pfilter = NULL;
169 return 0;
170 }
171
172 /*
173 * It's easier for the rest of the code to have the true branch
174 * skip and the false branch fall through.
175 */
176 unsigned char jt = flip ? NEXT : SKIP;
177 unsigned char jf = flip ? SKIP : NEXT;
Jorge Lucangeli Obesffec8912012-11-30 14:46:23 -0800178 curr_block += comp_function(curr_block, c, jt, jf);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700179 curr_block += set_bpf_jump_lbl(curr_block, label_id);
180
181 *pfilter = filter;
182 return curr_block - filter;
183}
184
185void dump_bpf_filter(struct sock_filter *filter, unsigned short len)
186{
187 int i = 0;
188
189 printf("len == %d\n", len);
190 printf("filter:\n");
191 for (i = 0; i < len; i++) {
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400192 printf("%d: \t{ code=%#x, jt=%u, jf=%u, k=%#x \t}\n", i,
193 filter[i].code, filter[i].jt, filter[i].jf, filter[i].k);
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700194 }
195}
196
197void dump_bpf_prog(struct sock_fprog *fprog)
198{
199 struct sock_filter *filter = fprog->filter;
200 unsigned short len = fprog->len;
201 dump_bpf_filter(filter, len);
202}
203
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400204int bpf_resolve_jumps(struct bpf_labels *labels, struct sock_filter *filter,
205 size_t len)
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700206{
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400207 struct sock_filter *instr;
208 size_t i, offset;
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400209
210 if (len > BPF_MAXINSNS)
211 return -1;
212
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700213 /*
214 * Walk it once, backwards, to build the label table and do fixups.
215 * Since backward jumps are disallowed by BPF, this is easy.
216 */
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400217 for (i = 0; i < len; i++) {
218 offset = len - i - 1;
219 instr = &filter[offset];
220 if (instr->code != (BPF_JMP + BPF_JA))
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700221 continue;
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400222 switch ((instr->jt << 8) | instr->jf) {
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400223 case (JUMP_JT << 8) | JUMP_JF:
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400224 if (instr->k >= labels->count) {
225 warn("nonexistent label id: %u", instr->k);
226 return -1;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700227 }
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400228 if (labels->labels[instr->k].location == 0xffffffff) {
229 warn("unresolved label: '%s'",
230 labels->labels[instr->k].label);
231 return -1;
232 }
233 instr->k =
234 labels->labels[instr->k].location - (offset + 1);
235 instr->jt = 0;
236 instr->jf = 0;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700237 continue;
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400238 case (LABEL_JT << 8) | LABEL_JF:
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400239 if (labels->labels[instr->k].location != 0xffffffff) {
240 warn("duplicate label: '%s'",
241 labels->labels[instr->k].label);
242 return -1;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700243 }
Jorge Lucangeli Obes8cc9d4a2016-10-03 10:00:57 -0400244 labels->labels[instr->k].location = offset;
245 instr->k = 0; /* Fall through. */
246 instr->jt = 0;
247 instr->jf = 0;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700248 continue;
249 }
250 }
251 return 0;
252}
253
254/* Simple lookup table for labels. */
255int bpf_label_id(struct bpf_labels *labels, const char *label)
256{
257 struct __bpf_label *begin = labels->labels, *end;
258 int id;
259 if (labels->count == 0) {
260 begin->label = strndup(label, MAX_BPF_LABEL_LEN);
261 if (!begin->label) {
262 return -1;
263 }
264 begin->location = 0xffffffff;
265 labels->count++;
266 return 0;
267 }
268 end = begin + labels->count;
269 for (id = 0; begin < end; ++begin, ++id) {
Jorge Lucangeli Obes45932a52017-03-15 17:02:58 -0400270 if (!strcmp(label, begin->label)) {
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700271 return id;
Jorge Lucangeli Obes45932a52017-03-15 17:02:58 -0400272 }
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700273 }
Jorge Lucangeli Obesf16d6d12016-09-29 20:25:27 -0400274
275 /* The label wasn't found. Insert it only if there's space. */
276 if (labels->count == BPF_LABELS_MAX) {
277 return -1;
278 }
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700279 begin->label = strndup(label, MAX_BPF_LABEL_LEN);
280 if (!begin->label) {
281 return -1;
282 }
283 begin->location = 0xffffffff;
284 labels->count++;
285 return id;
286}
287
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700288void free_label_strings(struct bpf_labels *labels)
289{
Jorge Lucangeli Obesd4467262012-03-23 16:19:59 -0700290 if (labels->count == 0)
291 return;
292
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700293 struct __bpf_label *begin = labels->labels, *end;
294
295 end = begin + labels->count;
296 for (; begin < end; ++begin) {
297 if (begin->label)
Jorge Lucangeli Obesfd6f8e32016-10-12 11:19:28 -0400298 free((void *)(begin->label));
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700299 }
Jorge Lucangeli Obesa67bd6a2016-08-19 15:33:48 -0400300
301 labels->count = 0;
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -0700302}