Skip to content

Commit cbd3570

Browse files
Alexei Starovoitovdavem330
authored andcommitted
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>
1 parent 51580e7 commit cbd3570

File tree

3 files changed

+239
-1
lines changed

3 files changed

+239
-1
lines changed

include/uapi/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ union bpf_attr {
138138
__u32 insn_cnt;
139139
__aligned_u64 insns;
140140
__aligned_u64 license;
141+
__u32 log_level; /* verbosity level of verifier */
142+
__u32 log_size; /* size of user buffer */
143+
__aligned_u64 log_buf; /* user supplied buffer */
141144
};
142145
} __attribute__((aligned(8)));
143146

kernel/bpf/syscall.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd)
458458
}
459459

460460
/* 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
462462

463463
static int bpf_prog_load(union bpf_attr *attr)
464464
{

kernel/bpf/verifier.c

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,244 @@
125125
* are set to NOT_INIT to indicate that they are no longer readable.
126126
*/
127127

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+
128303
int bpf_check(struct bpf_prog *prog, union bpf_attr *attr)
129304
{
305+
char __user *log_ubuf = NULL;
306+
struct verifier_env *env;
130307
int ret = -EINVAL;
131308

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);
132367
return ret;
133368
}

0 commit comments

Comments
 (0)