aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/bpf
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@plumgrid.com>2014-09-26 03:17:03 -0400
committerDavid S. Miller <davem@davemloft.net>2014-09-26 15:05:15 -0400
commitcbd357008604925355ae7b54a09137dabb81b580 (patch)
tree85e57d37ffdb0e743ce71c7124e07c1c9e5c3317 /kernel/bpf
parent51580e798cb61b0fc63fa3aa6c5c975375aa0550 (diff)
bpf: verifier (add ability to receive verification log)
add optional attributes for BPF_PROG_LOAD syscall: union bpf_attr { struct { ... __u32 log_level; /* verbosity level of eBPF verifier */ __u32 log_size; /* size of user buffer */ __aligned_u64 log_buf; /* user supplied 'char *buffer' */ }; }; when log_level > 0 the verifier will return its verification log in the user supplied buffer 'log_buf' which can be used by program author to analyze why verifier rejected given program. 'Understanding eBPF verifier messages' section of Documentation/networking/filter.txt provides several examples of these messages, like the program: BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_CALL_FUNC(BPF_FUNC_map_lookup_elem), BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0), BPF_EXIT_INSN(), will be rejected with the following multi-line message in log_buf: 0: (7a) *(u64 *)(r10 -8) = 0 1: (bf) r2 = r10 2: (07) r2 += -8 3: (b7) r1 = 0 4: (85) call 1 5: (15) if r0 == 0x0 goto pc+1 R0=map_ptr R10=fp 6: (7a) *(u64 *)(r0 +4) = 0 misaligned access off 4 size 8 The format of the output can change at any time as verifier evolves. Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/bpf')
-rw-r--r--kernel/bpf/syscall.c2
-rw-r--r--kernel/bpf/verifier.c235
2 files changed, 236 insertions, 1 deletions
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 74b3628c5fdb..ba61c8c16032 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -458,7 +458,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd)
458} 458}
459 459
460/* last field in 'union bpf_attr' used by this command */ 460/* last field in 'union bpf_attr' used by this command */
461#define BPF_PROG_LOAD_LAST_FIELD license 461#define BPF_PROG_LOAD_LAST_FIELD log_buf
462 462
463static int bpf_prog_load(union bpf_attr *attr) 463static int bpf_prog_load(union bpf_attr *attr)
464{ 464{
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d6f9c3d6b4d7..871edc1f2e1f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -125,9 +125,244 @@
125 * are set to NOT_INIT to indicate that they are no longer readable. 125 * are set to NOT_INIT to indicate that they are no longer readable.
126 */ 126 */
127 127
128/* single container for all structs
129 * one verifier_env per bpf_check() call
130 */
131struct verifier_env {
132};
133
134/* verbose verifier prints what it's seeing
135 * bpf_check() is called under lock, so no race to access these global vars
136 */
137static u32 log_level, log_size, log_len;
138static char *log_buf;
139
140static DEFINE_MUTEX(bpf_verifier_lock);
141
142/* log_level controls verbosity level of eBPF verifier.
143 * verbose() is used to dump the verification trace to the log, so the user
144 * can figure out what's wrong with the program
145 */
146static void verbose(const char *fmt, ...)
147{
148 va_list args;
149
150 if (log_level == 0 || log_len >= log_size - 1)
151 return;
152
153 va_start(args, fmt);
154 log_len += vscnprintf(log_buf + log_len, log_size - log_len, fmt, args);
155 va_end(args);
156}
157
158static const char *const bpf_class_string[] = {
159 [BPF_LD] = "ld",
160 [BPF_LDX] = "ldx",
161 [BPF_ST] = "st",
162 [BPF_STX] = "stx",
163 [BPF_ALU] = "alu",
164 [BPF_JMP] = "jmp",
165 [BPF_RET] = "BUG",
166 [BPF_ALU64] = "alu64",
167};
168
169static const char *const bpf_alu_string[] = {
170 [BPF_ADD >> 4] = "+=",
171 [BPF_SUB >> 4] = "-=",
172 [BPF_MUL >> 4] = "*=",
173 [BPF_DIV >> 4] = "/=",
174 [BPF_OR >> 4] = "|=",
175 [BPF_AND >> 4] = "&=",
176 [BPF_LSH >> 4] = "<<=",
177 [BPF_RSH >> 4] = ">>=",
178 [BPF_NEG >> 4] = "neg",
179 [BPF_MOD >> 4] = "%=",
180 [BPF_XOR >> 4] = "^=",
181 [BPF_MOV >> 4] = "=",
182 [BPF_ARSH >> 4] = "s>>=",
183 [BPF_END >> 4] = "endian",
184};
185
186static const char *const bpf_ldst_string[] = {
187 [BPF_W >> 3] = "u32",
188 [BPF_H >> 3] = "u16",
189 [BPF_B >> 3] = "u8",
190 [BPF_DW >> 3] = "u64",
191};
192
193static const char *const bpf_jmp_string[] = {
194 [BPF_JA >> 4] = "jmp",
195 [BPF_JEQ >> 4] = "==",
196 [BPF_JGT >> 4] = ">",
197 [BPF_JGE >> 4] = ">=",
198 [BPF_JSET >> 4] = "&",
199 [BPF_JNE >> 4] = "!=",
200 [BPF_JSGT >> 4] = "s>",
201 [BPF_JSGE >> 4] = "s>=",
202 [BPF_CALL >> 4] = "call",
203 [BPF_EXIT >> 4] = "exit",
204};
205
206static void print_bpf_insn(struct bpf_insn *insn)
207{
208 u8 class = BPF_CLASS(insn->code);
209
210 if (class == BPF_ALU || class == BPF_ALU64) {
211 if (BPF_SRC(insn->code) == BPF_X)
212 verbose("(%02x) %sr%d %s %sr%d\n",
213 insn->code, class == BPF_ALU ? "(u32) " : "",
214 insn->dst_reg,
215 bpf_alu_string[BPF_OP(insn->code) >> 4],
216 class == BPF_ALU ? "(u32) " : "",
217 insn->src_reg);
218 else
219 verbose("(%02x) %sr%d %s %s%d\n",
220 insn->code, class == BPF_ALU ? "(u32) " : "",
221 insn->dst_reg,
222 bpf_alu_string[BPF_OP(insn->code) >> 4],
223 class == BPF_ALU ? "(u32) " : "",
224 insn->imm);
225 } else if (class == BPF_STX) {
226 if (BPF_MODE(insn->code) == BPF_MEM)
227 verbose("(%02x) *(%s *)(r%d %+d) = r%d\n",
228 insn->code,
229 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
230 insn->dst_reg,
231 insn->off, insn->src_reg);
232 else if (BPF_MODE(insn->code) == BPF_XADD)
233 verbose("(%02x) lock *(%s *)(r%d %+d) += r%d\n",
234 insn->code,
235 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
236 insn->dst_reg, insn->off,
237 insn->src_reg);
238 else
239 verbose("BUG_%02x\n", insn->code);
240 } else if (class == BPF_ST) {
241 if (BPF_MODE(insn->code) != BPF_MEM) {
242 verbose("BUG_st_%02x\n", insn->code);
243 return;
244 }
245 verbose("(%02x) *(%s *)(r%d %+d) = %d\n",
246 insn->code,
247 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
248 insn->dst_reg,
249 insn->off, insn->imm);
250 } else if (class == BPF_LDX) {
251 if (BPF_MODE(insn->code) != BPF_MEM) {
252 verbose("BUG_ldx_%02x\n", insn->code);
253 return;
254 }
255 verbose("(%02x) r%d = *(%s *)(r%d %+d)\n",
256 insn->code, insn->dst_reg,
257 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
258 insn->src_reg, insn->off);
259 } else if (class == BPF_LD) {
260 if (BPF_MODE(insn->code) == BPF_ABS) {
261 verbose("(%02x) r0 = *(%s *)skb[%d]\n",
262 insn->code,
263 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
264 insn->imm);
265 } else if (BPF_MODE(insn->code) == BPF_IND) {
266 verbose("(%02x) r0 = *(%s *)skb[r%d + %d]\n",
267 insn->code,
268 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
269 insn->src_reg, insn->imm);
270 } else if (BPF_MODE(insn->code) == BPF_IMM) {
271 verbose("(%02x) r%d = 0x%x\n",
272 insn->code, insn->dst_reg, insn->imm);
273 } else {
274 verbose("BUG_ld_%02x\n", insn->code);
275 return;
276 }
277 } else if (class == BPF_JMP) {
278 u8 opcode = BPF_OP(insn->code);
279
280 if (opcode == BPF_CALL) {
281 verbose("(%02x) call %d\n", insn->code, insn->imm);
282 } else if (insn->code == (BPF_JMP | BPF_JA)) {
283 verbose("(%02x) goto pc%+d\n",
284 insn->code, insn->off);
285 } else if (insn->code == (BPF_JMP | BPF_EXIT)) {
286 verbose("(%02x) exit\n", insn->code);
287 } else if (BPF_SRC(insn->code) == BPF_X) {
288 verbose("(%02x) if r%d %s r%d goto pc%+d\n",
289 insn->code, insn->dst_reg,
290 bpf_jmp_string[BPF_OP(insn->code) >> 4],
291 insn->src_reg, insn->off);
292 } else {
293 verbose("(%02x) if r%d %s 0x%x goto pc%+d\n",
294 insn->code, insn->dst_reg,
295 bpf_jmp_string[BPF_OP(insn->code) >> 4],
296 insn->imm, insn->off);
297 }
298 } else {
299 verbose("(%02x) %s\n", insn->code, bpf_class_string[class]);
300 }
301}
302
128int bpf_check(struct bpf_prog *prog, union bpf_attr *attr) 303int bpf_check(struct bpf_prog *prog, union bpf_attr *attr)
129{ 304{
305 char __user *log_ubuf = NULL;
306 struct verifier_env *env;
130 int ret = -EINVAL; 307 int ret = -EINVAL;
131 308
309 if (prog->len <= 0 || prog->len > BPF_MAXINSNS)
310 return -E2BIG;
311
312 /* 'struct verifier_env' can be global, but since it's not small,
313 * allocate/free it every time bpf_check() is called
314 */
315 env = kzalloc(sizeof(struct verifier_env), GFP_KERNEL);
316 if (!env)
317 return -ENOMEM;
318
319 /* grab the mutex to protect few globals used by verifier */
320 mutex_lock(&bpf_verifier_lock);
321
322 if (attr->log_level || attr->log_buf || attr->log_size) {
323 /* user requested verbose verifier output
324 * and supplied buffer to store the verification trace
325 */
326 log_level = attr->log_level;
327 log_ubuf = (char __user *) (unsigned long) attr->log_buf;
328 log_size = attr->log_size;
329 log_len = 0;
330
331 ret = -EINVAL;
332 /* log_* values have to be sane */
333 if (log_size < 128 || log_size > UINT_MAX >> 8 ||
334 log_level == 0 || log_ubuf == NULL)
335 goto free_env;
336
337 ret = -ENOMEM;
338 log_buf = vmalloc(log_size);
339 if (!log_buf)
340 goto free_env;
341 } else {
342 log_level = 0;
343 }
344
345 /* ret = do_check(env); */
346
347 if (log_level && log_len >= log_size - 1) {
348 BUG_ON(log_len >= log_size);
349 /* verifier log exceeded user supplied buffer */
350 ret = -ENOSPC;
351 /* fall through to return what was recorded */
352 }
353
354 /* copy verifier log back to user space including trailing zero */
355 if (log_level && copy_to_user(log_ubuf, log_buf, log_len + 1) != 0) {
356 ret = -EFAULT;
357 goto free_log_buf;
358 }
359
360
361free_log_buf:
362 if (log_level)
363 vfree(log_buf);
364free_env:
365 kfree(env);
366 mutex_unlock(&bpf_verifier_lock);
132 return ret; 367 return ret;
133} 368}