Skip to content

Commit cb4158c

Browse files
kkdwvdAlexei Starovoitov
authored andcommitted
bpf: Mark raw_tp arguments with PTR_MAYBE_NULL
Arguments to a raw tracepoint are tagged as trusted, which carries the semantics that the pointer will be non-NULL. However, in certain cases, a raw tracepoint argument may end up being NULL. More context about this issue is available in [0]. Thus, there is a discrepancy between the reality, that raw_tp arguments can actually be NULL, and the verifier's knowledge, that they are never NULL, causing explicit NULL checks to be deleted, and accesses to such pointers potentially crashing the kernel. To fix this, mark raw_tp arguments as PTR_MAYBE_NULL, and then special case the dereference and pointer arithmetic to permit it, and allow passing them into helpers/kfuncs; these exceptions are made for raw_tp programs only. Ensure that we don't do this when ref_obj_id > 0, as in that case this is an acquired object and doesn't need such adjustment. The reason we do mask_raw_tp_trusted_reg logic is because other will recheck in places whether the register is a trusted_reg, and then consider our register as untrusted when detecting the presence of the PTR_MAYBE_NULL flag. To allow safe dereference, we enable PROBE_MEM marking when we see loads into trusted pointers with PTR_MAYBE_NULL. While trusted raw_tp arguments can also be passed into helpers or kfuncs where such broken assumption may cause issues, a future patch set will tackle their case separately, as PTR_TO_BTF_ID (without PTR_TRUSTED) can already be passed into helpers and causes similar problems. Thus, they are left alone for now. It is possible that these checks also permit passing non-raw_tp args that are trusted PTR_TO_BTF_ID with null marking. In such a case, allowing dereference when pointer is NULL expands allowed behavior, so won't regress existing programs, and the case of passing these into helpers is the same as above and will be dealt with later. Also update the failure case in tp_btf_nullable selftest to capture the new behavior, as the verifier will no longer cause an error when directly dereference a raw tracepoint argument marked as __nullable. [0]: https://lore.kernel.org/bpf/ZrCZS6nisraEqehw@jlelli-thinkpadt14gen4.remote.csb Reviewed-by: Jiri Olsa <jolsa@kernel.org> Reported-by: Juri Lelli <juri.lelli@redhat.com> Tested-by: Juri Lelli <juri.lelli@redhat.com> Fixes: 3f00c52 ("bpf: Allow trusted pointers to be passed to KF_TRUSTED_ARGS kfuncs") Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20241104171959.2938862-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 9a78313 commit cb4158c

File tree

4 files changed

+87
-9
lines changed

4 files changed

+87
-9
lines changed

include/linux/bpf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3495,4 +3495,10 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog)
34953495
return prog->aux->func_idx != 0;
34963496
}
34973497

3498+
static inline bool bpf_prog_is_raw_tp(const struct bpf_prog *prog)
3499+
{
3500+
return prog->type == BPF_PROG_TYPE_TRACING &&
3501+
prog->expected_attach_type == BPF_TRACE_RAW_TP;
3502+
}
3503+
34983504
#endif /* _LINUX_BPF_H */

kernel/bpf/btf.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6588,7 +6588,10 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
65886588
if (prog_args_trusted(prog))
65896589
info->reg_type |= PTR_TRUSTED;
65906590

6591-
if (btf_param_match_suffix(btf, &args[arg], "__nullable"))
6591+
/* Raw tracepoint arguments always get marked as maybe NULL */
6592+
if (bpf_prog_is_raw_tp(prog))
6593+
info->reg_type |= PTR_MAYBE_NULL;
6594+
else if (btf_param_match_suffix(btf, &args[arg], "__nullable"))
65926595
info->reg_type |= PTR_MAYBE_NULL;
65936596

65946597
if (tgt_prog) {

kernel/bpf/verifier.c

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,25 @@ static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg)
418418
return rec;
419419
}
420420

421+
static bool mask_raw_tp_reg_cond(const struct bpf_verifier_env *env, struct bpf_reg_state *reg) {
422+
return reg->type == (PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL) &&
423+
bpf_prog_is_raw_tp(env->prog) && !reg->ref_obj_id;
424+
}
425+
426+
static bool mask_raw_tp_reg(const struct bpf_verifier_env *env, struct bpf_reg_state *reg)
427+
{
428+
if (!mask_raw_tp_reg_cond(env, reg))
429+
return false;
430+
reg->type &= ~PTR_MAYBE_NULL;
431+
return true;
432+
}
433+
434+
static void unmask_raw_tp_reg(struct bpf_reg_state *reg, bool result)
435+
{
436+
if (result)
437+
reg->type |= PTR_MAYBE_NULL;
438+
}
439+
421440
static bool subprog_is_global(const struct bpf_verifier_env *env, int subprog)
422441
{
423442
struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
@@ -6622,6 +6641,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
66226641
const char *field_name = NULL;
66236642
enum bpf_type_flag flag = 0;
66246643
u32 btf_id = 0;
6644+
bool mask;
66256645
int ret;
66266646

66276647
if (!env->allow_ptr_leaks) {
@@ -6693,7 +6713,21 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
66936713

66946714
if (ret < 0)
66956715
return ret;
6696-
6716+
/* For raw_tp progs, we allow dereference of PTR_MAYBE_NULL
6717+
* trusted PTR_TO_BTF_ID, these are the ones that are possibly
6718+
* arguments to the raw_tp. Since internal checks in for trusted
6719+
* reg in check_ptr_to_btf_access would consider PTR_MAYBE_NULL
6720+
* modifier as problematic, mask it out temporarily for the
6721+
* check. Don't apply this to pointers with ref_obj_id > 0, as
6722+
* those won't be raw_tp args.
6723+
*
6724+
* We may end up applying this relaxation to other trusted
6725+
* PTR_TO_BTF_ID with maybe null flag, since we cannot
6726+
* distinguish PTR_MAYBE_NULL tagged for arguments vs normal
6727+
* tagging, but that should expand allowed behavior, and not
6728+
* cause regression for existing behavior.
6729+
*/
6730+
mask = mask_raw_tp_reg(env, reg);
66976731
if (ret != PTR_TO_BTF_ID) {
66986732
/* just mark; */
66996733

@@ -6754,8 +6788,13 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
67546788
clear_trusted_flags(&flag);
67556789
}
67566790

6757-
if (atype == BPF_READ && value_regno >= 0)
6791+
if (atype == BPF_READ && value_regno >= 0) {
67586792
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
6793+
/* We've assigned a new type to regno, so don't undo masking. */
6794+
if (regno == value_regno)
6795+
mask = false;
6796+
}
6797+
unmask_raw_tp_reg(reg, mask);
67596798

67606799
return 0;
67616800
}
@@ -7140,7 +7179,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
71407179
if (!err && t == BPF_READ && value_regno >= 0)
71417180
mark_reg_unknown(env, regs, value_regno);
71427181
} else if (base_type(reg->type) == PTR_TO_BTF_ID &&
7143-
!type_may_be_null(reg->type)) {
7182+
(mask_raw_tp_reg_cond(env, reg) || !type_may_be_null(reg->type))) {
71447183
err = check_ptr_to_btf_access(env, regs, regno, off, size, t,
71457184
value_regno);
71467185
} else if (reg->type == CONST_PTR_TO_MAP) {
@@ -8833,6 +8872,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
88338872
enum bpf_reg_type type = reg->type;
88348873
u32 *arg_btf_id = NULL;
88358874
int err = 0;
8875+
bool mask;
88368876

88378877
if (arg_type == ARG_DONTCARE)
88388878
return 0;
@@ -8873,11 +8913,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
88738913
base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK)
88748914
arg_btf_id = fn->arg_btf_id[arg];
88758915

8916+
mask = mask_raw_tp_reg(env, reg);
88768917
err = check_reg_type(env, regno, arg_type, arg_btf_id, meta);
8877-
if (err)
8878-
return err;
88798918

8880-
err = check_func_arg_reg_off(env, reg, regno, arg_type);
8919+
err = err ?: check_func_arg_reg_off(env, reg, regno, arg_type);
8920+
unmask_raw_tp_reg(reg, mask);
88818921
if (err)
88828922
return err;
88838923

@@ -9672,14 +9712,17 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
96729712
return ret;
96739713
} else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
96749714
struct bpf_call_arg_meta meta;
9715+
bool mask;
96759716
int err;
96769717

96779718
if (register_is_null(reg) && type_may_be_null(arg->arg_type))
96789719
continue;
96799720

96809721
memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */
9722+
mask = mask_raw_tp_reg(env, reg);
96819723
err = check_reg_type(env, regno, arg->arg_type, &arg->btf_id, &meta);
96829724
err = err ?: check_func_arg_reg_off(env, reg, regno, arg->arg_type);
9725+
unmask_raw_tp_reg(reg, mask);
96839726
if (err)
96849727
return err;
96859728
} else {
@@ -12007,6 +12050,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
1200712050
enum bpf_arg_type arg_type = ARG_DONTCARE;
1200812051
u32 regno = i + 1, ref_id, type_size;
1200912052
bool is_ret_buf_sz = false;
12053+
bool mask = false;
1201012054
int kf_arg_type;
1201112055

1201212056
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
@@ -12065,12 +12109,15 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
1206512109
return -EINVAL;
1206612110
}
1206712111

12112+
mask = mask_raw_tp_reg(env, reg);
1206812113
if ((is_kfunc_trusted_args(meta) || is_kfunc_rcu(meta)) &&
1206912114
(register_is_null(reg) || type_may_be_null(reg->type)) &&
1207012115
!is_kfunc_arg_nullable(meta->btf, &args[i])) {
1207112116
verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i);
12117+
unmask_raw_tp_reg(reg, mask);
1207212118
return -EACCES;
1207312119
}
12120+
unmask_raw_tp_reg(reg, mask);
1207412121

1207512122
if (reg->ref_obj_id) {
1207612123
if (is_kfunc_release(meta) && meta->ref_obj_id) {
@@ -12128,16 +12175,24 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
1212812175
if (!is_kfunc_trusted_args(meta) && !is_kfunc_rcu(meta))
1212912176
break;
1213012177

12178+
/* Allow passing maybe NULL raw_tp arguments to
12179+
* kfuncs for compatibility. Don't apply this to
12180+
* arguments with ref_obj_id > 0.
12181+
*/
12182+
mask = mask_raw_tp_reg(env, reg);
1213112183
if (!is_trusted_reg(reg)) {
1213212184
if (!is_kfunc_rcu(meta)) {
1213312185
verbose(env, "R%d must be referenced or trusted\n", regno);
12186+
unmask_raw_tp_reg(reg, mask);
1213412187
return -EINVAL;
1213512188
}
1213612189
if (!is_rcu_reg(reg)) {
1213712190
verbose(env, "R%d must be a rcu pointer\n", regno);
12191+
unmask_raw_tp_reg(reg, mask);
1213812192
return -EINVAL;
1213912193
}
1214012194
}
12195+
unmask_raw_tp_reg(reg, mask);
1214112196
fallthrough;
1214212197
case KF_ARG_PTR_TO_CTX:
1214312198
case KF_ARG_PTR_TO_DYNPTR:
@@ -12160,7 +12215,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
1216012215

1216112216
if (is_kfunc_release(meta) && reg->ref_obj_id)
1216212217
arg_type |= OBJ_RELEASE;
12218+
mask = mask_raw_tp_reg(env, reg);
1216312219
ret = check_func_arg_reg_off(env, reg, regno, arg_type);
12220+
unmask_raw_tp_reg(reg, mask);
1216412221
if (ret < 0)
1216512222
return ret;
1216612223

@@ -12337,6 +12394,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
1233712394
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
1233812395
fallthrough;
1233912396
case KF_ARG_PTR_TO_BTF_ID:
12397+
mask = mask_raw_tp_reg(env, reg);
1234012398
/* Only base_type is checked, further checks are done here */
1234112399
if ((base_type(reg->type) != PTR_TO_BTF_ID ||
1234212400
(bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
@@ -12345,9 +12403,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
1234512403
verbose(env, "expected %s or socket\n",
1234612404
reg_type_str(env, base_type(reg->type) |
1234712405
(type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
12406+
unmask_raw_tp_reg(reg, mask);
1234812407
return -EINVAL;
1234912408
}
1235012409
ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
12410+
unmask_raw_tp_reg(reg, mask);
1235112411
if (ret < 0)
1235212412
return ret;
1235312413
break;
@@ -13320,7 +13380,7 @@ static int sanitize_check_bounds(struct bpf_verifier_env *env,
1332013380
*/
1332113381
static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
1332213382
struct bpf_insn *insn,
13323-
const struct bpf_reg_state *ptr_reg,
13383+
struct bpf_reg_state *ptr_reg,
1332413384
const struct bpf_reg_state *off_reg)
1332513385
{
1332613386
struct bpf_verifier_state *vstate = env->cur_state;
@@ -13334,6 +13394,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
1333413394
struct bpf_sanitize_info info = {};
1333513395
u8 opcode = BPF_OP(insn->code);
1333613396
u32 dst = insn->dst_reg;
13397+
bool mask;
1333713398
int ret;
1333813399

1333913400
dst_reg = &regs[dst];
@@ -13360,11 +13421,14 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
1336013421
return -EACCES;
1336113422
}
1336213423

13424+
mask = mask_raw_tp_reg(env, ptr_reg);
1336313425
if (ptr_reg->type & PTR_MAYBE_NULL) {
1336413426
verbose(env, "R%d pointer arithmetic on %s prohibited, null-check it first\n",
1336513427
dst, reg_type_str(env, ptr_reg->type));
13428+
unmask_raw_tp_reg(ptr_reg, mask);
1336613429
return -EACCES;
1336713430
}
13431+
unmask_raw_tp_reg(ptr_reg, mask);
1336813432

1336913433
switch (base_type(ptr_reg->type)) {
1337013434
case PTR_TO_CTX:
@@ -19866,6 +19930,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
1986619930
* for this case.
1986719931
*/
1986819932
case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED:
19933+
case PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL:
1986919934
if (type == BPF_READ) {
1987019935
if (BPF_MODE(insn->code) == BPF_MEM)
1987119936
insn->code = BPF_LDX | BPF_PROBE_MEM |

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
#include "bpf_misc.h"
88

99
SEC("tp_btf/bpf_testmod_test_nullable_bare")
10-
__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'")
10+
/* This used to be a failure test, but raw_tp nullable arguments can now
11+
* directly be dereferenced, whether they have nullable annotation or not,
12+
* and don't need to be explicitly checked.
13+
*/
14+
__success
1115
int BPF_PROG(handle_tp_btf_nullable_bare1, struct bpf_testmod_test_read_ctx *nullable_ctx)
1216
{
1317
return nullable_ctx->len;

0 commit comments

Comments
 (0)