bpf: Allow trusted pointers to be passed to KF_TRUSTED_ARGS kfuncs
Kfuncs currently support specifying the KF_TRUSTED_ARGS flag to signal to the verifier that it should enforce that a BPF program passes it a "safe", trusted pointer. Currently, "safe" means that the pointer is either PTR_TO_CTX, or is refcounted. There may be cases, however, where the kernel passes a BPF program a safe / trusted pointer to an object that the BPF program wishes to use as a kptr, but because the object does not yet have a ref_obj_id from the perspective of the verifier, the program would be unable to pass it to a KF_ACQUIRE | KF_TRUSTED_ARGS kfunc. The solution is to expand the set of pointers that are considered trusted according to KF_TRUSTED_ARGS, so that programs can invoke kfuncs with these pointers without getting rejected by the verifier. There is already a PTR_UNTRUSTED flag that is set in some scenarios, such as when a BPF program reads a kptr directly from a map without performing a bpf_kptr_xchg() call. These pointers of course can and should be rejected by the verifier. Unfortunately, however, PTR_UNTRUSTED does not cover all the cases for safety that need to be addressed to adequately protect kfuncs. Specifically, pointers obtained by a BPF program "walking" a struct are _not_ considered PTR_UNTRUSTED according to BPF. For example, say that we were to add a kfunc called bpf_task_acquire(), with KF_ACQUIRE | KF_TRUSTED_ARGS, to acquire a struct task_struct *. If we only used PTR_UNTRUSTED to signal that a task was unsafe to pass to a kfunc, the verifier would mistakenly allow the following unsafe BPF program to be loaded: SEC("tp_btf/task_newtask") int BPF_PROG(unsafe_acquire_task, struct task_struct *task, u64 clone_flags) { struct task_struct *acquired, *nested; nested = task->last_wakee; /* Would not be rejected by the verifier. */ acquired = bpf_task_acquire(nested); if (!acquired) return 0; bpf_task_release(acquired); return 0; } To address this, this patch defines a new type flag called PTR_TRUSTED which tracks whether a PTR_TO_BTF_ID pointer is safe to pass to a KF_TRUSTED_ARGS kfunc or a BPF helper function. PTR_TRUSTED pointers are passed directly from the kernel as a tracepoint or struct_ops callback argument. Any nested pointer that is obtained from walking a PTR_TRUSTED pointer is no longer PTR_TRUSTED. From the example above, the struct task_struct *task argument is PTR_TRUSTED, but the 'nested' pointer obtained from 'task->last_wakee' is not PTR_TRUSTED. A subsequent patch will add kfuncs for storing a task kfunc as a kptr, and then another patch will add selftests to validate. Signed-off-by: David Vernet <void@manifault.com> Link: https://lore.kernel.org/r/20221120051004.3605026-3-void@manifault.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
ef66c5475d
commit
3f00c52393
@ -161,22 +161,20 @@ KF_ACQUIRE and KF_RET_NULL flags.
|
|||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
The KF_TRUSTED_ARGS flag is used for kfuncs taking pointer arguments. It
|
The KF_TRUSTED_ARGS flag is used for kfuncs taking pointer arguments. It
|
||||||
indicates that the all pointer arguments will always have a guaranteed lifetime,
|
indicates that the all pointer arguments are valid, and that all pointers to
|
||||||
and pointers to kernel objects are always passed to helpers in their unmodified
|
BTF objects have been passed in their unmodified form (that is, at a zero
|
||||||
form (as obtained from acquire kfuncs).
|
offset, and without having been obtained from walking another pointer).
|
||||||
|
|
||||||
It can be used to enforce that a pointer to a refcounted object acquired from a
|
There are two types of pointers to kernel objects which are considered "valid":
|
||||||
kfunc or BPF helper is passed as an argument to this kfunc without any
|
|
||||||
modifications (e.g. pointer arithmetic) such that it is trusted and points to
|
|
||||||
the original object.
|
|
||||||
|
|
||||||
Meanwhile, it is also allowed pass pointers to normal memory to such kfuncs,
|
1. Pointers which are passed as tracepoint or struct_ops callback arguments.
|
||||||
but those can have a non-zero offset.
|
2. Pointers which were returned from a KF_ACQUIRE or KF_KPTR_GET kfunc.
|
||||||
|
|
||||||
This flag is often used for kfuncs that operate (change some property, perform
|
Pointers to non-BTF objects (e.g. scalar pointers) may also be passed to
|
||||||
some operation) on an object that was obtained using an acquire kfunc. Such
|
KF_TRUSTED_ARGS kfuncs, and may have a non-zero offset.
|
||||||
kfuncs need an unchanged pointer to ensure the integrity of the operation being
|
|
||||||
performed on the expected object.
|
The definition of "valid" pointers is subject to change at any time, and has
|
||||||
|
absolutely no ABI stability guarantees.
|
||||||
|
|
||||||
2.4.6 KF_SLEEPABLE flag
|
2.4.6 KF_SLEEPABLE flag
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -543,6 +543,35 @@ enum bpf_type_flag {
|
|||||||
*/
|
*/
|
||||||
MEM_ALLOC = BIT(11 + BPF_BASE_TYPE_BITS),
|
MEM_ALLOC = BIT(11 + BPF_BASE_TYPE_BITS),
|
||||||
|
|
||||||
|
/* PTR was passed from the kernel in a trusted context, and may be
|
||||||
|
* passed to KF_TRUSTED_ARGS kfuncs or BPF helper functions.
|
||||||
|
* Confusingly, this is _not_ the opposite of PTR_UNTRUSTED above.
|
||||||
|
* PTR_UNTRUSTED refers to a kptr that was read directly from a map
|
||||||
|
* without invoking bpf_kptr_xchg(). What we really need to know is
|
||||||
|
* whether a pointer is safe to pass to a kfunc or BPF helper function.
|
||||||
|
* While PTR_UNTRUSTED pointers are unsafe to pass to kfuncs and BPF
|
||||||
|
* helpers, they do not cover all possible instances of unsafe
|
||||||
|
* pointers. For example, a pointer that was obtained from walking a
|
||||||
|
* struct will _not_ get the PTR_UNTRUSTED type modifier, despite the
|
||||||
|
* fact that it may be NULL, invalid, etc. This is due to backwards
|
||||||
|
* compatibility requirements, as this was the behavior that was first
|
||||||
|
* introduced when kptrs were added. The behavior is now considered
|
||||||
|
* deprecated, and PTR_UNTRUSTED will eventually be removed.
|
||||||
|
*
|
||||||
|
* PTR_TRUSTED, on the other hand, is a pointer that the kernel
|
||||||
|
* guarantees to be valid and safe to pass to kfuncs and BPF helpers.
|
||||||
|
* For example, pointers passed to tracepoint arguments are considered
|
||||||
|
* PTR_TRUSTED, as are pointers that are passed to struct_ops
|
||||||
|
* callbacks. As alluded to above, pointers that are obtained from
|
||||||
|
* walking PTR_TRUSTED pointers are _not_ trusted. For example, if a
|
||||||
|
* struct task_struct *task is PTR_TRUSTED, then accessing
|
||||||
|
* task->last_wakee will lose the PTR_TRUSTED modifier when it's stored
|
||||||
|
* in a BPF register. Similarly, pointers passed to certain programs
|
||||||
|
* types such as kretprobes are not guaranteed to be valid, as they may
|
||||||
|
* for example contain an object that was recently freed.
|
||||||
|
*/
|
||||||
|
PTR_TRUSTED = BIT(12 + 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,
|
||||||
};
|
};
|
||||||
@ -636,6 +665,7 @@ enum bpf_return_type {
|
|||||||
RET_PTR_TO_RINGBUF_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_RINGBUF | RET_PTR_TO_MEM,
|
RET_PTR_TO_RINGBUF_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_RINGBUF | RET_PTR_TO_MEM,
|
||||||
RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_MEM,
|
RET_PTR_TO_DYNPTR_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_MEM,
|
||||||
RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID,
|
RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID,
|
||||||
|
RET_PTR_TO_BTF_ID_TRUSTED = PTR_TRUSTED | RET_PTR_TO_BTF_ID,
|
||||||
|
|
||||||
/* This must be the last entry. Its purpose is to ensure the enum is
|
/* This must be the last entry. Its purpose is to ensure the enum is
|
||||||
* wide enough to hold the higher bits reserved for bpf_type_flag.
|
* wide enough to hold the higher bits reserved for bpf_type_flag.
|
||||||
|
@ -680,4 +680,11 @@ static inline bool bpf_prog_check_recur(const struct bpf_prog *prog)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BPF_REG_TRUSTED_MODIFIERS (MEM_ALLOC | PTR_TRUSTED)
|
||||||
|
|
||||||
|
static inline bool bpf_type_has_unsafe_modifiers(u32 type)
|
||||||
|
{
|
||||||
|
return type_flag(type) & ~BPF_REG_TRUSTED_MODIFIERS;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _LINUX_BPF_VERIFIER_H */
|
#endif /* _LINUX_BPF_VERIFIER_H */
|
||||||
|
@ -19,36 +19,53 @@
|
|||||||
#define KF_RELEASE (1 << 1) /* kfunc is a release function */
|
#define KF_RELEASE (1 << 1) /* kfunc is a release function */
|
||||||
#define KF_RET_NULL (1 << 2) /* kfunc returns a pointer that may be NULL */
|
#define KF_RET_NULL (1 << 2) /* kfunc returns a pointer that may be NULL */
|
||||||
#define KF_KPTR_GET (1 << 3) /* kfunc returns reference to a kptr */
|
#define KF_KPTR_GET (1 << 3) /* kfunc returns reference to a kptr */
|
||||||
/* Trusted arguments are those which are meant to be referenced arguments with
|
/* Trusted arguments are those which are guaranteed to be valid when passed to
|
||||||
* unchanged offset. It is used to enforce that pointers obtained from acquire
|
* the kfunc. It is used to enforce that pointers obtained from either acquire
|
||||||
* kfuncs remain unmodified when being passed to helpers taking trusted args.
|
* kfuncs, or from the main kernel on a tracepoint or struct_ops callback
|
||||||
|
* invocation, remain unmodified when being passed to helpers taking trusted
|
||||||
|
* args.
|
||||||
*
|
*
|
||||||
* Consider
|
* Consider, for example, the following new task tracepoint:
|
||||||
* struct foo {
|
|
||||||
* int data;
|
|
||||||
* struct foo *next;
|
|
||||||
* };
|
|
||||||
*
|
*
|
||||||
* struct bar {
|
* SEC("tp_btf/task_newtask")
|
||||||
* int data;
|
* int BPF_PROG(new_task_tp, struct task_struct *task, u64 clone_flags)
|
||||||
* struct foo f;
|
* {
|
||||||
* };
|
* ...
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
* struct foo *f = alloc_foo(); // Acquire kfunc
|
* And the following kfunc:
|
||||||
* struct bar *b = alloc_bar(); // Acquire kfunc
|
|
||||||
*
|
*
|
||||||
* If a kfunc set_foo_data() wants to operate only on the allocated object, it
|
* BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS)
|
||||||
* will set the KF_TRUSTED_ARGS flag, which will prevent unsafe usage like:
|
|
||||||
*
|
*
|
||||||
* set_foo_data(f, 42); // Allowed
|
* All invocations to the kfunc must pass the unmodified, unwalked task:
|
||||||
* set_foo_data(f->next, 42); // Rejected, non-referenced pointer
|
|
||||||
* set_foo_data(&f->next, 42);// Rejected, referenced, but wrong type
|
|
||||||
* set_foo_data(&b->f, 42); // Rejected, referenced, but bad offset
|
|
||||||
*
|
*
|
||||||
* In the final case, usually for the purposes of type matching, it is deduced
|
* bpf_task_acquire(task); // Allowed
|
||||||
* by looking at the type of the member at the offset, but due to the
|
* bpf_task_acquire(task->last_wakee); // Rejected, walked task
|
||||||
* requirement of trusted argument, this deduction will be strict and not done
|
*
|
||||||
* for this case.
|
* Programs may also pass referenced tasks directly to the kfunc:
|
||||||
|
*
|
||||||
|
* struct task_struct *acquired;
|
||||||
|
*
|
||||||
|
* acquired = bpf_task_acquire(task); // Allowed, same as above
|
||||||
|
* bpf_task_acquire(acquired); // Allowed
|
||||||
|
* bpf_task_acquire(task); // Allowed
|
||||||
|
* bpf_task_acquire(acquired->last_wakee); // Rejected, walked task
|
||||||
|
*
|
||||||
|
* Programs may _not_, however, pass a task from an arbitrary fentry/fexit, or
|
||||||
|
* kprobe/kretprobe to the kfunc, as BPF cannot guarantee that all of these
|
||||||
|
* pointers are guaranteed to be safe. For example, the following BPF program
|
||||||
|
* would be rejected:
|
||||||
|
*
|
||||||
|
* SEC("kretprobe/free_task")
|
||||||
|
* int BPF_PROG(free_task_probe, struct task_struct *tsk)
|
||||||
|
* {
|
||||||
|
* struct task_struct *acquired;
|
||||||
|
*
|
||||||
|
* acquired = bpf_task_acquire(acquired); // Rejected, not a trusted pointer
|
||||||
|
* bpf_task_release(acquired);
|
||||||
|
*
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
#define KF_TRUSTED_ARGS (1 << 4) /* kfunc only takes trusted pointer arguments */
|
#define KF_TRUSTED_ARGS (1 << 4) /* kfunc only takes trusted pointer arguments */
|
||||||
#define KF_SLEEPABLE (1 << 5) /* kfunc may sleep */
|
#define KF_SLEEPABLE (1 << 5) /* kfunc may sleep */
|
||||||
|
@ -5799,6 +5799,11 @@ static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto,
|
|||||||
return nr_args + 1;
|
return nr_args + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool prog_type_args_trusted(enum bpf_prog_type prog_type)
|
||||||
|
{
|
||||||
|
return prog_type == BPF_PROG_TYPE_TRACING || prog_type == BPF_PROG_TYPE_STRUCT_OPS;
|
||||||
|
}
|
||||||
|
|
||||||
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||||
const struct bpf_prog *prog,
|
const struct bpf_prog *prog,
|
||||||
struct bpf_insn_access_aux *info)
|
struct bpf_insn_access_aux *info)
|
||||||
@ -5942,6 +5947,9 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
info->reg_type = PTR_TO_BTF_ID;
|
info->reg_type = PTR_TO_BTF_ID;
|
||||||
|
if (prog_type_args_trusted(prog->type))
|
||||||
|
info->reg_type |= PTR_TRUSTED;
|
||||||
|
|
||||||
if (tgt_prog) {
|
if (tgt_prog) {
|
||||||
enum bpf_prog_type tgt_type;
|
enum bpf_prog_type tgt_type;
|
||||||
|
|
||||||
|
@ -589,12 +589,13 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
|
|||||||
strncpy(postfix, "_or_null", 16);
|
strncpy(postfix, "_or_null", 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(prefix, sizeof(prefix), "%s%s%s%s%s",
|
snprintf(prefix, sizeof(prefix), "%s%s%s%s%s%s",
|
||||||
type & MEM_RDONLY ? "rdonly_" : "",
|
type & MEM_RDONLY ? "rdonly_" : "",
|
||||||
type & MEM_RINGBUF ? "ringbuf_" : "",
|
type & MEM_RINGBUF ? "ringbuf_" : "",
|
||||||
type & MEM_USER ? "user_" : "",
|
type & MEM_USER ? "user_" : "",
|
||||||
type & MEM_PERCPU ? "percpu_" : "",
|
type & MEM_PERCPU ? "percpu_" : "",
|
||||||
type & PTR_UNTRUSTED ? "untrusted_" : ""
|
type & PTR_UNTRUSTED ? "untrusted_" : "",
|
||||||
|
type & PTR_TRUSTED ? "trusted_" : ""
|
||||||
);
|
);
|
||||||
|
|
||||||
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
|
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
|
||||||
@ -3856,7 +3857,7 @@ static int map_kptr_match_type(struct bpf_verifier_env *env,
|
|||||||
struct bpf_reg_state *reg, u32 regno)
|
struct bpf_reg_state *reg, u32 regno)
|
||||||
{
|
{
|
||||||
const char *targ_name = kernel_type_name(kptr_field->kptr.btf, kptr_field->kptr.btf_id);
|
const char *targ_name = kernel_type_name(kptr_field->kptr.btf, kptr_field->kptr.btf_id);
|
||||||
int perm_flags = PTR_MAYBE_NULL;
|
int perm_flags = PTR_MAYBE_NULL | PTR_TRUSTED;
|
||||||
const char *reg_name = "";
|
const char *reg_name = "";
|
||||||
|
|
||||||
/* Only unreferenced case accepts untrusted pointers */
|
/* Only unreferenced case accepts untrusted pointers */
|
||||||
@ -4732,6 +4733,9 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
|
|||||||
if (type_flag(reg->type) & PTR_UNTRUSTED)
|
if (type_flag(reg->type) & PTR_UNTRUSTED)
|
||||||
flag |= PTR_UNTRUSTED;
|
flag |= PTR_UNTRUSTED;
|
||||||
|
|
||||||
|
/* Any pointer obtained from walking a trusted pointer is no longer trusted. */
|
||||||
|
flag &= ~PTR_TRUSTED;
|
||||||
|
|
||||||
if (atype == BPF_READ && value_regno >= 0)
|
if (atype == BPF_READ && value_regno >= 0)
|
||||||
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
|
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
|
||||||
|
|
||||||
@ -5844,6 +5848,7 @@ static const struct bpf_reg_types btf_id_sock_common_types = {
|
|||||||
PTR_TO_TCP_SOCK,
|
PTR_TO_TCP_SOCK,
|
||||||
PTR_TO_XDP_SOCK,
|
PTR_TO_XDP_SOCK,
|
||||||
PTR_TO_BTF_ID,
|
PTR_TO_BTF_ID,
|
||||||
|
PTR_TO_BTF_ID | PTR_TRUSTED,
|
||||||
},
|
},
|
||||||
.btf_id = &btf_sock_ids[BTF_SOCK_TYPE_SOCK_COMMON],
|
.btf_id = &btf_sock_ids[BTF_SOCK_TYPE_SOCK_COMMON],
|
||||||
};
|
};
|
||||||
@ -5884,8 +5889,18 @@ static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } };
|
|||||||
static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } };
|
static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } };
|
||||||
static const struct bpf_reg_types ringbuf_mem_types = { .types = { PTR_TO_MEM | MEM_RINGBUF } };
|
static const struct bpf_reg_types ringbuf_mem_types = { .types = { PTR_TO_MEM | MEM_RINGBUF } };
|
||||||
static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } };
|
static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } };
|
||||||
static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } };
|
static const struct bpf_reg_types btf_ptr_types = {
|
||||||
static const struct bpf_reg_types percpu_btf_ptr_types = { .types = { PTR_TO_BTF_ID | MEM_PERCPU } };
|
.types = {
|
||||||
|
PTR_TO_BTF_ID,
|
||||||
|
PTR_TO_BTF_ID | PTR_TRUSTED,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static const struct bpf_reg_types percpu_btf_ptr_types = {
|
||||||
|
.types = {
|
||||||
|
PTR_TO_BTF_ID | MEM_PERCPU,
|
||||||
|
PTR_TO_BTF_ID | MEM_PERCPU | PTR_TRUSTED,
|
||||||
|
}
|
||||||
|
};
|
||||||
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
|
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
|
||||||
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
|
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
|
||||||
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
|
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
|
||||||
@ -5973,7 +5988,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
|
|||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
found:
|
found:
|
||||||
if (reg->type == PTR_TO_BTF_ID) {
|
if (reg->type == PTR_TO_BTF_ID || reg->type & PTR_TRUSTED) {
|
||||||
/* For bpf_sk_release, it needs to match against first member
|
/* For bpf_sk_release, it needs to match against first member
|
||||||
* 'struct sock_common', hence make an exception for it. This
|
* 'struct sock_common', hence make an exception for it. This
|
||||||
* allows bpf_sk_release to work for multiple socket types.
|
* allows bpf_sk_release to work for multiple socket types.
|
||||||
@ -6055,6 +6070,8 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
|||||||
*/
|
*/
|
||||||
case PTR_TO_BTF_ID:
|
case PTR_TO_BTF_ID:
|
||||||
case PTR_TO_BTF_ID | MEM_ALLOC:
|
case PTR_TO_BTF_ID | MEM_ALLOC:
|
||||||
|
case PTR_TO_BTF_ID | PTR_TRUSTED:
|
||||||
|
case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED:
|
||||||
/* 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.
|
||||||
@ -7939,6 +7956,25 @@ static bool is_kfunc_arg_kptr_get(struct bpf_kfunc_call_arg_meta *meta, int arg)
|
|||||||
return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET);
|
return arg == 0 && (meta->kfunc_flags & KF_KPTR_GET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_trusted_reg(const struct bpf_reg_state *reg)
|
||||||
|
{
|
||||||
|
/* A referenced register is always trusted. */
|
||||||
|
if (reg->ref_obj_id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* If a register is not referenced, it is trusted if it has either the
|
||||||
|
* MEM_ALLOC or PTR_TRUSTED type modifiers, and no others. Some of the
|
||||||
|
* other type modifiers may be safe, but we elect to take an opt-in
|
||||||
|
* approach here as some (e.g. PTR_UNTRUSTED and PTR_MAYBE_NULL) are
|
||||||
|
* not.
|
||||||
|
*
|
||||||
|
* Eventually, we should make PTR_TRUSTED the single source of truth
|
||||||
|
* for whether a register is trusted.
|
||||||
|
*/
|
||||||
|
return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS &&
|
||||||
|
!bpf_type_has_unsafe_modifiers(reg->type);
|
||||||
|
}
|
||||||
|
|
||||||
static bool __kfunc_param_match_suffix(const struct btf *btf,
|
static bool __kfunc_param_match_suffix(const struct btf *btf,
|
||||||
const struct btf_param *arg,
|
const struct btf_param *arg,
|
||||||
const char *suffix)
|
const char *suffix)
|
||||||
@ -8220,7 +8256,7 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
|
|||||||
const char *reg_ref_tname;
|
const char *reg_ref_tname;
|
||||||
u32 reg_ref_id;
|
u32 reg_ref_id;
|
||||||
|
|
||||||
if (reg->type == PTR_TO_BTF_ID) {
|
if (base_type(reg->type) == PTR_TO_BTF_ID) {
|
||||||
reg_btf = reg->btf;
|
reg_btf = reg->btf;
|
||||||
reg_ref_id = reg->btf_id;
|
reg_ref_id = reg->btf_id;
|
||||||
} else {
|
} else {
|
||||||
@ -8366,6 +8402,7 @@ static int check_reg_allocation_locked(struct bpf_verifier_env *env, struct bpf_
|
|||||||
ptr = reg->map_ptr;
|
ptr = reg->map_ptr;
|
||||||
break;
|
break;
|
||||||
case PTR_TO_BTF_ID | MEM_ALLOC:
|
case PTR_TO_BTF_ID | MEM_ALLOC:
|
||||||
|
case PTR_TO_BTF_ID | MEM_ALLOC | PTR_TRUSTED:
|
||||||
ptr = reg->btf;
|
ptr = reg->btf;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -8596,8 +8633,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
|||||||
case KF_ARG_PTR_TO_BTF_ID:
|
case KF_ARG_PTR_TO_BTF_ID:
|
||||||
if (!is_kfunc_trusted_args(meta))
|
if (!is_kfunc_trusted_args(meta))
|
||||||
break;
|
break;
|
||||||
if (!reg->ref_obj_id) {
|
|
||||||
verbose(env, "R%d must be referenced\n", regno);
|
if (!is_trusted_reg(reg)) {
|
||||||
|
verbose(env, "R%d must be referenced or trusted\n", regno);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
fallthrough;
|
fallthrough;
|
||||||
@ -8702,9 +8740,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
|||||||
break;
|
break;
|
||||||
case KF_ARG_PTR_TO_BTF_ID:
|
case KF_ARG_PTR_TO_BTF_ID:
|
||||||
/* Only base_type is checked, further checks are done here */
|
/* Only base_type is checked, further checks are done here */
|
||||||
if (reg->type != PTR_TO_BTF_ID &&
|
if ((base_type(reg->type) != PTR_TO_BTF_ID ||
|
||||||
(!reg2btf_ids[base_type(reg->type)] || type_flag(reg->type))) {
|
bpf_type_has_unsafe_modifiers(reg->type)) &&
|
||||||
verbose(env, "arg#%d expected pointer to btf or socket\n", i);
|
!reg2btf_ids[base_type(reg->type)]) {
|
||||||
|
verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type));
|
||||||
|
verbose(env, "expected %s or socket\n",
|
||||||
|
reg_type_str(env, base_type(reg->type) |
|
||||||
|
(type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
|
ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
|
||||||
@ -14713,6 +14755,7 @@ 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:
|
||||||
|
case PTR_TO_BTF_ID | PTR_TRUSTED:
|
||||||
/* PTR_TO_BTF_ID | MEM_ALLOC always has a valid lifetime, unlike
|
/* 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
|
* 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
|
* be said once it is marked PTR_UNTRUSTED, hence we must handle
|
||||||
@ -14720,6 +14763,8 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
|||||||
* for this case.
|
* for this case.
|
||||||
*/
|
*/
|
||||||
case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED:
|
case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED:
|
||||||
|
case PTR_TO_BTF_ID | PTR_UNTRUSTED | PTR_TRUSTED:
|
||||||
|
case PTR_TO_BTF_ID | PTR_UNTRUSTED | MEM_ALLOC | PTR_TRUSTED:
|
||||||
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);
|
||||||
|
@ -774,7 +774,7 @@ BPF_CALL_0(bpf_get_current_task_btf)
|
|||||||
const struct bpf_func_proto bpf_get_current_task_btf_proto = {
|
const struct bpf_func_proto bpf_get_current_task_btf_proto = {
|
||||||
.func = bpf_get_current_task_btf,
|
.func = bpf_get_current_task_btf,
|
||||||
.gpl_only = true,
|
.gpl_only = true,
|
||||||
.ret_type = RET_PTR_TO_BTF_ID,
|
.ret_type = RET_PTR_TO_BTF_ID_TRUSTED,
|
||||||
.ret_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
|
.ret_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +61,9 @@ static bool bpf_tcp_ca_is_valid_access(int off, int size,
|
|||||||
if (!bpf_tracing_btf_ctx_access(off, size, type, prog, info))
|
if (!bpf_tracing_btf_ctx_access(off, size, type, prog, info))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (info->reg_type == PTR_TO_BTF_ID && info->btf_id == sock_id)
|
if (base_type(info->reg_type) == PTR_TO_BTF_ID &&
|
||||||
|
!bpf_type_has_unsafe_modifiers(info->reg_type) &&
|
||||||
|
info->btf_id == sock_id)
|
||||||
/* promote it to tcp_sock */
|
/* promote it to tcp_sock */
|
||||||
info->btf_id = tcp_sock_id;
|
info->btf_id = tcp_sock_id;
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@
|
|||||||
},
|
},
|
||||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
.result = REJECT,
|
.result = REJECT,
|
||||||
.errstr = "arg#0 expected pointer to btf or socket",
|
.errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
|
||||||
.fixup_kfunc_btf_id = {
|
.fixup_kfunc_btf_id = {
|
||||||
{ "bpf_kfunc_call_test_acquire", 3 },
|
{ "bpf_kfunc_call_test_acquire", 3 },
|
||||||
{ "bpf_kfunc_call_test_release", 5 },
|
{ "bpf_kfunc_call_test_release", 5 },
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
.kfunc = "bpf",
|
.kfunc = "bpf",
|
||||||
.expected_attach_type = BPF_LSM_MAC,
|
.expected_attach_type = BPF_LSM_MAC,
|
||||||
.flags = BPF_F_SLEEPABLE,
|
.flags = BPF_F_SLEEPABLE,
|
||||||
.errstr = "arg#0 expected pointer to btf or socket",
|
.errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
|
||||||
.fixup_kfunc_btf_id = {
|
.fixup_kfunc_btf_id = {
|
||||||
{ "bpf_lookup_user_key", 2 },
|
{ "bpf_lookup_user_key", 2 },
|
||||||
{ "bpf_key_put", 4 },
|
{ "bpf_key_put", 4 },
|
||||||
@ -163,7 +163,7 @@
|
|||||||
.kfunc = "bpf",
|
.kfunc = "bpf",
|
||||||
.expected_attach_type = BPF_LSM_MAC,
|
.expected_attach_type = BPF_LSM_MAC,
|
||||||
.flags = BPF_F_SLEEPABLE,
|
.flags = BPF_F_SLEEPABLE,
|
||||||
.errstr = "arg#0 expected pointer to btf or socket",
|
.errstr = "arg#0 is ptr_or_null_ expected ptr_ or socket",
|
||||||
.fixup_kfunc_btf_id = {
|
.fixup_kfunc_btf_id = {
|
||||||
{ "bpf_lookup_system_key", 1 },
|
{ "bpf_lookup_system_key", 1 },
|
||||||
{ "bpf_key_put", 3 },
|
{ "bpf_key_put", 3 },
|
||||||
|
Loading…
Reference in New Issue
Block a user