mirror of
https://github.com/lkl/linux.git
synced 2025-12-19 16:13:19 +09:00
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:
committed by
Alexei Starovoitov
parent
ef66c5475d
commit
3f00c52393
@@ -19,36 +19,53 @@
|
||||
#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_KPTR_GET (1 << 3) /* kfunc returns reference to a kptr */
|
||||
/* Trusted arguments are those which are meant to be referenced arguments with
|
||||
* unchanged offset. It is used to enforce that pointers obtained from acquire
|
||||
* kfuncs remain unmodified when being passed to helpers taking trusted args.
|
||||
/* Trusted arguments are those which are guaranteed to be valid when passed to
|
||||
* the kfunc. It is used to enforce that pointers obtained from either acquire
|
||||
* 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
|
||||
* struct foo {
|
||||
* int data;
|
||||
* struct foo *next;
|
||||
* };
|
||||
* Consider, for example, the following new task tracepoint:
|
||||
*
|
||||
* struct bar {
|
||||
* int data;
|
||||
* struct foo f;
|
||||
* };
|
||||
* SEC("tp_btf/task_newtask")
|
||||
* int BPF_PROG(new_task_tp, struct task_struct *task, u64 clone_flags)
|
||||
* {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* struct foo *f = alloc_foo(); // Acquire kfunc
|
||||
* struct bar *b = alloc_bar(); // Acquire kfunc
|
||||
* And the following kfunc:
|
||||
*
|
||||
* If a kfunc set_foo_data() wants to operate only on the allocated object, it
|
||||
* will set the KF_TRUSTED_ARGS flag, which will prevent unsafe usage like:
|
||||
* BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_TRUSTED_ARGS)
|
||||
*
|
||||
* set_foo_data(f, 42); // Allowed
|
||||
* 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
|
||||
* All invocations to the kfunc must pass the unmodified, unwalked task:
|
||||
*
|
||||
* In the final case, usually for the purposes of type matching, it is deduced
|
||||
* by looking at the type of the member at the offset, but due to the
|
||||
* requirement of trusted argument, this deduction will be strict and not done
|
||||
* for this case.
|
||||
* bpf_task_acquire(task); // Allowed
|
||||
* bpf_task_acquire(task->last_wakee); // Rejected, walked task
|
||||
*
|
||||
* 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_SLEEPABLE (1 << 5) /* kfunc may sleep */
|
||||
|
||||
Reference in New Issue
Block a user