Skip to content

Commit 9067970

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-verifier-retval-logic-fixes'
Andrii Nakryiko says: ==================== BPF verifier retval logic fixes This patch set fixes BPF verifier logic around validating and enforcing return values for BPF programs that have specific range of expected return values. Both sync and async callbacks have similar logic and are fixes as well. A few tests are added that would fail without the fixes in this patch set. Also, while at it, we update retval checking logic to use smin/smax range instead of tnum, avoiding future potential issues if expected range cannot be represented precisely by tnum (e.g., [0, 2] is not representable by tnum and is treated as [0, 3]). There is a little bit of refactoring to unify async callback and program exit logic to avoid duplication of checks as much as possible. v4->v5: - fix timer_bad_ret test on no-alu32 flavor (CI); v3->v4: - add back bpf_func_state rearrangement patch; - simplified patch #4 as suggested (Shung-Hsi); v2->v3: - more carefullly switch from umin/umax to smin/smax; v1->v2: - drop tnum from retval checks (Eduard); - use smin/smax instead of umin/umax (Alexei). ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 6685aad + 81eff2e commit 9067970

15 files changed

+212
-80
lines changed

include/linux/bpf_verifier.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ struct bpf_reference_state {
275275
int callback_ref;
276276
};
277277

278+
struct bpf_retval_range {
279+
s32 minval;
280+
s32 maxval;
281+
};
282+
278283
/* state of the program:
279284
* type of all registers and stack info
280285
*/
@@ -297,8 +302,8 @@ struct bpf_func_state {
297302
* void foo(void) { bpf_timer_set_callback(,foo); }
298303
*/
299304
u32 async_entry_cnt;
305+
struct bpf_retval_range callback_ret_range;
300306
bool in_callback_fn;
301-
struct tnum callback_ret_range;
302307
bool in_async_callback_fn;
303308
bool in_exception_callback_fn;
304309
/* For callback calling functions that limit number of possible
@@ -316,8 +321,8 @@ struct bpf_func_state {
316321
/* The following fields should be last. See copy_func_state() */
317322
int acquired_refs;
318323
struct bpf_reference_state *refs;
319-
int allocated_stack;
320324
struct bpf_stack_state *stack;
325+
int allocated_stack;
321326
};
322327

323328
struct bpf_idx_pair {

kernel/bpf/log.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,19 @@ static void verbose_snum(struct bpf_verifier_env *env, s64 num)
539539
verbose(env, "%#llx", num);
540540
}
541541

542+
int tnum_strn(char *str, size_t size, struct tnum a)
543+
{
544+
/* print as a constant, if tnum is fully known */
545+
if (a.mask == 0) {
546+
if (is_unum_decimal(a.value))
547+
return snprintf(str, size, "%llu", a.value);
548+
else
549+
return snprintf(str, size, "%#llx", a.value);
550+
}
551+
return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
552+
}
553+
EXPORT_SYMBOL_GPL(tnum_strn);
554+
542555
static void print_scalar_ranges(struct bpf_verifier_env *env,
543556
const struct bpf_reg_state *reg,
544557
const char **sep)

kernel/bpf/tnum.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,6 @@ bool tnum_in(struct tnum a, struct tnum b)
172172
return a.value == b.value;
173173
}
174174

175-
int tnum_strn(char *str, size_t size, struct tnum a)
176-
{
177-
return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
178-
}
179-
EXPORT_SYMBOL_GPL(tnum_strn);
180-
181175
int tnum_sbin(char *str, size_t size, struct tnum a)
182176
{
183177
size_t n;

kernel/bpf/verifier.c

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -362,20 +362,23 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
362362

363363
static void verbose_invalid_scalar(struct bpf_verifier_env *env,
364364
struct bpf_reg_state *reg,
365-
struct tnum *range, const char *ctx,
365+
struct bpf_retval_range range, const char *ctx,
366366
const char *reg_name)
367367
{
368-
char tn_buf[48];
368+
bool unknown = true;
369369

370-
verbose(env, "At %s the register %s ", ctx, reg_name);
371-
if (!tnum_is_unknown(reg->var_off)) {
372-
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
373-
verbose(env, "has value %s", tn_buf);
374-
} else {
375-
verbose(env, "has unknown scalar value");
370+
verbose(env, "%s the register %s has", ctx, reg_name);
371+
if (reg->smin_value > S64_MIN) {
372+
verbose(env, " smin=%lld", reg->smin_value);
373+
unknown = false;
376374
}
377-
tnum_strn(tn_buf, sizeof(tn_buf), *range);
378-
verbose(env, " should have been in %s\n", tn_buf);
375+
if (reg->smax_value < S64_MAX) {
376+
verbose(env, " smax=%lld", reg->smax_value);
377+
unknown = false;
378+
}
379+
if (unknown)
380+
verbose(env, " unknown scalar value");
381+
verbose(env, " should have been in [%d, %d]\n", range.minval, range.maxval);
379382
}
380383

381384
static bool type_may_be_null(u32 type)
@@ -2305,6 +2308,11 @@ static void init_reg_state(struct bpf_verifier_env *env,
23052308
regs[BPF_REG_FP].frameno = state->frameno;
23062309
}
23072310

2311+
static struct bpf_retval_range retval_range(s32 minval, s32 maxval)
2312+
{
2313+
return (struct bpf_retval_range){ minval, maxval };
2314+
}
2315+
23082316
#define BPF_MAIN_FUNC (-1)
23092317
static void init_func_state(struct bpf_verifier_env *env,
23102318
struct bpf_func_state *state,
@@ -2313,7 +2321,7 @@ static void init_func_state(struct bpf_verifier_env *env,
23132321
state->callsite = callsite;
23142322
state->frameno = frameno;
23152323
state->subprogno = subprogno;
2316-
state->callback_ret_range = tnum_range(0, 0);
2324+
state->callback_ret_range = retval_range(0, 0);
23172325
init_reg_state(env, state);
23182326
mark_verifier_state_scratched(env);
23192327
}
@@ -9396,7 +9404,7 @@ static int set_map_elem_callback_state(struct bpf_verifier_env *env,
93969404
return err;
93979405

93989406
callee->in_callback_fn = true;
9399-
callee->callback_ret_range = tnum_range(0, 1);
9407+
callee->callback_ret_range = retval_range(0, 1);
94009408
return 0;
94019409
}
94029410

@@ -9418,7 +9426,7 @@ static int set_loop_callback_state(struct bpf_verifier_env *env,
94189426
__mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
94199427

94209428
callee->in_callback_fn = true;
9421-
callee->callback_ret_range = tnum_range(0, 1);
9429+
callee->callback_ret_range = retval_range(0, 1);
94229430
return 0;
94239431
}
94249432

@@ -9448,7 +9456,7 @@ static int set_timer_callback_state(struct bpf_verifier_env *env,
94489456
__mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
94499457
__mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
94509458
callee->in_async_callback_fn = true;
9451-
callee->callback_ret_range = tnum_range(0, 1);
9459+
callee->callback_ret_range = retval_range(0, 1);
94529460
return 0;
94539461
}
94549462

@@ -9476,7 +9484,7 @@ static int set_find_vma_callback_state(struct bpf_verifier_env *env,
94769484
__mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
94779485
__mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
94789486
callee->in_callback_fn = true;
9479-
callee->callback_ret_range = tnum_range(0, 1);
9487+
callee->callback_ret_range = retval_range(0, 1);
94809488
return 0;
94819489
}
94829490

@@ -9499,7 +9507,7 @@ static int set_user_ringbuf_callback_state(struct bpf_verifier_env *env,
94999507
__mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
95009508

95019509
callee->in_callback_fn = true;
9502-
callee->callback_ret_range = tnum_range(0, 1);
9510+
callee->callback_ret_range = retval_range(0, 1);
95039511
return 0;
95049512
}
95059513

@@ -9531,7 +9539,7 @@ static int set_rbtree_add_callback_state(struct bpf_verifier_env *env,
95319539
__mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
95329540
__mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
95339541
callee->in_callback_fn = true;
9534-
callee->callback_ret_range = tnum_range(0, 1);
9542+
callee->callback_ret_range = retval_range(0, 1);
95359543
return 0;
95369544
}
95379545

@@ -9560,6 +9568,11 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
95609568
return is_rbtree_lock_required_kfunc(kfunc_btf_id);
95619569
}
95629570

9571+
static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg)
9572+
{
9573+
return range.minval <= reg->smin_value && reg->smax_value <= range.maxval;
9574+
}
9575+
95639576
static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
95649577
{
95659578
struct bpf_verifier_state *state = env->cur_state, *prev_st;
@@ -9583,15 +9596,21 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
95839596

95849597
caller = state->frame[state->curframe - 1];
95859598
if (callee->in_callback_fn) {
9586-
/* enforce R0 return value range [0, 1]. */
9587-
struct tnum range = callee->callback_ret_range;
9588-
95899599
if (r0->type != SCALAR_VALUE) {
95909600
verbose(env, "R0 not a scalar value\n");
95919601
return -EACCES;
95929602
}
9593-
if (!tnum_in(range, r0->var_off)) {
9594-
verbose_invalid_scalar(env, r0, &range, "callback return", "R0");
9603+
9604+
/* we are going to rely on register's precise value */
9605+
err = mark_reg_read(env, r0, r0->parent, REG_LIVE_READ64);
9606+
err = err ?: mark_chain_precision(env, BPF_REG_0);
9607+
if (err)
9608+
return err;
9609+
9610+
/* enforce R0 return value range */
9611+
if (!retval_range_within(callee->callback_ret_range, r0)) {
9612+
verbose_invalid_scalar(env, r0, callee->callback_ret_range,
9613+
"At callback return", "R0");
95959614
return -EINVAL;
95969615
}
95979616
if (!calls_callback(env, callee->callsite)) {
@@ -11805,7 +11824,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
1180511824
return 0;
1180611825
}
1180711826

11808-
static int check_return_code(struct bpf_verifier_env *env, int regno);
11827+
static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name);
1180911828

1181011829
static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1181111830
int *insn_idx_p)
@@ -11942,7 +11961,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1194211961
* to bpf_throw becomes the return value of the program.
1194311962
*/
1194411963
if (!env->exception_callback_subprog) {
11945-
err = check_return_code(env, BPF_REG_1);
11964+
err = check_return_code(env, BPF_REG_1, "R1");
1194611965
if (err < 0)
1194711966
return err;
1194811967
}
@@ -14972,12 +14991,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
1497214991
return 0;
1497314992
}
1497414993

14975-
static int check_return_code(struct bpf_verifier_env *env, int regno)
14994+
static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name)
1497614995
{
14996+
const char *exit_ctx = "At program exit";
1497714997
struct tnum enforce_attach_type_range = tnum_unknown;
1497814998
const struct bpf_prog *prog = env->prog;
1497914999
struct bpf_reg_state *reg;
14980-
struct tnum range = tnum_range(0, 1), const_0 = tnum_const(0);
15000+
struct bpf_retval_range range = retval_range(0, 1);
1498115001
enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
1498215002
int err;
1498315003
struct bpf_func_state *frame = env->cur_state->frame[0];
@@ -15019,17 +15039,9 @@ static int check_return_code(struct bpf_verifier_env *env, int regno)
1501915039

1502015040
if (frame->in_async_callback_fn) {
1502115041
/* enforce return zero from async callbacks like timer */
15022-
if (reg->type != SCALAR_VALUE) {
15023-
verbose(env, "In async callback the register R%d is not a known value (%s)\n",
15024-
regno, reg_type_str(env, reg->type));
15025-
return -EINVAL;
15026-
}
15027-
15028-
if (!tnum_in(const_0, reg->var_off)) {
15029-
verbose_invalid_scalar(env, reg, &const_0, "async callback", "R0");
15030-
return -EINVAL;
15031-
}
15032-
return 0;
15042+
exit_ctx = "At async callback return";
15043+
range = retval_range(0, 0);
15044+
goto enforce_retval;
1503315045
}
1503415046

1503515047
if (is_subprog && !frame->in_exception_callback_fn) {
@@ -15052,14 +15064,14 @@ static int check_return_code(struct bpf_verifier_env *env, int regno)
1505215064
env->prog->expected_attach_type == BPF_CGROUP_INET4_GETSOCKNAME ||
1505315065
env->prog->expected_attach_type == BPF_CGROUP_INET6_GETSOCKNAME ||
1505415066
env->prog->expected_attach_type == BPF_CGROUP_UNIX_GETSOCKNAME)
15055-
range = tnum_range(1, 1);
15067+
range = retval_range(1, 1);
1505615068
if (env->prog->expected_attach_type == BPF_CGROUP_INET4_BIND ||
1505715069
env->prog->expected_attach_type == BPF_CGROUP_INET6_BIND)
15058-
range = tnum_range(0, 3);
15070+
range = retval_range(0, 3);
1505915071
break;
1506015072
case BPF_PROG_TYPE_CGROUP_SKB:
1506115073
if (env->prog->expected_attach_type == BPF_CGROUP_INET_EGRESS) {
15062-
range = tnum_range(0, 3);
15074+
range = retval_range(0, 3);
1506315075
enforce_attach_type_range = tnum_range(2, 3);
1506415076
}
1506515077
break;
@@ -15072,13 +15084,13 @@ static int check_return_code(struct bpf_verifier_env *env, int regno)
1507215084
case BPF_PROG_TYPE_RAW_TRACEPOINT:
1507315085
if (!env->prog->aux->attach_btf_id)
1507415086
return 0;
15075-
range = tnum_const(0);
15087+
range = retval_range(0, 0);
1507615088
break;
1507715089
case BPF_PROG_TYPE_TRACING:
1507815090
switch (env->prog->expected_attach_type) {
1507915091
case BPF_TRACE_FENTRY:
1508015092
case BPF_TRACE_FEXIT:
15081-
range = tnum_const(0);
15093+
range = retval_range(0, 0);
1508215094
break;
1508315095
case BPF_TRACE_RAW_TP:
1508415096
case BPF_MODIFY_RETURN:
@@ -15090,7 +15102,7 @@ static int check_return_code(struct bpf_verifier_env *env, int regno)
1509015102
}
1509115103
break;
1509215104
case BPF_PROG_TYPE_SK_LOOKUP:
15093-
range = tnum_range(SK_DROP, SK_PASS);
15105+
range = retval_range(SK_DROP, SK_PASS);
1509415106
break;
1509515107

1509615108
case BPF_PROG_TYPE_LSM:
@@ -15104,12 +15116,12 @@ static int check_return_code(struct bpf_verifier_env *env, int regno)
1510415116
/* Make sure programs that attach to void
1510515117
* hooks don't try to modify return value.
1510615118
*/
15107-
range = tnum_range(1, 1);
15119+
range = retval_range(1, 1);
1510815120
}
1510915121
break;
1511015122

1511115123
case BPF_PROG_TYPE_NETFILTER:
15112-
range = tnum_range(NF_DROP, NF_ACCEPT);
15124+
range = retval_range(NF_DROP, NF_ACCEPT);
1511315125
break;
1511415126
case BPF_PROG_TYPE_EXT:
1511515127
/* freplace program can return anything as its return value
@@ -15119,15 +15131,21 @@ static int check_return_code(struct bpf_verifier_env *env, int regno)
1511915131
return 0;
1512015132
}
1512115133

15134+
enforce_retval:
1512215135
if (reg->type != SCALAR_VALUE) {
15123-
verbose(env, "At program exit the register R%d is not a known value (%s)\n",
15124-
regno, reg_type_str(env, reg->type));
15136+
verbose(env, "%s the register R%d is not a known value (%s)\n",
15137+
exit_ctx, regno, reg_type_str(env, reg->type));
1512515138
return -EINVAL;
1512615139
}
1512715140

15128-
if (!tnum_in(range, reg->var_off)) {
15129-
verbose_invalid_scalar(env, reg, &range, "program exit", "R0");
15130-
if (prog->expected_attach_type == BPF_LSM_CGROUP &&
15141+
err = mark_chain_precision(env, regno);
15142+
if (err)
15143+
return err;
15144+
15145+
if (!retval_range_within(range, reg)) {
15146+
verbose_invalid_scalar(env, reg, range, exit_ctx, reg_name);
15147+
if (!is_subprog &&
15148+
prog->expected_attach_type == BPF_LSM_CGROUP &&
1513115149
prog_type == BPF_PROG_TYPE_LSM &&
1513215150
!prog->aux->attach_func_proto->type)
1513315151
verbose(env, "Note, BPF_LSM_CGROUP that attach to void LSM hooks can't modify return value!\n");
@@ -17410,7 +17428,7 @@ static int do_check(struct bpf_verifier_env *env)
1741017428
continue;
1741117429
}
1741217430

17413-
err = check_return_code(env, BPF_REG_0);
17431+
err = check_return_code(env, BPF_REG_0, "R0");
1741417432
if (err)
1741517433
return err;
1741617434
process_bpf_exit:

tools/testing/selftests/bpf/progs/exceptions_assert.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ int check_assert_generic(struct __sk_buff *ctx)
125125
}
126126

127127
SEC("?fentry/bpf_check")
128-
__failure __msg("At program exit the register R0 has value (0x40; 0x0)")
128+
__failure __msg("At program exit the register R1 has smin=64 smax=64")
129129
int check_assert_with_return(void *ctx)
130130
{
131131
bpf_assert_with(!ctx, 64);

tools/testing/selftests/bpf/progs/exceptions_fail.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ int reject_set_exception_cb_bad_ret1(void *ctx)
308308
}
309309

310310
SEC("?fentry/bpf_check")
311-
__failure __msg("At program exit the register R0 has value (0x40; 0x0) should")
311+
__failure __msg("At program exit the register R1 has smin=64 smax=64 should")
312312
int reject_set_exception_cb_bad_ret2(void *ctx)
313313
{
314314
bpf_throw(64);

0 commit comments

Comments
 (0)