Commit 282de143 authored by Kumar Kartikeya Dwivedi's avatar Kumar Kartikeya Dwivedi Committed by Alexei Starovoitov

bpf: Introduce allocated objects support

Introduce support for representing pointers to objects allocated by the
BPF program, i.e. PTR_TO_BTF_ID that point to a type in program BTF.
This is indicated by the presence of MEM_ALLOC type flag in reg->type to
avoid having to check btf_is_kernel when trying to match argument types
in helpers.

Whenever walking such types, any pointers being walked will always yield
a SCALAR instead of pointer. In the future we might permit kptr inside
such allocated objects (either kernel or program allocated), and it will
then form a PTR_TO_BTF_ID of the respective type.

For now, such allocated objects will always be referenced in verifier
context, hence ref_obj_id == 0 for them is a bug. It is allowed to write
to such objects, as long fields that are special are not touched
(support for which will be added in subsequent patches). Note that once
such a pointer is marked PTR_UNTRUSTED, it is no longer allowed to write
to it.

No PROBE_MEM handling is therefore done for loads into this type unless
PTR_UNTRUSTED is part of the register type, since they can never be in
an undefined state, and their lifetime will always be valid.
Signed-off-by: default avatarKumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-6-memxor@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent f73e601a
...@@ -525,6 +525,11 @@ enum bpf_type_flag { ...@@ -525,6 +525,11 @@ enum bpf_type_flag {
/* Size is known at compile time. */ /* Size is known at compile time. */
MEM_FIXED_SIZE = BIT(10 + BPF_BASE_TYPE_BITS), MEM_FIXED_SIZE = BIT(10 + BPF_BASE_TYPE_BITS),
/* MEM is of an allocated object of type in program BTF. This is used to
* tag PTR_TO_BTF_ID allocated using bpf_obj_new.
*/
MEM_ALLOC = BIT(11 + BPF_BASE_TYPE_BITS),
__BPF_TYPE_FLAG_MAX, __BPF_TYPE_FLAG_MAX,
__BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1, __BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1,
}; };
...@@ -2792,4 +2797,10 @@ struct bpf_key { ...@@ -2792,4 +2797,10 @@ struct bpf_key {
bool has_ref; bool has_ref;
}; };
#endif /* CONFIG_KEYS */ #endif /* CONFIG_KEYS */
static inline bool type_is_alloc(u32 type)
{
return type & MEM_ALLOC;
}
#endif /* _LINUX_BPF_H */ #endif /* _LINUX_BPF_H */
...@@ -6034,6 +6034,11 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -6034,6 +6034,11 @@ int btf_struct_access(struct bpf_verifier_log *log,
switch (err) { switch (err) {
case WALK_PTR: case WALK_PTR:
/* For local types, the destination register cannot
* become a pointer again.
*/
if (type_is_alloc(reg->type))
return SCALAR_VALUE;
/* If we found the pointer or scalar on t+off, /* If we found the pointer or scalar on t+off,
* we're done. * we're done.
*/ */
......
...@@ -4687,14 +4687,27 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, ...@@ -4687,14 +4687,27 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
return -EACCES; return -EACCES;
} }
if (env->ops->btf_struct_access) { if (env->ops->btf_struct_access && !type_is_alloc(reg->type)) {
if (!btf_is_kernel(reg->btf)) {
verbose(env, "verifier internal error: reg->btf must be kernel btf\n");
return -EFAULT;
}
ret = env->ops->btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); ret = env->ops->btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag);
} else { } else {
if (atype != BPF_READ) { /* Writes are permitted with default btf_struct_access for
* program allocated objects (which always have ref_obj_id > 0),
* but not for untrusted PTR_TO_BTF_ID | MEM_ALLOC.
*/
if (atype != BPF_READ && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
verbose(env, "only read is supported\n"); verbose(env, "only read is supported\n");
return -EACCES; return -EACCES;
} }
if (type_is_alloc(reg->type) && !reg->ref_obj_id) {
verbose(env, "verifier internal error: ref_obj_id for allocated object must be non-zero\n");
return -EFAULT;
}
ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag);
} }
...@@ -5973,6 +5986,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env, ...@@ -5973,6 +5986,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env,
* fixed offset. * fixed offset.
*/ */
case PTR_TO_BTF_ID: case PTR_TO_BTF_ID:
case PTR_TO_BTF_ID | MEM_ALLOC:
/* When referenced PTR_TO_BTF_ID is passed to release function, /* When referenced PTR_TO_BTF_ID is passed to release function,
* it's fixed offset must be 0. In the other cases, fixed offset * it's fixed offset must be 0. In the other cases, fixed offset
* can be non-zero. * can be non-zero.
...@@ -13690,6 +13704,13 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) ...@@ -13690,6 +13704,13 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
break; break;
case PTR_TO_BTF_ID: case PTR_TO_BTF_ID:
case PTR_TO_BTF_ID | PTR_UNTRUSTED: case PTR_TO_BTF_ID | PTR_UNTRUSTED:
/* PTR_TO_BTF_ID | MEM_ALLOC always has a valid lifetime, unlike
* PTR_TO_BTF_ID, and an active ref_obj_id, but the same cannot
* be said once it is marked PTR_UNTRUSTED, hence we must handle
* any faults for loads into such types. BPF_WRITE is disallowed
* for this case.
*/
case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED:
if (type == BPF_READ) { if (type == BPF_READ) {
insn->code = BPF_LDX | BPF_PROBE_MEM | insn->code = BPF_LDX | BPF_PROBE_MEM |
BPF_SIZE((insn)->code); BPF_SIZE((insn)->code);
......
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