Commit 50fc9786 authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'bpf: add __user tagging support in vmlinux BTF'

Yonghong Song says:

====================

The __user attribute is currently mainly used by sparse for type checking.
The attribute indicates whether a memory access is in user memory address
space or not. Such information is important during tracing kernel
internal functions or data structures as accessing user memory often
has different mechanisms compared to accessing kernel memory. For example,
the perf-probe needs explicit command line specification to indicate a
particular argument or string in user-space memory ([1], [2], [3]).
Currently, vmlinux BTF is available in kernel with many distributions.
If __user attribute information is available in vmlinux BTF, the explicit
user memory access information from users will not be necessary as
the kernel can figure it out by itself with vmlinux BTF.

Besides the above possible use for perf/probe, another use case is
for bpf verifier. Currently, for bpf BPF_PROG_TYPE_TRACING type of bpf
programs, users can write direct code like
  p->m1->m2
and "p" could be a function parameter. Without __user information in BTF,
the verifier will assume p->m1 accessing kernel memory and will generate
normal loads. Let us say "p" actually tagged with __user in the source
code.  In such cases, p->m1 is actually accessing user memory and direct
load is not right and may produce incorrect result. For such cases,
bpf_probe_read_user() will be the correct way to read p->m1.

To support encoding __user information in BTF, a new attribute
  __attribute__((btf_type_tag("<arbitrary_string>")))
is implemented in clang ([4]). For example, if we have
  #define __user __attribute__((btf_type_tag("user")))
during kernel compilation, the attribute "user" information will
be preserved in dwarf. After pahole converting dwarf to BTF, __user
information will be available in vmlinux BTF and such information
can be used by bpf verifier, perf/probe or other use cases.

Currently btf_type_tag is only supported in clang (>= clang14) and
pahole (>= 1.23). gcc support is also proposed and under development ([5]).

In the rest of patch set, Patch 1 added support of __user btf_type_tag
during compilation. Patch 2 added bpf verifier support to utilize __user
tag information to reject bpf programs not using proper helper to access
user memories. Patches 3-5 are for bpf selftests which demonstrate verifier
can reject direct user memory accesses.

  [1] http://lkml.kernel.org/r/155789874562.26965.10836126971405890891.stgit@devnote2
  [2] http://lkml.kernel.org/r/155789872187.26965.4468456816590888687.stgit@devnote2
  [3] http://lkml.kernel.org/r/155789871009.26965.14167558859557329331.stgit@devnote2
  [4] https://reviews.llvm.org/D111199
  [5] https://lore.kernel.org/bpf/0cbeb2fb-1a18-f690-e360-24b1c90c2a91@fb.com/



Changelog:
  v2 -> v3:
    - remove FLAG_DONTCARE enumerator and just use 0 as dontcare flag.
    - explain how btf type_tag is encoded in btf type chain.
  v1 -> v2:
    - use MEM_USER flag for PTR_TO_BTF_ID reg type instead of a separate
      field to encode __user tag.
    - add a test with kernel function __sys_getsockname which has __user tagged
      argument.
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 46531a30 b7290384
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -503,6 +503,19 @@ valid index (starting from 0) pointing to a member or an argument.
 * ``info.vlen``: 0
 * ``type``: the type with ``btf_type_tag`` attribute

Currently, ``BTF_KIND_TYPE_TAG`` is only emitted for pointer types.
It has the following btf type chain:
::

  ptr -> [type_tag]*
      -> [const | volatile | restrict | typedef]*
      -> base_type

Basically, a pointer type points to zero or more
type_tag, then zero or more const/volatile/restrict/typedef
and finally the base type. The base type is one of
int, ptr, array, struct, union, enum, func_proto and float types.

3. BTF Kernel API
=================

+6 −3
Original line number Diff line number Diff line
@@ -332,7 +332,10 @@ enum bpf_type_flag {
	 */
	MEM_ALLOC		= BIT(2 + BPF_BASE_TYPE_BITS),

	__BPF_TYPE_LAST_FLAG	= MEM_ALLOC,
	/* MEM is in user address space. */
	MEM_USER		= BIT(3 + BPF_BASE_TYPE_BITS),

	__BPF_TYPE_LAST_FLAG	= MEM_USER,
};

/* Max number of base types. */
@@ -588,7 +591,7 @@ struct bpf_verifier_ops {
				 const struct btf *btf,
				 const struct btf_type *t, int off, int size,
				 enum bpf_access_type atype,
				 u32 *next_btf_id);
				 u32 *next_btf_id, enum bpf_type_flag *flag);
};

struct bpf_prog_offload_ops {
@@ -1780,7 +1783,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size,
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
		      const struct btf_type *t, int off, int size,
		      enum bpf_access_type atype,
		      u32 *next_btf_id);
		      u32 *next_btf_id, enum bpf_type_flag *flag);
bool btf_struct_ids_match(struct bpf_verifier_log *log,
			  const struct btf *btf, u32 id, int off,
			  const struct btf *need_btf, u32 need_type_id);
+5 −0
Original line number Diff line number Diff line
@@ -238,6 +238,11 @@ static inline bool btf_type_is_var(const struct btf_type *t)
	return BTF_INFO_KIND(t->info) == BTF_KIND_VAR;
}

static inline bool btf_type_is_type_tag(const struct btf_type *t)
{
	return BTF_INFO_KIND(t->info) == BTF_KIND_TYPE_TAG;
}

/* union is only a special case of struct:
 * all its offsetof(member) == 0
 */
+3 −0
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
# define __kernel
# ifdef STRUCTLEAK_PLUGIN
#  define __user	__attribute__((user))
# elif defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
	__has_attribute(btf_type_tag)
#  define __user	__attribute__((btf_type_tag("user")))
# else
#  define __user
# endif
+28 −6
Original line number Diff line number Diff line
@@ -4886,6 +4886,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
	const char *tname = prog->aux->attach_func_name;
	struct bpf_verifier_log *log = info->log;
	const struct btf_param *args;
	const char *tag_value;
	u32 nr_args, arg;
	int i, ret;

@@ -5038,6 +5039,13 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
	info->btf = btf;
	info->btf_id = t->type;
	t = btf_type_by_id(btf, t->type);

	if (btf_type_is_type_tag(t)) {
		tag_value = __btf_name_by_offset(btf, t->name_off);
		if (strcmp(tag_value, "user") == 0)
			info->reg_type |= MEM_USER;
	}

	/* skip modifiers */
	while (btf_type_is_modifier(t)) {
		info->btf_id = t->type;
@@ -5064,12 +5072,12 @@ enum bpf_struct_walk_result {

static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
			   const struct btf_type *t, int off, int size,
			   u32 *next_btf_id)
			   u32 *next_btf_id, enum bpf_type_flag *flag)
{
	u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
	const struct btf_type *mtype, *elem_type = NULL;
	const struct btf_member *member;
	const char *tname, *mname;
	const char *tname, *mname, *tag_value;
	u32 vlen, elem_id, mid;

again:
@@ -5253,7 +5261,8 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
		}

		if (btf_type_is_ptr(mtype)) {
			const struct btf_type *stype;
			const struct btf_type *stype, *t;
			enum bpf_type_flag tmp_flag = 0;
			u32 id;

			if (msize != size || off != moff) {
@@ -5262,9 +5271,19 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
					mname, moff, tname, off, size);
				return -EACCES;
			}

			/* check __user tag */
			t = btf_type_by_id(btf, mtype->type);
			if (btf_type_is_type_tag(t)) {
				tag_value = __btf_name_by_offset(btf, t->name_off);
				if (strcmp(tag_value, "user") == 0)
					tmp_flag = MEM_USER;
			}

			stype = btf_type_skip_modifiers(btf, mtype->type, &id);
			if (btf_type_is_struct(stype)) {
				*next_btf_id = id;
				*flag = tmp_flag;
				return WALK_PTR;
			}
		}
@@ -5291,13 +5310,14 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
		      const struct btf_type *t, int off, int size,
		      enum bpf_access_type atype __maybe_unused,
		      u32 *next_btf_id)
		      u32 *next_btf_id, enum bpf_type_flag *flag)
{
	enum bpf_type_flag tmp_flag = 0;
	int err;
	u32 id;

	do {
		err = btf_struct_walk(log, btf, t, off, size, &id);
		err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag);

		switch (err) {
		case WALK_PTR:
@@ -5305,6 +5325,7 @@ int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
			 * we're done.
			 */
			*next_btf_id = id;
			*flag = tmp_flag;
			return PTR_TO_BTF_ID;
		case WALK_SCALAR:
			return SCALAR_VALUE;
@@ -5349,6 +5370,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
			  const struct btf *need_btf, u32 need_type_id)
{
	const struct btf_type *type;
	enum bpf_type_flag flag;
	int err;

	/* Are we already done? */
@@ -5359,7 +5381,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
	type = btf_type_by_id(btf, id);
	if (!type)
		return false;
	err = btf_struct_walk(log, btf, type, off, 1, &id);
	err = btf_struct_walk(log, btf, type, off, 1, &id, &flag);
	if (err != WALK_STRUCT)
		return false;

Loading