Skip to content

Commit ab371e3

Browse files
pchaignoKernel Patches Daemon
authored andcommitted
bpf: Check oracle in interpreter
If we run into our special BPF_PSEUDO_MAP_ORACLE instruction in the interpreter, we need to run the oracle test to check for inconsistencies between concrete values and verifier states. This patch implements that check and throws a kernel warning if any inconsistency is found. The kernel warning message looks as follows, if only R6 was found not to match some states: oracle caught invalid states in oracle_map[id:21]: r6=0xffffffffffffffd4 Signed-off-by: Paul Chaignon <[email protected]>
1 parent 3751c51 commit ab371e3

File tree

5 files changed

+84
-2
lines changed

5 files changed

+84
-2
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,5 +1130,6 @@ int save_state_in_oracle(struct bpf_verifier_env *env, int insn_idx);
11301130
struct bpf_prog *patch_oracle_check_insn(struct bpf_verifier_env *env, struct bpf_insn *insn,
11311131
int i, int *cnt);
11321132
int create_and_populate_oracle_map(struct bpf_verifier_env *env);
1133+
void oracle_test(struct bpf_map *oracle_states, u64 *regs);
11331134

11341135
#endif /* _LINUX_BPF_VERIFIER_H */

include/linux/tnum.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ struct tnum tnum_mul(struct tnum a, struct tnum b);
5454
/* Return true if the known bits of both tnums have the same value */
5555
bool tnum_overlap(struct tnum a, struct tnum b);
5656

57+
/* Return true if tnum a matches value b. */
58+
bool tnum_match(struct tnum a, u64 b);
59+
5760
/* Return a tnum representing numbers satisfying both @a and @b */
5861
struct tnum tnum_intersect(struct tnum a, struct tnum b);
5962

kernel/bpf/core.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,10 +1848,18 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
18481848
ALU64_MOV_K:
18491849
DST = IMM;
18501850
CONT;
1851-
LD_IMM_DW:
1852-
DST = (u64) (u32) insn[0].imm | ((u64) (u32) insn[1].imm) << 32;
1851+
LD_IMM_DW: {
1852+
u64 address = (u64)(u32)insn[0].imm | ((u64)(u32)insn[1].imm) << 32;
1853+
1854+
if (insn[0].src_reg == BPF_PSEUDO_MAP_ORACLE) {
1855+
oracle_test((struct bpf_map *)address, regs);
1856+
insn++;
1857+
CONT;
1858+
}
1859+
DST = address;
18531860
insn++;
18541861
CONT;
1862+
}
18551863
ALU_ARSH_X:
18561864
DST = (u64) (u32) (((s32) DST) >> (SRC & 31));
18571865
CONT;

kernel/bpf/oracle.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <linux/bpf_verifier.h>
1010

11+
#define REGS_FMT_BUF_LEN 221
12+
1113
static void convert_oracle_state(struct bpf_verifier_state *istate, struct bpf_oracle_state *ostate)
1214
{
1315
struct bpf_func_state *frame = istate->frame[istate->curframe];
@@ -320,3 +322,66 @@ int create_and_populate_oracle_map(struct bpf_verifier_env *env)
320322

321323
return populate_oracle_map(env, oracle_map);
322324
}
325+
326+
static bool oracle_test_reg(struct bpf_reg_oracle_state *exp, u64 reg)
327+
{
328+
if (exp->scalar) {
329+
if (reg < exp->umin_value || reg > exp->umax_value ||
330+
(s64)reg < exp->smin_value || (s64)reg > exp->smax_value ||
331+
(u32)reg < exp->u32_min_value || (u32)reg > exp->u32_max_value ||
332+
(s32)reg < exp->s32_min_value || (s32)reg > exp->s32_max_value ||
333+
!tnum_match(exp->var_off, reg))
334+
return true;
335+
} else if (exp->ptr_not_null && !reg) {
336+
return true;
337+
}
338+
return false;
339+
}
340+
341+
static bool oracle_test_state(struct bpf_oracle_state *state, u64 *regs, u32 *non_match_regs)
342+
{
343+
int i;
344+
345+
for (i = 0; i < MAX_BPF_REG - 1; i++) {
346+
if (oracle_test_reg(&state->regs[i], regs[i])) {
347+
*non_match_regs |= 1 << i;
348+
return true;
349+
}
350+
}
351+
352+
return false;
353+
}
354+
355+
static void format_non_match_regs(u32 non_match_regs, u64 *regs, char *buf)
356+
{
357+
int i, delta = 0;
358+
359+
for (i = 0; i < MAX_BPF_REG - 1; i++) {
360+
if (non_match_regs & (1 << i)) {
361+
delta += snprintf(buf + delta, REGS_FMT_BUF_LEN - delta, "r%d=%#llx ",
362+
i, regs[i]);
363+
}
364+
}
365+
}
366+
367+
void oracle_test(struct bpf_map *oracle_states, u64 *regs)
368+
{
369+
struct bpf_oracle_state *state;
370+
u32 non_match_regs = 0;
371+
char regs_fmt[REGS_FMT_BUF_LEN];
372+
bool expected = false;
373+
int i;
374+
375+
for (i = 0; i < oracle_states->max_entries; i++) {
376+
state = oracle_states->ops->map_lookup_elem(oracle_states, &i);
377+
if (!oracle_test_state(state, regs, &non_match_regs)) {
378+
expected = true;
379+
break;
380+
}
381+
}
382+
if (!expected) {
383+
format_non_match_regs(non_match_regs, regs, regs_fmt);
384+
BPF_WARN_ONCE(1, "oracle caught invalid states in oracle_map[id:%d]: %s\n",
385+
oracle_states->id, regs_fmt);
386+
}
387+
}

kernel/bpf/tnum.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ bool tnum_overlap(struct tnum a, struct tnum b)
167167
return (a.value & mu) == (b.value & mu);
168168
}
169169

170+
bool tnum_match(struct tnum a, u64 b)
171+
{
172+
return (a.value & ~a.mask) == (b & ~a.mask);
173+
}
174+
170175
/* Note that if a and b disagree - i.e. one has a 'known 1' where the other has
171176
* a 'known 0' - this will return a 'known 1' for that bit.
172177
*/

0 commit comments

Comments
 (0)