diff options
author | Alexei Starovoitov <ast@plumgrid.com> | 2014-09-26 03:17:03 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-26 15:05:15 -0400 |
commit | cbd357008604925355ae7b54a09137dabb81b580 (patch) | |
tree | 85e57d37ffdb0e743ce71c7124e07c1c9e5c3317 /kernel/bpf | |
parent | 51580e798cb61b0fc63fa3aa6c5c975375aa0550 (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.c | 2 | ||||
-rw-r--r-- | kernel/bpf/verifier.c | 235 |
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 | ||
463 | static int bpf_prog_load(union bpf_attr *attr) | 463 | static 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 | */ | ||
131 | struct 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 | */ | ||
137 | static u32 log_level, log_size, log_len; | ||
138 | static char *log_buf; | ||
139 | |||
140 | static 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 | */ | ||
146 | static 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 | |||
158 | static 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 | |||
169 | static 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 | |||
186 | static 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 | |||
193 | static 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 | |||
206 | static 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 | |||
128 | int bpf_check(struct bpf_prog *prog, union bpf_attr *attr) | 303 | int 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 | |||
361 | free_log_buf: | ||
362 | if (log_level) | ||
363 | vfree(log_buf); | ||
364 | free_env: | ||
365 | kfree(env); | ||
366 | mutex_unlock(&bpf_verifier_lock); | ||
132 | return ret; | 367 | return ret; |
133 | } | 368 | } |