Skip to content

Commit f1434ee

Browse files
kkdwivediKernel Patches Daemon
authored andcommitted
bpf: Treat first argument as return value for bpf_throw
In case of the default exception callback, change the behavior of bpf_throw, where the passed cookie value is no longer ignored, but is instead the return value of the default exception callback. As such, we need to place restrictions on the value being passed into bpf_throw in such a case, only allowing those permitted by the check_return_code function. Thus, bpf_throw can now control the return value of the program from each call site without having the user install a custom exception callback just to override the return value when an exception is thrown. We also modify the hidden subprog instructions to now move BPF_REG_1 to BPF_REG_0, so as to set the return value before exit in the default callback. Signed-off-by: Kumar Kartikeya Dwivedi <[email protected]>
1 parent 362df38 commit f1434ee

File tree

1 file changed

+24
-13
lines changed

1 file changed

+24
-13
lines changed

kernel/bpf/verifier.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11485,6 +11485,8 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
1148511485
return 0;
1148611486
}
1148711487

11488+
static int check_return_code(struct bpf_verifier_env *env, int regno);
11489+
1148811490
static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1148911491
int *insn_idx_p)
1149011492
{
@@ -11613,6 +11615,15 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1161311615
return -ENOTSUPP;
1161411616
}
1161511617
env->seen_exception = true;
11618+
11619+
/* In the case of the default callback, the cookie value passed
11620+
* to bpf_throw becomes the return value of the program.
11621+
*/
11622+
if (!env->exception_callback_subprog) {
11623+
err = check_return_code(env, BPF_REG_1);
11624+
if (err < 0)
11625+
return err;
11626+
}
1161611627
}
1161711628

1161811629
for (i = 0; i < CALLER_SAVED_REGS; i++)
@@ -14709,7 +14720,7 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
1470914720
return 0;
1471014721
}
1471114722

14712-
static int check_return_code(struct bpf_verifier_env *env)
14723+
static int check_return_code(struct bpf_verifier_env *env, int regno)
1471314724
{
1471414725
struct tnum enforce_attach_type_range = tnum_unknown;
1471514726
const struct bpf_prog *prog = env->prog;
@@ -14743,22 +14754,22 @@ static int check_return_code(struct bpf_verifier_env *env)
1474314754
* of bpf_exit, which means that program wrote
1474414755
* something into it earlier
1474514756
*/
14746-
err = check_reg_arg(env, BPF_REG_0, SRC_OP);
14757+
err = check_reg_arg(env, regno, SRC_OP);
1474714758
if (err)
1474814759
return err;
1474914760

14750-
if (is_pointer_value(env, BPF_REG_0)) {
14751-
verbose(env, "R0 leaks addr as return value\n");
14761+
if (is_pointer_value(env, regno)) {
14762+
verbose(env, "R%d leaks addr as return value\n", regno);
1475214763
return -EACCES;
1475314764
}
1475414765

14755-
reg = cur_regs(env) + BPF_REG_0;
14766+
reg = cur_regs(env) + regno;
1475614767

1475714768
if (frame->in_async_callback_fn) {
1475814769
/* enforce return zero from async callbacks like timer */
1475914770
if (reg->type != SCALAR_VALUE) {
14760-
verbose(env, "In async callback the register R0 is not a known value (%s)\n",
14761-
reg_type_str(env, reg->type));
14771+
verbose(env, "In async callback the register R%d is not a known value (%s)\n",
14772+
regno, reg_type_str(env, reg->type));
1476214773
return -EINVAL;
1476314774
}
1476414775

@@ -14771,8 +14782,8 @@ static int check_return_code(struct bpf_verifier_env *env)
1477114782

1477214783
if (is_subprog && !frame->in_exception_callback_fn) {
1477314784
if (reg->type != SCALAR_VALUE) {
14774-
verbose(env, "At subprogram exit the register R0 is not a scalar value (%s)\n",
14775-
reg_type_str(env, reg->type));
14785+
verbose(env, "At subprogram exit the register R%d is not a scalar value (%s)\n",
14786+
regno, reg_type_str(env, reg->type));
1477614787
return -EINVAL;
1477714788
}
1477814789
return 0;
@@ -14854,8 +14865,8 @@ static int check_return_code(struct bpf_verifier_env *env)
1485414865
}
1485514866

1485614867
if (reg->type != SCALAR_VALUE) {
14857-
verbose(env, "At program exit the register R0 is not a known value (%s)\n",
14858-
reg_type_str(env, reg->type));
14868+
verbose(env, "At program exit the register R%d is not a known value (%s)\n",
14869+
regno, reg_type_str(env, reg->type));
1485914870
return -EINVAL;
1486014871
}
1486114872

@@ -17053,7 +17064,7 @@ static int do_check(struct bpf_verifier_env *env)
1705317064
continue;
1705417065
}
1705517066

17056-
err = check_return_code(env);
17067+
err = check_return_code(env, BPF_REG_0);
1705717068
if (err)
1705817069
return err;
1705917070
process_bpf_exit:
@@ -18722,7 +18733,7 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
1872218733
if (env->seen_exception && !env->exception_callback_subprog) {
1872318734
struct bpf_insn patch[] = {
1872418735
env->prog->insnsi[insn_cnt - 1],
18725-
BPF_MOV64_IMM(BPF_REG_0, 0),
18736+
BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
1872618737
BPF_EXIT_INSN(),
1872718738
};
1872818739

0 commit comments

Comments
 (0)