Commit 37c7b1ca authored by Daniel Borkmann's avatar Daniel Borkmann
Browse files

Merge branch 'bpf-btf-type-fixes'

Yonghong Song says:

====================
Commit 69b693f0 ("bpf: btf: Introduce BPF Type Format (BTF)")
introduced BTF, a debug info format for BTF.

The original design has a couple of issues though.
First, the bitfield size is only encoded in int type.
If the struct member bitfield type is enum, pahole ([1])
or llvm is forced to replace enum with int type. As a result, the original
type information gets lost.

Second, the original BTF design does not envision the possibility of
BTF=>header_file conversion ([2]), hence does not encode "struct" or
"union" info for a forward type. Such information is necessary to
convert BTF to a header file.

This patch set fixed the issue by introducing kind_flag, using one bit
in type->info. When kind_flag, the struct/union btf_member->offset
will encode both bitfield_size and bit_offset, covering both
int and enum base types. The kind_flag is also used to indicate whether
the forward type is a union (when set) or a struct.

Patch #1 refactors function btf_int_bits_seq_show() so Patch #2
can reuse part of the function.
Patch #2 implemented kind_flag support for struct/union/fwd types.
Patch #3 added kind_flag support for cgroup local storage map pretty print.
Patch #4 syncs kernel uapi btf.h to tools directory.
Patch #5 added unit tests for kind_flag.
Patch #6 added tests for kernel bpffs based pretty print with kind_flag.
Patch #7 refactors function btf_dumper_int_bits() so Patch #8
can reuse part of the function.
Patch #8 added bpftool support of pretty print with kind_flag set.

  [1] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?id=b18354f64cc215368c3bc0df4a7e5341c55c378c
  [2] https://lwn.net/SubscriberLink/773198/fe3074838f5c3f26/



Change logs:
  v2 -> v3:
    . Relocated comments about bitfield_size/bit_offset interpretation
      of the "offset" field right before the "offset" struct member.
    . Added missing byte alignment checking for non-bitfield enum
      member of a struct with kind_flag set.
    . Added two test cases in unit tests for struct type, kind_flag set,
      non-bitfield int/enum member, not-byte aligned bit offsets.
    . Added comments to help understand there is no overflow for
      total_bits_offset in bpftool function btf_dumper_int_bits().
    . Added explanation of typedef type dumping fix in Patch #8 commit
      message.

  v1 -> v2:
    . If kind_flag is set for a structure, ensure an int member,
      whether it is a bitfield or not, is a regular int type.
    . Added support so cgroup local storage map pretty print
      works with kind_flag.
====================

Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents 6c4fc209 8772c8bc
Loading
Loading
Loading
Loading
+4 −1
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/types.h>


struct btf;
struct btf;
struct btf_member;
struct btf_type;
struct btf_type;
union bpf_attr;
union bpf_attr;


@@ -46,7 +47,9 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
		       struct seq_file *m);
		       struct seq_file *m);
int btf_get_fd_by_id(u32 id);
int btf_get_fd_by_id(u32 id);
u32 btf_id(const struct btf *btf);
u32 btf_id(const struct btf *btf);
bool btf_type_is_reg_int(const struct btf_type *t, u32 expected_size);
bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
			   const struct btf_member *m,
			   u32 expected_offset, u32 expected_size);


#ifdef CONFIG_BPF_SYSCALL
#ifdef CONFIG_BPF_SYSCALL
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
+18 −2
Original line number Original line Diff line number Diff line
@@ -34,7 +34,9 @@ struct btf_type {
	 * bits  0-15: vlen (e.g. # of struct's members)
	 * bits  0-15: vlen (e.g. # of struct's members)
	 * bits 16-23: unused
	 * bits 16-23: unused
	 * bits 24-27: kind (e.g. int, ptr, array...etc)
	 * bits 24-27: kind (e.g. int, ptr, array...etc)
	 * bits 28-31: unused
	 * bits 28-30: unused
	 * bit     31: kind_flag, currently used by
	 *             struct, union and fwd
	 */
	 */
	__u32 info;
	__u32 info;
	/* "size" is used by INT, ENUM, STRUCT and UNION.
	/* "size" is used by INT, ENUM, STRUCT and UNION.
@@ -52,6 +54,7 @@ struct btf_type {


#define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
#define BTF_INFO_KIND(info)	(((info) >> 24) & 0x0f)
#define BTF_INFO_VLEN(info)	((info) & 0xffff)
#define BTF_INFO_VLEN(info)	((info) & 0xffff)
#define BTF_INFO_KFLAG(info)	((info) >> 31)


#define BTF_KIND_UNKN		0	/* Unknown	*/
#define BTF_KIND_UNKN		0	/* Unknown	*/
#define BTF_KIND_INT		1	/* Integer	*/
#define BTF_KIND_INT		1	/* Integer	*/
@@ -110,9 +113,22 @@ struct btf_array {
struct btf_member {
struct btf_member {
	__u32	name_off;
	__u32	name_off;
	__u32	type;
	__u32	type;
	__u32	offset;	/* offset in bits */
	/* If the type info kind_flag is set, the btf_member offset
	 * contains both member bitfield size and bit offset. The
	 * bitfield size is set for bitfield members. If the type
	 * info kind_flag is not set, the offset contains only bit
	 * offset.
	 */
	__u32	offset;
};
};


/* If the struct/union type info kind_flag is set, the
 * following two macros are used to access bitfield_size
 * and bit_offset from btf_member.offset.
 */
#define BTF_MEMBER_BITFIELD_SIZE(val)	((val) >> 24)
#define BTF_MEMBER_BIT_OFFSET(val)	((val) & 0xffffff)

/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
 * The exact number of btf_param is stored in the vlen (of the
 * The exact number of btf_param is stored in the vlen (of the
 * info in "struct btf_type").
 * info in "struct btf_type").
+309 −43
Original line number Original line Diff line number Diff line
@@ -164,7 +164,7 @@
#define BITS_ROUNDUP_BYTES(bits) \
#define BITS_ROUNDUP_BYTES(bits) \
	(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
	(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))


#define BTF_INFO_MASK 0x0f00ffff
#define BTF_INFO_MASK 0x8f00ffff
#define BTF_INT_MASK 0x0fffffff
#define BTF_INT_MASK 0x0fffffff
#define BTF_TYPE_ID_VALID(type_id) ((type_id) <= BTF_MAX_TYPE)
#define BTF_TYPE_ID_VALID(type_id) ((type_id) <= BTF_MAX_TYPE)
#define BTF_STR_OFFSET_VALID(name_off) ((name_off) <= BTF_MAX_NAME_OFFSET)
#define BTF_STR_OFFSET_VALID(name_off) ((name_off) <= BTF_MAX_NAME_OFFSET)
@@ -274,6 +274,10 @@ struct btf_kind_operations {
			    const struct btf_type *struct_type,
			    const struct btf_type *struct_type,
			    const struct btf_member *member,
			    const struct btf_member *member,
			    const struct btf_type *member_type);
			    const struct btf_type *member_type);
	int (*check_kflag_member)(struct btf_verifier_env *env,
				  const struct btf_type *struct_type,
				  const struct btf_member *member,
				  const struct btf_type *member_type);
	void (*log_details)(struct btf_verifier_env *env,
	void (*log_details)(struct btf_verifier_env *env,
			    const struct btf_type *t);
			    const struct btf_type *t);
	void (*seq_show)(const struct btf *btf, const struct btf_type *t,
	void (*seq_show)(const struct btf *btf, const struct btf_type *t,
@@ -419,6 +423,25 @@ static u16 btf_type_vlen(const struct btf_type *t)
	return BTF_INFO_VLEN(t->info);
	return BTF_INFO_VLEN(t->info);
}
}


static bool btf_type_kflag(const struct btf_type *t)
{
	return BTF_INFO_KFLAG(t->info);
}

static u32 btf_member_bit_offset(const struct btf_type *struct_type,
			     const struct btf_member *member)
{
	return btf_type_kflag(struct_type) ? BTF_MEMBER_BIT_OFFSET(member->offset)
					   : member->offset;
}

static u32 btf_member_bitfield_size(const struct btf_type *struct_type,
				    const struct btf_member *member)
{
	return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset)
					   : 0;
}

static u32 btf_type_int(const struct btf_type *t)
static u32 btf_type_int(const struct btf_type *t)
{
{
	return *(u32 *)(t + 1);
	return *(u32 *)(t + 1);
@@ -523,22 +546,41 @@ static bool btf_type_int_is_regular(const struct btf_type *t)
}
}


/*
/*
 * Check that given type is a regular int and has the expected size.
 * Check that given struct member is a regular int with expected
 * offset and size.
 */
 */
bool btf_type_is_reg_int(const struct btf_type *t, u32 expected_size)
bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
			   const struct btf_member *m,
			   u32 expected_offset, u32 expected_size)
{
{
	u8 nr_bits, nr_bytes;
	const struct btf_type *t;
	u32 int_data;
	u32 id, int_data;
	u8 nr_bits;


	if (!btf_type_is_int(t))
	id = m->type;
	t = btf_type_id_size(btf, &id, NULL);
	if (!t || !btf_type_is_int(t))
		return false;
		return false;


	int_data = btf_type_int(t);
	int_data = btf_type_int(t);
	nr_bits = BTF_INT_BITS(int_data);
	nr_bits = BTF_INT_BITS(int_data);
	nr_bytes = BITS_ROUNDUP_BYTES(nr_bits);
	if (btf_type_kflag(s)) {
	if (BITS_PER_BYTE_MASKED(nr_bits) ||
		u32 bitfield_size = BTF_MEMBER_BITFIELD_SIZE(m->offset);
	    BTF_INT_OFFSET(int_data) ||
		u32 bit_offset = BTF_MEMBER_BIT_OFFSET(m->offset);
	    nr_bytes != expected_size)

		/* if kflag set, int should be a regular int and
		 * bit offset should be at byte boundary.
		 */
		return !bitfield_size &&
		       BITS_ROUNDUP_BYTES(bit_offset) == expected_offset &&
		       BITS_ROUNDUP_BYTES(nr_bits) == expected_size;
	}

	if (BTF_INT_OFFSET(int_data) ||
	    BITS_PER_BYTE_MASKED(m->offset) ||
	    BITS_ROUNDUP_BYTES(m->offset) != expected_offset ||
	    BITS_PER_BYTE_MASKED(nr_bits) ||
	    BITS_ROUNDUP_BYTES(nr_bits) != expected_size)
		return false;
		return false;


	return true;
	return true;
@@ -627,6 +669,14 @@ static void btf_verifier_log_member(struct btf_verifier_env *env,
	if (env->phase != CHECK_META)
	if (env->phase != CHECK_META)
		btf_verifier_log_type(env, struct_type, NULL);
		btf_verifier_log_type(env, struct_type, NULL);


	if (btf_type_kflag(struct_type))
		__btf_verifier_log(log,
				   "\t%s type_id=%u bitfield_size=%u bits_offset=%u",
				   __btf_name_by_offset(btf, member->name_off),
				   member->type,
				   BTF_MEMBER_BITFIELD_SIZE(member->offset),
				   BTF_MEMBER_BIT_OFFSET(member->offset));
	else
		__btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
		__btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
				   __btf_name_by_offset(btf, member->name_off),
				   __btf_name_by_offset(btf, member->name_off),
				   member->type, member->offset);
				   member->type, member->offset);
@@ -945,6 +995,38 @@ static int btf_df_check_member(struct btf_verifier_env *env,
	return -EINVAL;
	return -EINVAL;
}
}


static int btf_df_check_kflag_member(struct btf_verifier_env *env,
				     const struct btf_type *struct_type,
				     const struct btf_member *member,
				     const struct btf_type *member_type)
{
	btf_verifier_log_basic(env, struct_type,
			       "Unsupported check_kflag_member");
	return -EINVAL;
}

/* Used for ptr, array and struct/union type members.
 * int, enum and modifier types have their specific callback functions.
 */
static int btf_generic_check_kflag_member(struct btf_verifier_env *env,
					  const struct btf_type *struct_type,
					  const struct btf_member *member,
					  const struct btf_type *member_type)
{
	if (BTF_MEMBER_BITFIELD_SIZE(member->offset)) {
		btf_verifier_log_member(env, struct_type, member,
					"Invalid member bitfield_size");
		return -EINVAL;
	}

	/* bitfield size is 0, so member->offset represents bit offset only.
	 * It is safe to call non kflag check_member variants.
	 */
	return btf_type_ops(member_type)->check_member(env, struct_type,
						       member,
						       member_type);
}

static int btf_df_resolve(struct btf_verifier_env *env,
static int btf_df_resolve(struct btf_verifier_env *env,
			  const struct resolve_vertex *v)
			  const struct resolve_vertex *v)
{
{
@@ -997,6 +1079,62 @@ static int btf_int_check_member(struct btf_verifier_env *env,
	return 0;
	return 0;
}
}


static int btf_int_check_kflag_member(struct btf_verifier_env *env,
				      const struct btf_type *struct_type,
				      const struct btf_member *member,
				      const struct btf_type *member_type)
{
	u32 struct_bits_off, nr_bits, nr_int_data_bits, bytes_offset;
	u32 int_data = btf_type_int(member_type);
	u32 struct_size = struct_type->size;
	u32 nr_copy_bits;

	/* a regular int type is required for the kflag int member */
	if (!btf_type_int_is_regular(member_type)) {
		btf_verifier_log_member(env, struct_type, member,
					"Invalid member base type");
		return -EINVAL;
	}

	/* check sanity of bitfield size */
	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
	nr_int_data_bits = BTF_INT_BITS(int_data);
	if (!nr_bits) {
		/* Not a bitfield member, member offset must be at byte
		 * boundary.
		 */
		if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
			btf_verifier_log_member(env, struct_type, member,
						"Invalid member offset");
			return -EINVAL;
		}

		nr_bits = nr_int_data_bits;
	} else if (nr_bits > nr_int_data_bits) {
		btf_verifier_log_member(env, struct_type, member,
					"Invalid member bitfield_size");
		return -EINVAL;
	}

	bytes_offset = BITS_ROUNDDOWN_BYTES(struct_bits_off);
	nr_copy_bits = nr_bits + BITS_PER_BYTE_MASKED(struct_bits_off);
	if (nr_copy_bits > BITS_PER_U64) {
		btf_verifier_log_member(env, struct_type, member,
					"nr_copy_bits exceeds 64");
		return -EINVAL;
	}

	if (struct_size < bytes_offset ||
	    struct_size - bytes_offset < BITS_ROUNDUP_BYTES(nr_copy_bits)) {
		btf_verifier_log_member(env, struct_type, member,
					"Member exceeds struct_size");
		return -EINVAL;
	}

	return 0;
}

static s32 btf_int_check_meta(struct btf_verifier_env *env,
static s32 btf_int_check_meta(struct btf_verifier_env *env,
			      const struct btf_type *t,
			      const struct btf_type *t,
			      u32 meta_left)
			      u32 meta_left)
@@ -1016,6 +1154,11 @@ static s32 btf_int_check_meta(struct btf_verifier_env *env,
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (btf_type_kflag(t)) {
		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
		return -EINVAL;
	}

	int_data = btf_type_int(t);
	int_data = btf_type_int(t);
	if (int_data & ~BTF_INT_MASK) {
	if (int_data & ~BTF_INT_MASK) {
		btf_verifier_log_basic(env, t, "Invalid int_data:%x",
		btf_verifier_log_basic(env, t, "Invalid int_data:%x",
@@ -1068,26 +1211,16 @@ static void btf_int_log(struct btf_verifier_env *env,
			 btf_int_encoding_str(BTF_INT_ENCODING(int_data)));
			 btf_int_encoding_str(BTF_INT_ENCODING(int_data)));
}
}


static void btf_int_bits_seq_show(const struct btf *btf,
static void btf_bitfield_seq_show(void *data, u8 bits_offset,
				  const struct btf_type *t,
				  u8 nr_bits, struct seq_file *m)
				  void *data, u8 bits_offset,
				  struct seq_file *m)
{
{
	u16 left_shift_bits, right_shift_bits;
	u16 left_shift_bits, right_shift_bits;
	u32 int_data = btf_type_int(t);
	u8 nr_bits = BTF_INT_BITS(int_data);
	u8 total_bits_offset;
	u8 nr_copy_bytes;
	u8 nr_copy_bytes;
	u8 nr_copy_bits;
	u8 nr_copy_bits;
	u64 print_num;
	u64 print_num;


	/*
	data += BITS_ROUNDDOWN_BYTES(bits_offset);
	 * bits_offset is at most 7.
	bits_offset = BITS_PER_BYTE_MASKED(bits_offset);
	 * BTF_INT_OFFSET() cannot exceed 64 bits.
	 */
	total_bits_offset = bits_offset + BTF_INT_OFFSET(int_data);
	data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
	bits_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
	nr_copy_bits = nr_bits + bits_offset;
	nr_copy_bits = nr_bits + bits_offset;
	nr_copy_bytes = BITS_ROUNDUP_BYTES(nr_copy_bits);
	nr_copy_bytes = BITS_ROUNDUP_BYTES(nr_copy_bits);


@@ -1107,6 +1240,24 @@ static void btf_int_bits_seq_show(const struct btf *btf,
	seq_printf(m, "0x%llx", print_num);
	seq_printf(m, "0x%llx", print_num);
}
}



static void btf_int_bits_seq_show(const struct btf *btf,
				  const struct btf_type *t,
				  void *data, u8 bits_offset,
				  struct seq_file *m)
{
	u32 int_data = btf_type_int(t);
	u8 nr_bits = BTF_INT_BITS(int_data);
	u8 total_bits_offset;

	/*
	 * bits_offset is at most 7.
	 * BTF_INT_OFFSET() cannot exceed 64 bits.
	 */
	total_bits_offset = bits_offset + BTF_INT_OFFSET(int_data);
	btf_bitfield_seq_show(data, total_bits_offset, nr_bits, m);
}

static void btf_int_seq_show(const struct btf *btf, const struct btf_type *t,
static void btf_int_seq_show(const struct btf *btf, const struct btf_type *t,
			     u32 type_id, void *data, u8 bits_offset,
			     u32 type_id, void *data, u8 bits_offset,
			     struct seq_file *m)
			     struct seq_file *m)
@@ -1156,6 +1307,7 @@ static const struct btf_kind_operations int_ops = {
	.check_meta = btf_int_check_meta,
	.check_meta = btf_int_check_meta,
	.resolve = btf_df_resolve,
	.resolve = btf_df_resolve,
	.check_member = btf_int_check_member,
	.check_member = btf_int_check_member,
	.check_kflag_member = btf_int_check_kflag_member,
	.log_details = btf_int_log,
	.log_details = btf_int_log,
	.seq_show = btf_int_seq_show,
	.seq_show = btf_int_seq_show,
};
};
@@ -1185,6 +1337,31 @@ static int btf_modifier_check_member(struct btf_verifier_env *env,
							 resolved_type);
							 resolved_type);
}
}


static int btf_modifier_check_kflag_member(struct btf_verifier_env *env,
					   const struct btf_type *struct_type,
					   const struct btf_member *member,
					   const struct btf_type *member_type)
{
	const struct btf_type *resolved_type;
	u32 resolved_type_id = member->type;
	struct btf_member resolved_member;
	struct btf *btf = env->btf;

	resolved_type = btf_type_id_size(btf, &resolved_type_id, NULL);
	if (!resolved_type) {
		btf_verifier_log_member(env, struct_type, member,
					"Invalid member");
		return -EINVAL;
	}

	resolved_member = *member;
	resolved_member.type = resolved_type_id;

	return btf_type_ops(resolved_type)->check_kflag_member(env, struct_type,
							       &resolved_member,
							       resolved_type);
}

static int btf_ptr_check_member(struct btf_verifier_env *env,
static int btf_ptr_check_member(struct btf_verifier_env *env,
				const struct btf_type *struct_type,
				const struct btf_type *struct_type,
				const struct btf_member *member,
				const struct btf_member *member,
@@ -1220,6 +1397,11 @@ static int btf_ref_type_check_meta(struct btf_verifier_env *env,
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (btf_type_kflag(t)) {
		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
		return -EINVAL;
	}

	if (!BTF_TYPE_ID_VALID(t->type)) {
	if (!BTF_TYPE_ID_VALID(t->type)) {
		btf_verifier_log_type(env, t, "Invalid type_id");
		btf_verifier_log_type(env, t, "Invalid type_id");
		return -EINVAL;
		return -EINVAL;
@@ -1373,6 +1555,7 @@ static struct btf_kind_operations modifier_ops = {
	.check_meta = btf_ref_type_check_meta,
	.check_meta = btf_ref_type_check_meta,
	.resolve = btf_modifier_resolve,
	.resolve = btf_modifier_resolve,
	.check_member = btf_modifier_check_member,
	.check_member = btf_modifier_check_member,
	.check_kflag_member = btf_modifier_check_kflag_member,
	.log_details = btf_ref_type_log,
	.log_details = btf_ref_type_log,
	.seq_show = btf_modifier_seq_show,
	.seq_show = btf_modifier_seq_show,
};
};
@@ -1381,6 +1564,7 @@ static struct btf_kind_operations ptr_ops = {
	.check_meta = btf_ref_type_check_meta,
	.check_meta = btf_ref_type_check_meta,
	.resolve = btf_ptr_resolve,
	.resolve = btf_ptr_resolve,
	.check_member = btf_ptr_check_member,
	.check_member = btf_ptr_check_member,
	.check_kflag_member = btf_generic_check_kflag_member,
	.log_details = btf_ref_type_log,
	.log_details = btf_ref_type_log,
	.seq_show = btf_ptr_seq_show,
	.seq_show = btf_ptr_seq_show,
};
};
@@ -1415,6 +1599,7 @@ static struct btf_kind_operations fwd_ops = {
	.check_meta = btf_fwd_check_meta,
	.check_meta = btf_fwd_check_meta,
	.resolve = btf_df_resolve,
	.resolve = btf_df_resolve,
	.check_member = btf_df_check_member,
	.check_member = btf_df_check_member,
	.check_kflag_member = btf_df_check_kflag_member,
	.log_details = btf_ref_type_log,
	.log_details = btf_ref_type_log,
	.seq_show = btf_df_seq_show,
	.seq_show = btf_df_seq_show,
};
};
@@ -1473,6 +1658,11 @@ static s32 btf_array_check_meta(struct btf_verifier_env *env,
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (btf_type_kflag(t)) {
		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
		return -EINVAL;
	}

	if (t->size) {
	if (t->size) {
		btf_verifier_log_type(env, t, "size != 0");
		btf_verifier_log_type(env, t, "size != 0");
		return -EINVAL;
		return -EINVAL;
@@ -1596,6 +1786,7 @@ static struct btf_kind_operations array_ops = {
	.check_meta = btf_array_check_meta,
	.check_meta = btf_array_check_meta,
	.resolve = btf_array_resolve,
	.resolve = btf_array_resolve,
	.check_member = btf_array_check_member,
	.check_member = btf_array_check_member,
	.check_kflag_member = btf_generic_check_kflag_member,
	.log_details = btf_array_log,
	.log_details = btf_array_log,
	.seq_show = btf_array_seq_show,
	.seq_show = btf_array_seq_show,
};
};
@@ -1634,6 +1825,7 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
	u32 meta_needed, last_offset;
	u32 meta_needed, last_offset;
	struct btf *btf = env->btf;
	struct btf *btf = env->btf;
	u32 struct_size = t->size;
	u32 struct_size = t->size;
	u32 offset;
	u16 i;
	u16 i;


	meta_needed = btf_type_vlen(t) * sizeof(*member);
	meta_needed = btf_type_vlen(t) * sizeof(*member);
@@ -1675,7 +1867,8 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
			return -EINVAL;
			return -EINVAL;
		}
		}


		if (is_union && member->offset) {
		offset = btf_member_bit_offset(t, member);
		if (is_union && offset) {
			btf_verifier_log_member(env, t, member,
			btf_verifier_log_member(env, t, member,
						"Invalid member bits_offset");
						"Invalid member bits_offset");
			return -EINVAL;
			return -EINVAL;
@@ -1685,20 +1878,20 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
		 * ">" instead of ">=" because the last member could be
		 * ">" instead of ">=" because the last member could be
		 * "char a[0];"
		 * "char a[0];"
		 */
		 */
		if (last_offset > member->offset) {
		if (last_offset > offset) {
			btf_verifier_log_member(env, t, member,
			btf_verifier_log_member(env, t, member,
						"Invalid member bits_offset");
						"Invalid member bits_offset");
			return -EINVAL;
			return -EINVAL;
		}
		}


		if (BITS_ROUNDUP_BYTES(member->offset) > struct_size) {
		if (BITS_ROUNDUP_BYTES(offset) > struct_size) {
			btf_verifier_log_member(env, t, member,
			btf_verifier_log_member(env, t, member,
						"Member bits_offset exceeds its struct size");
						"Member bits_offset exceeds its struct size");
			return -EINVAL;
			return -EINVAL;
		}
		}


		btf_verifier_log_member(env, t, member, NULL);
		btf_verifier_log_member(env, t, member, NULL);
		last_offset = member->offset;
		last_offset = offset;
	}
	}


	return meta_needed;
	return meta_needed;
@@ -1728,6 +1921,11 @@ static int btf_struct_resolve(struct btf_verifier_env *env,


		last_member_type = btf_type_by_id(env->btf,
		last_member_type = btf_type_by_id(env->btf,
						  last_member_type_id);
						  last_member_type_id);
		if (btf_type_kflag(v->t))
			err = btf_type_ops(last_member_type)->check_kflag_member(env, v->t,
								last_member,
								last_member_type);
		else
			err = btf_type_ops(last_member_type)->check_member(env, v->t,
			err = btf_type_ops(last_member_type)->check_member(env, v->t,
								last_member,
								last_member,
								last_member_type);
								last_member_type);
@@ -1752,6 +1950,11 @@ static int btf_struct_resolve(struct btf_verifier_env *env,
			return env_stack_push(env, member_type, member_type_id);
			return env_stack_push(env, member_type, member_type_id);
		}
		}


		if (btf_type_kflag(v->t))
			err = btf_type_ops(member_type)->check_kflag_member(env, v->t,
									    member,
									    member_type);
		else
			err = btf_type_ops(member_type)->check_member(env, v->t,
			err = btf_type_ops(member_type)->check_member(env, v->t,
								      member,
								      member,
								      member_type);
								      member_type);
@@ -1782,18 +1985,27 @@ static void btf_struct_seq_show(const struct btf *btf, const struct btf_type *t,
	for_each_member(i, t, member) {
	for_each_member(i, t, member) {
		const struct btf_type *member_type = btf_type_by_id(btf,
		const struct btf_type *member_type = btf_type_by_id(btf,
								member->type);
								member->type);
		u32 member_offset = member->offset;
		u32 bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset);
		u8 bits8_offset = BITS_PER_BYTE_MASKED(member_offset);
		const struct btf_kind_operations *ops;
		const struct btf_kind_operations *ops;
		u32 member_offset, bitfield_size;
		u32 bytes_offset;
		u8 bits8_offset;


		if (i)
		if (i)
			seq_puts(m, seq);
			seq_puts(m, seq);


		member_offset = btf_member_bit_offset(t, member);
		bitfield_size = btf_member_bitfield_size(t, member);
		if (bitfield_size) {
			btf_bitfield_seq_show(data, member_offset,
					      bitfield_size, m);
		} else {
			bytes_offset = BITS_ROUNDDOWN_BYTES(member_offset);
			bits8_offset = BITS_PER_BYTE_MASKED(member_offset);
			ops = btf_type_ops(member_type);
			ops = btf_type_ops(member_type);
			ops->seq_show(btf, member_type, member->type,
			ops->seq_show(btf, member_type, member->type,
				      data + bytes_offset, bits8_offset, m);
				      data + bytes_offset, bits8_offset, m);
		}
		}
	}
	seq_puts(m, "}");
	seq_puts(m, "}");
}
}


@@ -1801,6 +2013,7 @@ static struct btf_kind_operations struct_ops = {
	.check_meta = btf_struct_check_meta,
	.check_meta = btf_struct_check_meta,
	.resolve = btf_struct_resolve,
	.resolve = btf_struct_resolve,
	.check_member = btf_struct_check_member,
	.check_member = btf_struct_check_member,
	.check_kflag_member = btf_generic_check_kflag_member,
	.log_details = btf_struct_log,
	.log_details = btf_struct_log,
	.seq_show = btf_struct_seq_show,
	.seq_show = btf_struct_seq_show,
};
};
@@ -1830,6 +2043,41 @@ static int btf_enum_check_member(struct btf_verifier_env *env,
	return 0;
	return 0;
}
}


static int btf_enum_check_kflag_member(struct btf_verifier_env *env,
				       const struct btf_type *struct_type,
				       const struct btf_member *member,
				       const struct btf_type *member_type)
{
	u32 struct_bits_off, nr_bits, bytes_end, struct_size;
	u32 int_bitsize = sizeof(int) * BITS_PER_BYTE;

	struct_bits_off = BTF_MEMBER_BIT_OFFSET(member->offset);
	nr_bits = BTF_MEMBER_BITFIELD_SIZE(member->offset);
	if (!nr_bits) {
		if (BITS_PER_BYTE_MASKED(struct_bits_off)) {
			btf_verifier_log_member(env, struct_type, member,
						"Member is not byte aligned");
				return -EINVAL;
		}

		nr_bits = int_bitsize;
	} else if (nr_bits > int_bitsize) {
		btf_verifier_log_member(env, struct_type, member,
					"Invalid member bitfield_size");
		return -EINVAL;
	}

	struct_size = struct_type->size;
	bytes_end = BITS_ROUNDUP_BYTES(struct_bits_off + nr_bits);
	if (struct_size < bytes_end) {
		btf_verifier_log_member(env, struct_type, member,
					"Member exceeds struct_size");
		return -EINVAL;
	}

	return 0;
}

static s32 btf_enum_check_meta(struct btf_verifier_env *env,
static s32 btf_enum_check_meta(struct btf_verifier_env *env,
			       const struct btf_type *t,
			       const struct btf_type *t,
			       u32 meta_left)
			       u32 meta_left)
@@ -1849,6 +2097,11 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (btf_type_kflag(t)) {
		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
		return -EINVAL;
	}

	if (t->size != sizeof(int)) {
	if (t->size != sizeof(int)) {
		btf_verifier_log_type(env, t, "Expected size:%zu",
		btf_verifier_log_type(env, t, "Expected size:%zu",
				      sizeof(int));
				      sizeof(int));
@@ -1917,6 +2170,7 @@ static struct btf_kind_operations enum_ops = {
	.check_meta = btf_enum_check_meta,
	.check_meta = btf_enum_check_meta,
	.resolve = btf_df_resolve,
	.resolve = btf_df_resolve,
	.check_member = btf_enum_check_member,
	.check_member = btf_enum_check_member,
	.check_kflag_member = btf_enum_check_kflag_member,
	.log_details = btf_enum_log,
	.log_details = btf_enum_log,
	.seq_show = btf_enum_seq_show,
	.seq_show = btf_enum_seq_show,
};
};
@@ -1939,6 +2193,11 @@ static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (btf_type_kflag(t)) {
		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
		return -EINVAL;
	}

	btf_verifier_log_type(env, t, NULL);
	btf_verifier_log_type(env, t, NULL);


	return meta_needed;
	return meta_needed;
@@ -1998,6 +2257,7 @@ static struct btf_kind_operations func_proto_ops = {
	 * Hence, there is no btf_func_check_member().
	 * Hence, there is no btf_func_check_member().
	 */
	 */
	.check_member = btf_df_check_member,
	.check_member = btf_df_check_member,
	.check_kflag_member = btf_df_check_kflag_member,
	.log_details = btf_func_proto_log,
	.log_details = btf_func_proto_log,
	.seq_show = btf_df_seq_show,
	.seq_show = btf_df_seq_show,
};
};
@@ -2017,6 +2277,11 @@ static s32 btf_func_check_meta(struct btf_verifier_env *env,
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (btf_type_kflag(t)) {
		btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
		return -EINVAL;
	}

	btf_verifier_log_type(env, t, NULL);
	btf_verifier_log_type(env, t, NULL);


	return 0;
	return 0;
@@ -2026,6 +2291,7 @@ static struct btf_kind_operations func_ops = {
	.check_meta = btf_func_check_meta,
	.check_meta = btf_func_check_meta,
	.resolve = btf_df_resolve,
	.resolve = btf_df_resolve,
	.check_member = btf_df_check_member,
	.check_member = btf_df_check_member,
	.check_kflag_member = btf_df_check_kflag_member,
	.log_details = btf_ref_type_log,
	.log_details = btf_ref_type_log,
	.seq_show = btf_df_seq_show,
	.seq_show = btf_df_seq_show,
};
};
+4 −13
Original line number Original line Diff line number Diff line
@@ -315,9 +315,8 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,
				    const struct btf_type *key_type,
				    const struct btf_type *key_type,
				    const struct btf_type *value_type)
				    const struct btf_type *value_type)
{
{
	const struct btf_type *t;
	struct btf_member *m;
	struct btf_member *m;
	u32 id, size;
	u32 offset, size;


	/* Key is expected to be of struct bpf_cgroup_storage_key type,
	/* Key is expected to be of struct bpf_cgroup_storage_key type,
	 * which is:
	 * which is:
@@ -338,25 +337,17 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,
	 * The first field must be a 64 bit integer at 0 offset.
	 * The first field must be a 64 bit integer at 0 offset.
	 */
	 */
	m = (struct btf_member *)(key_type + 1);
	m = (struct btf_member *)(key_type + 1);
	if (m->offset)
		return -EINVAL;
	id = m->type;
	t = btf_type_id_size(btf, &id, NULL);
	size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id);
	size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id);
	if (!t || !btf_type_is_reg_int(t, size))
	if (!btf_member_is_reg_int(btf, key_type, m, 0, size))
		return -EINVAL;
		return -EINVAL;


	/*
	/*
	 * The second field must be a 32 bit integer at 64 bit offset.
	 * The second field must be a 32 bit integer at 64 bit offset.
	 */
	 */
	m++;
	m++;
	if (m->offset != offsetof(struct bpf_cgroup_storage_key, attach_type) *
	offset = offsetof(struct bpf_cgroup_storage_key, attach_type);
	    BITS_PER_BYTE)
		return -EINVAL;
	id = m->type;
	t = btf_type_id_size(btf, &id, NULL);
	size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type);
	size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type);
	if (!t || !btf_type_is_reg_int(t, size))
	if (!btf_member_is_reg_int(btf, key_type, m, offset, size))
		return -EINVAL;
		return -EINVAL;


	return 0;
	return 0;
+46 −15
Original line number Original line Diff line number Diff line
@@ -73,20 +73,17 @@ static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
	return ret;
	return ret;
}
}


static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
static void btf_dumper_bitfield(__u32 nr_bits, __u8 bit_offset,
				const void *data, json_writer_t *jw,
				const void *data, json_writer_t *jw,
				bool is_plain_text)
				bool is_plain_text)
{
{
	int left_shift_bits, right_shift_bits;
	int left_shift_bits, right_shift_bits;
	int nr_bits = BTF_INT_BITS(int_type);
	int total_bits_offset;
	int bytes_to_copy;
	int bytes_to_copy;
	int bits_to_copy;
	int bits_to_copy;
	__u64 print_num;
	__u64 print_num;


	total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
	data += BITS_ROUNDDOWN_BYTES(bit_offset);
	data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
	bit_offset = BITS_PER_BYTE_MASKED(bit_offset);
	bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
	bits_to_copy = bit_offset + nr_bits;
	bits_to_copy = bit_offset + nr_bits;
	bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
	bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);


@@ -109,6 +106,22 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
		jsonw_printf(jw, "%llu", print_num);
		jsonw_printf(jw, "%llu", print_num);
}
}



static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
				const void *data, json_writer_t *jw,
				bool is_plain_text)
{
	int nr_bits = BTF_INT_BITS(int_type);
	int total_bits_offset;

	/* bits_offset is at most 7.
	 * BTF_INT_OFFSET() cannot exceed 64 bits.
	 */
	total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
	btf_dumper_bitfield(nr_bits, total_bits_offset, data, jw,
			    is_plain_text);
}

static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
			  const void *data, json_writer_t *jw,
			  const void *data, json_writer_t *jw,
			  bool is_plain_text)
			  bool is_plain_text)
@@ -180,6 +193,7 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
	const struct btf_type *t;
	const struct btf_type *t;
	struct btf_member *m;
	struct btf_member *m;
	const void *data_off;
	const void *data_off;
	int kind_flag;
	int ret = 0;
	int ret = 0;
	int i, vlen;
	int i, vlen;


@@ -187,19 +201,33 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
	if (!t)
	if (!t)
		return -EINVAL;
		return -EINVAL;


	kind_flag = BTF_INFO_KFLAG(t->info);
	vlen = BTF_INFO_VLEN(t->info);
	vlen = BTF_INFO_VLEN(t->info);
	jsonw_start_object(d->jw);
	jsonw_start_object(d->jw);
	m = (struct btf_member *)(t + 1);
	m = (struct btf_member *)(t + 1);


	for (i = 0; i < vlen; i++) {
	for (i = 0; i < vlen; i++) {
		data_off = data + BITS_ROUNDDOWN_BYTES(m[i].offset);
		__u32 bit_offset = m[i].offset;
		__u32 bitfield_size = 0;

		if (kind_flag) {
			bitfield_size = BTF_MEMBER_BITFIELD_SIZE(bit_offset);
			bit_offset = BTF_MEMBER_BIT_OFFSET(bit_offset);
		}

		jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
		jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
		if (bitfield_size) {
			btf_dumper_bitfield(bitfield_size, bit_offset,
					    data, d->jw, d->is_plain_text);
		} else {
			data_off = data + BITS_ROUNDDOWN_BYTES(bit_offset);
			ret = btf_dumper_do_type(d, m[i].type,
			ret = btf_dumper_do_type(d, m[i].type,
					 BITS_PER_BYTE_MASKED(m[i].offset),
						 BITS_PER_BYTE_MASKED(bit_offset),
						 data_off);
						 data_off);
			if (ret)
			if (ret)
				break;
				break;
		}
		}
	}


	jsonw_end_object(d->jw);
	jsonw_end_object(d->jw);


@@ -285,6 +313,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,


	switch (BTF_INFO_KIND(t->info)) {
	switch (BTF_INFO_KIND(t->info)) {
	case BTF_KIND_INT:
	case BTF_KIND_INT:
	case BTF_KIND_TYPEDEF:
		BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off));
		BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off));
		break;
		break;
	case BTF_KIND_STRUCT:
	case BTF_KIND_STRUCT:
@@ -308,10 +337,11 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
		BTF_PRINT_TYPE(t->type);
		BTF_PRINT_TYPE(t->type);
		BTF_PRINT_ARG("* ");
		BTF_PRINT_ARG("* ");
		break;
		break;
	case BTF_KIND_UNKN:
	case BTF_KIND_FWD:
	case BTF_KIND_FWD:
	case BTF_KIND_TYPEDEF:
		BTF_PRINT_ARG("%s %s ",
		return -1;
			      BTF_INFO_KFLAG(t->info) ? "union" : "struct",
			      btf__name_by_offset(btf, t->name_off));
		break;
	case BTF_KIND_VOLATILE:
	case BTF_KIND_VOLATILE:
		BTF_PRINT_ARG("volatile ");
		BTF_PRINT_ARG("volatile ");
		BTF_PRINT_TYPE(t->type);
		BTF_PRINT_TYPE(t->type);
@@ -335,6 +365,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
		if (pos == -1)
		if (pos == -1)
			return -1;
			return -1;
		break;
		break;
	case BTF_KIND_UNKN:
	default:
	default:
		return -1;
		return -1;
	}
	}
Loading