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:
David Vernet
2022-11-19 23:10:02 -06:00
committed by Alexei Starovoitov
parent ef66c5475d
commit 3f00c52393
10 changed files with 161 additions and 54 deletions

View File

@@ -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 */