Commit 3f00c523 authored by David Vernet's avatar David Vernet Committed by Alexei Starovoitov
Browse files

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: default avatarDavid Vernet <void@manifault.com>
Link: https://lore.kernel.org/r/20221120051004.3605026-3-void@manifault.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent ef66c547
Loading
Loading
Loading
Loading
+14 −16
Original line number Diff line number Diff line
@@ -161,22 +161,20 @@ KF_ACQUIRE and KF_RET_NULL flags.
--------------------------

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,
and pointers to kernel objects are always passed to helpers in their unmodified
form (as obtained from acquire kfuncs).

It can be used to enforce that a pointer to a refcounted object acquired from a
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,
but those can have a non-zero offset.

This flag is often used for kfuncs that operate (change some property, perform
some operation) on an object that was obtained using an acquire kfunc. Such
kfuncs need an unchanged pointer to ensure the integrity of the operation being
performed on the expected object.
indicates that the all pointer arguments are valid, and that all pointers to
BTF objects have been passed in their unmodified form (that is, at a zero
offset, and without having been obtained from walking another pointer).

There are two types of pointers to kernel objects which are considered "valid":

1. Pointers which are passed as tracepoint or struct_ops callback arguments.
2. Pointers which were returned from a KF_ACQUIRE or KF_KPTR_GET kfunc.

Pointers to non-BTF objects (e.g. scalar pointers) may also be passed to
KF_TRUSTED_ARGS kfuncs, and may have a non-zero offset.

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
-----------------------
+30 −0
Original line number Diff line number Diff line
@@ -543,6 +543,35 @@ enum bpf_type_flag {
	 */
	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_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_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_TRUSTED	= PTR_TRUSTED	 | RET_PTR_TO_BTF_ID,

	/* 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.
+7 −0
Original line number Diff line number Diff line
@@ -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 */
+41 −24
Original line number Diff line number Diff line
@@ -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 */
+8 −0
Original line number Diff line number Diff line
@@ -5799,6 +5799,11 @@ static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto,
	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,
		    const struct bpf_prog *prog,
		    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;
	if (prog_type_args_trusted(prog->type))
		info->reg_type |= PTR_TRUSTED;

	if (tgt_prog) {
		enum bpf_prog_type tgt_type;

Loading