Commit c871d0e0 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov

bpf: enforce precise retval range on program exit

Similarly to subprog/callback logic, enforce return value of BPF program
using more precise smin/smax range.

We need to adjust a bunch of tests due to a changed format of an error
message.
Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Acked-by: default avatarShung-Hsi Yu <shung-hsi.yu@suse.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231202175705.885270-7-andrii@kernel.orgSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 60a6b2c7
...@@ -362,20 +362,23 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...) ...@@ -362,20 +362,23 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
static void verbose_invalid_scalar(struct bpf_verifier_env *env, static void verbose_invalid_scalar(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, struct bpf_reg_state *reg,
struct tnum *range, const char *ctx, struct bpf_retval_range range, const char *ctx,
const char *reg_name) const char *reg_name)
{ {
char tn_buf[48]; bool unknown = true;
verbose(env, "At %s the register %s ", ctx, reg_name); verbose(env, "At %s the register %s has", ctx, reg_name);
if (!tnum_is_unknown(reg->var_off)) { if (reg->smin_value > S64_MIN) {
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); verbose(env, " smin=%lld", reg->smin_value);
verbose(env, "has value %s", tn_buf); unknown = false;
} else {
verbose(env, "has unknown scalar value");
} }
tnum_strn(tn_buf, sizeof(tn_buf), *range); if (reg->smax_value < S64_MAX) {
verbose(env, " should have been in %s\n", tn_buf); verbose(env, " smax=%lld", reg->smax_value);
unknown = false;
}
if (unknown)
verbose(env, " unknown scalar value");
verbose(env, " should have been in [%d, %d]\n", range.minval, range.maxval);
} }
static bool type_may_be_null(u32 type) static bool type_may_be_null(u32 type)
...@@ -9606,10 +9609,8 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) ...@@ -9606,10 +9609,8 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
/* enforce R0 return value range */ /* enforce R0 return value range */
if (!retval_range_within(callee->callback_ret_range, r0)) { if (!retval_range_within(callee->callback_ret_range, r0)) {
struct tnum range = tnum_range(callee->callback_ret_range.minval, verbose_invalid_scalar(env, r0, callee->callback_ret_range,
callee->callback_ret_range.maxval); "callback return", "R0");
verbose_invalid_scalar(env, r0, &range, "callback return", "R0");
return -EINVAL; return -EINVAL;
} }
if (!calls_callback(env, callee->callsite)) { if (!calls_callback(env, callee->callsite)) {
...@@ -14995,7 +14996,8 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -14995,7 +14996,8 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
struct tnum enforce_attach_type_range = tnum_unknown; struct tnum enforce_attach_type_range = tnum_unknown;
const struct bpf_prog *prog = env->prog; const struct bpf_prog *prog = env->prog;
struct bpf_reg_state *reg; struct bpf_reg_state *reg;
struct tnum range = tnum_range(0, 1), const_0 = tnum_const(0); struct bpf_retval_range range = retval_range(0, 1);
struct bpf_retval_range const_0 = retval_range(0, 0);
enum bpf_prog_type prog_type = resolve_prog_type(env->prog); enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
int err; int err;
struct bpf_func_state *frame = env->cur_state->frame[0]; struct bpf_func_state *frame = env->cur_state->frame[0];
...@@ -15043,8 +15045,8 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -15043,8 +15045,8 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
return -EINVAL; return -EINVAL;
} }
if (!tnum_in(const_0, reg->var_off)) { if (!retval_range_within(const_0, reg)) {
verbose_invalid_scalar(env, reg, &const_0, "async callback", reg_name); verbose_invalid_scalar(env, reg, const_0, "async callback", reg_name);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
...@@ -15070,14 +15072,14 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -15070,14 +15072,14 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
env->prog->expected_attach_type == BPF_CGROUP_INET4_GETSOCKNAME || env->prog->expected_attach_type == BPF_CGROUP_INET4_GETSOCKNAME ||
env->prog->expected_attach_type == BPF_CGROUP_INET6_GETSOCKNAME || env->prog->expected_attach_type == BPF_CGROUP_INET6_GETSOCKNAME ||
env->prog->expected_attach_type == BPF_CGROUP_UNIX_GETSOCKNAME) env->prog->expected_attach_type == BPF_CGROUP_UNIX_GETSOCKNAME)
range = tnum_range(1, 1); range = retval_range(1, 1);
if (env->prog->expected_attach_type == BPF_CGROUP_INET4_BIND || if (env->prog->expected_attach_type == BPF_CGROUP_INET4_BIND ||
env->prog->expected_attach_type == BPF_CGROUP_INET6_BIND) env->prog->expected_attach_type == BPF_CGROUP_INET6_BIND)
range = tnum_range(0, 3); range = retval_range(0, 3);
break; break;
case BPF_PROG_TYPE_CGROUP_SKB: case BPF_PROG_TYPE_CGROUP_SKB:
if (env->prog->expected_attach_type == BPF_CGROUP_INET_EGRESS) { if (env->prog->expected_attach_type == BPF_CGROUP_INET_EGRESS) {
range = tnum_range(0, 3); range = retval_range(0, 3);
enforce_attach_type_range = tnum_range(2, 3); enforce_attach_type_range = tnum_range(2, 3);
} }
break; break;
...@@ -15090,13 +15092,13 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -15090,13 +15092,13 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
case BPF_PROG_TYPE_RAW_TRACEPOINT: case BPF_PROG_TYPE_RAW_TRACEPOINT:
if (!env->prog->aux->attach_btf_id) if (!env->prog->aux->attach_btf_id)
return 0; return 0;
range = tnum_const(0); range = retval_range(0, 0);
break; break;
case BPF_PROG_TYPE_TRACING: case BPF_PROG_TYPE_TRACING:
switch (env->prog->expected_attach_type) { switch (env->prog->expected_attach_type) {
case BPF_TRACE_FENTRY: case BPF_TRACE_FENTRY:
case BPF_TRACE_FEXIT: case BPF_TRACE_FEXIT:
range = tnum_const(0); range = retval_range(0, 0);
break; break;
case BPF_TRACE_RAW_TP: case BPF_TRACE_RAW_TP:
case BPF_MODIFY_RETURN: case BPF_MODIFY_RETURN:
...@@ -15108,7 +15110,7 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -15108,7 +15110,7 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
} }
break; break;
case BPF_PROG_TYPE_SK_LOOKUP: case BPF_PROG_TYPE_SK_LOOKUP:
range = tnum_range(SK_DROP, SK_PASS); range = retval_range(SK_DROP, SK_PASS);
break; break;
case BPF_PROG_TYPE_LSM: case BPF_PROG_TYPE_LSM:
...@@ -15122,12 +15124,12 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -15122,12 +15124,12 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
/* Make sure programs that attach to void /* Make sure programs that attach to void
* hooks don't try to modify return value. * hooks don't try to modify return value.
*/ */
range = tnum_range(1, 1); range = retval_range(1, 1);
} }
break; break;
case BPF_PROG_TYPE_NETFILTER: case BPF_PROG_TYPE_NETFILTER:
range = tnum_range(NF_DROP, NF_ACCEPT); range = retval_range(NF_DROP, NF_ACCEPT);
break; break;
case BPF_PROG_TYPE_EXT: case BPF_PROG_TYPE_EXT:
/* freplace program can return anything as its return value /* freplace program can return anything as its return value
...@@ -15143,8 +15145,8 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char ...@@ -15143,8 +15145,8 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
return -EINVAL; return -EINVAL;
} }
if (!tnum_in(range, reg->var_off)) { if (!retval_range_within(range, reg)) {
verbose_invalid_scalar(env, reg, &range, "program exit", reg_name); verbose_invalid_scalar(env, reg, range, "program exit", reg_name);
if (prog->expected_attach_type == BPF_LSM_CGROUP && if (prog->expected_attach_type == BPF_LSM_CGROUP &&
prog_type == BPF_PROG_TYPE_LSM && prog_type == BPF_PROG_TYPE_LSM &&
!prog->aux->attach_func_proto->type) !prog->aux->attach_func_proto->type)
......
...@@ -125,7 +125,7 @@ int check_assert_generic(struct __sk_buff *ctx) ...@@ -125,7 +125,7 @@ int check_assert_generic(struct __sk_buff *ctx)
} }
SEC("?fentry/bpf_check") SEC("?fentry/bpf_check")
__failure __msg("At program exit the register R1 has value (0x40; 0x0)") __failure __msg("At program exit the register R1 has smin=64 smax=64")
int check_assert_with_return(void *ctx) int check_assert_with_return(void *ctx)
{ {
bpf_assert_with(!ctx, 64); bpf_assert_with(!ctx, 64);
......
...@@ -308,7 +308,7 @@ int reject_set_exception_cb_bad_ret1(void *ctx) ...@@ -308,7 +308,7 @@ int reject_set_exception_cb_bad_ret1(void *ctx)
} }
SEC("?fentry/bpf_check") SEC("?fentry/bpf_check")
__failure __msg("At program exit the register R1 has value (0x40; 0x0) should") __failure __msg("At program exit the register R1 has smin=64 smax=64 should")
int reject_set_exception_cb_bad_ret2(void *ctx) int reject_set_exception_cb_bad_ret2(void *ctx)
{ {
bpf_throw(64); bpf_throw(64);
......
...@@ -13,7 +13,7 @@ __noinline int foo(unsigned int *v) ...@@ -13,7 +13,7 @@ __noinline int foo(unsigned int *v)
} }
SEC("cgroup_skb/ingress") SEC("cgroup_skb/ingress")
__failure __msg("At program exit the register R0 has value") __failure __msg("At program exit the register R0 has ")
int global_func15(struct __sk_buff *skb) int global_func15(struct __sk_buff *skb)
{ {
unsigned int v = 1; unsigned int v = 1;
......
...@@ -30,7 +30,7 @@ static int timer_cb_ret1(void *map, int *key, struct bpf_timer *timer) ...@@ -30,7 +30,7 @@ static int timer_cb_ret1(void *map, int *key, struct bpf_timer *timer)
} }
SEC("fentry/bpf_fentry_test1") SEC("fentry/bpf_fentry_test1")
__failure __msg("should have been in (0x0; 0x0)") __failure __msg("should have been in [0, 0]")
int BPF_PROG2(test_ret_1, int, a) int BPF_PROG2(test_ret_1, int, a)
{ {
int key = 0; int key = 0;
......
...@@ -184,7 +184,7 @@ invalid_drain_callback_return(struct bpf_dynptr *dynptr, void *context) ...@@ -184,7 +184,7 @@ invalid_drain_callback_return(struct bpf_dynptr *dynptr, void *context)
* not be able to write to that pointer. * not be able to write to that pointer.
*/ */
SEC("?raw_tp") SEC("?raw_tp")
__failure __msg("At callback return the register R0 has value") __failure __msg("At callback return the register R0 has ")
int user_ringbuf_callback_invalid_return(void *ctx) int user_ringbuf_callback_invalid_return(void *ctx)
{ {
bpf_user_ringbuf_drain(&user_ringbuf, invalid_drain_callback_return, NULL, 0); bpf_user_ringbuf_drain(&user_ringbuf, invalid_drain_callback_return, NULL, 0);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
SEC("cgroup/sock") SEC("cgroup/sock")
__description("bpf_exit with invalid return code. test1") __description("bpf_exit with invalid return code. test1")
__failure __msg("R0 has value (0x0; 0xffffffff)") __failure __msg("smin=0 smax=4294967295 should have been in [0, 1]")
__naked void with_invalid_return_code_test1(void) __naked void with_invalid_return_code_test1(void)
{ {
asm volatile (" \ asm volatile (" \
...@@ -30,7 +30,7 @@ __naked void with_invalid_return_code_test2(void) ...@@ -30,7 +30,7 @@ __naked void with_invalid_return_code_test2(void)
SEC("cgroup/sock") SEC("cgroup/sock")
__description("bpf_exit with invalid return code. test3") __description("bpf_exit with invalid return code. test3")
__failure __msg("R0 has value (0x0; 0x3)") __failure __msg("smin=0 smax=3 should have been in [0, 1]")
__naked void with_invalid_return_code_test3(void) __naked void with_invalid_return_code_test3(void)
{ {
asm volatile (" \ asm volatile (" \
...@@ -53,7 +53,7 @@ __naked void with_invalid_return_code_test4(void) ...@@ -53,7 +53,7 @@ __naked void with_invalid_return_code_test4(void)
SEC("cgroup/sock") SEC("cgroup/sock")
__description("bpf_exit with invalid return code. test5") __description("bpf_exit with invalid return code. test5")
__failure __msg("R0 has value (0x2; 0x0)") __failure __msg("smin=2 smax=2 should have been in [0, 1]")
__naked void with_invalid_return_code_test5(void) __naked void with_invalid_return_code_test5(void)
{ {
asm volatile (" \ asm volatile (" \
...@@ -75,7 +75,7 @@ __naked void with_invalid_return_code_test6(void) ...@@ -75,7 +75,7 @@ __naked void with_invalid_return_code_test6(void)
SEC("cgroup/sock") SEC("cgroup/sock")
__description("bpf_exit with invalid return code. test7") __description("bpf_exit with invalid return code. test7")
__failure __msg("R0 has unknown scalar value") __failure __msg("R0 has unknown scalar value should have been in [0, 1]")
__naked void with_invalid_return_code_test7(void) __naked void with_invalid_return_code_test7(void)
{ {
asm volatile (" \ asm volatile (" \
......
...@@ -39,7 +39,7 @@ __naked void with_valid_return_code_test3(void) ...@@ -39,7 +39,7 @@ __naked void with_valid_return_code_test3(void)
SEC("netfilter") SEC("netfilter")
__description("bpf_exit with invalid return code. test4") __description("bpf_exit with invalid return code. test4")
__failure __msg("R0 has value (0x2; 0x0)") __failure __msg("R0 has smin=2 smax=2 should have been in [0, 1]")
__naked void with_invalid_return_code_test4(void) __naked void with_invalid_return_code_test4(void)
{ {
asm volatile (" \ asm volatile (" \
......
...@@ -148,7 +148,7 @@ __msg("mark_precise: frame1: regs=r0 stack= before 11: (b7) r0 = 0") ...@@ -148,7 +148,7 @@ __msg("mark_precise: frame1: regs=r0 stack= before 11: (b7) r0 = 0")
__msg("from 10 to 12: frame1: R0=scalar(smin=umin=1001") __msg("from 10 to 12: frame1: R0=scalar(smin=umin=1001")
/* check that branch code path marks r0 as precise, before failing */ /* check that branch code path marks r0 as precise, before failing */
__msg("mark_precise: frame1: regs=r0 stack= before 9: (85) call bpf_get_prandom_u32#7") __msg("mark_precise: frame1: regs=r0 stack= before 9: (85) call bpf_get_prandom_u32#7")
__msg("At callback return the register R0 has value (0x0; 0x7fffffffffffffff) should have been in (0x0; 0x1)") __msg("At callback return the register R0 has smin=1001 should have been in [0, 1]")
__naked int callback_precise_return_fail(void) __naked int callback_precise_return_fail(void)
{ {
asm volatile ( asm volatile (
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment