Commit 401af75c authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'Fixes for bad PTR_TO_BTF_ID offset'

Kumar Kartikeya Dwivedi says:

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

This set fixes a bug related to bad var_off being permitted for kfunc call in
case of PTR_TO_BTF_ID, consolidates offset checks for all register types allowed
as helper or kfunc arguments into a common shared helper, and introduces a
couple of other checks to harden the kfunc release logic and prevent future
bugs. Some selftests are also included that fail in absence of these fixes,
serving as demonstration of the issues being fixed.

Changelog:
----------
v3 -> v4:
v3: https://lore.kernel.org/bpf/20220304000508.2904128-1-memxor@gmail.com

 * Update commit message for __diag patch to say clang instead of LLVM (Nathan)
 * Address nits for check_func_arg_reg_off (Martin)
 * Add comment for fixed_off_ok case, remove is_kfunc check (Martin)

v2 -> v3:
v2: https://lore.kernel.org/bpf/20220303045029.2645297-1-memxor@gmail.com

 * Add my SoB to __diag for clang patch (Nathan)

v1 -> v2:
v1: https://lore.kernel.org/bpf/20220301065745.1634848-1-memxor@gmail.com

 * Put reg->off check for release kfunc inside check_func_arg_reg_off,
   make the check a bit more readable
 * Squash verifier selftests errstr update into patch 3 for bisect (Alexei)
 * Include fix from Nathan for clang warning about missing prototypes
 * Add unified __diag_ingore_all that works for both GCC/LLVM (Alexei)

Older discussion:
Link: https://lore.kernel.org/bpf/20220219113744.1852259-1-memxor@gmail.com



Kumar Kartikeya Dwivedi (7):
  bpf: Add check_func_arg_reg_off function
  bpf: Fix PTR_TO_BTF_ID var_off check
  bpf: Disallow negative offset in check_ptr_off_reg
  bpf: Harden register offset checks for release helpers and kfuncs
  compiler_types.h: Add unified __diag_ignore_all for GCC/LLVM
  bpf: Replace __diag_ignore with unified __diag_ignore_all
  selftests/bpf: Add tests for kfunc register offset checks
====================

Acked-by: default avatarMartin KaFai Lau <kafai@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents caec5495 8218ccb5
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -521,6 +521,10 @@ bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt);

int check_ptr_off_reg(struct bpf_verifier_env *env,
		      const struct bpf_reg_state *reg, int regno);
int check_func_arg_reg_off(struct bpf_verifier_env *env,
			   const struct bpf_reg_state *reg, int regno,
			   enum bpf_arg_type arg_type,
			   bool is_release_func);
int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
			     u32 regno);
int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+25 −0
Original line number Diff line number Diff line
@@ -68,3 +68,28 @@

#define __nocfi		__attribute__((__no_sanitize__("cfi")))
#define __cficanonical	__attribute__((__cfi_canonical_jump_table__))

/*
 * Turn individual warnings and errors on and off locally, depending
 * on version.
 */
#define __diag_clang(version, severity, s) \
	__diag_clang_ ## version(__diag_clang_ ## severity s)

/* Severity used in pragma directives */
#define __diag_clang_ignore	ignored
#define __diag_clang_warn	warning
#define __diag_clang_error	error

#define __diag_str1(s)		#s
#define __diag_str(s)		__diag_str1(s)
#define __diag(s)		_Pragma(__diag_str(clang diagnostic s))

#if CONFIG_CLANG_VERSION >= 110000
#define __diag_clang_11(s)	__diag(s)
#else
#define __diag_clang_11(s)
#endif

#define __diag_ignore_all(option, comment) \
	__diag_clang(11, ignore, option)
+3 −0
Original line number Diff line number Diff line
@@ -151,6 +151,9 @@
#define __diag_GCC_8(s)
#endif

#define __diag_ignore_all(option, comment) \
	__diag_GCC(8, ignore, option)

/*
 * Prior to 9.1, -Wno-alloc-size-larger-than (and therefore the "alloc_size"
 * attribute) do not work, and must be disabled.
+4 −0
Original line number Diff line number Diff line
@@ -371,4 +371,8 @@ struct ftrace_likely_data {
#define __diag_error(compiler, version, option, comment) \
	__diag_ ## compiler(version, error, option)

#ifndef __diag_ignore_all
#define __diag_ignore_all(option, comment)
#endif

#endif /* __LINUX_COMPILER_TYPES_H */
+24 −16
Original line number Diff line number Diff line
@@ -5726,7 +5726,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
	const char *func_name, *ref_tname;
	const struct btf_type *t, *ref_t;
	const struct btf_param *args;
	int ref_regno = 0;
	int ref_regno = 0, ret;
	bool rel = false;

	t = btf_type_by_id(btf, func_id);
@@ -5753,6 +5753,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
		return -EINVAL;
	}

	/* Only kfunc can be release func */
	if (is_kfunc)
		rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog),
						BTF_KFUNC_TYPE_RELEASE, func_id);
	/* check that BTF function arguments match actual types that the
	 * verifier sees.
	 */
@@ -5776,6 +5780,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,

		ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
		ref_tname = btf_name_by_offset(btf, ref_t->name_off);

		ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE, rel);
		if (ret < 0)
			return ret;

		if (btf_get_prog_ctx_type(log, btf, t,
					  env->prog->type, i)) {
			/* If function expects ctx type in BTF check that caller
@@ -5787,8 +5796,6 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
					i, btf_type_str(t));
				return -EINVAL;
			}
			if (check_ptr_off_reg(env, reg, regno))
				return -EINVAL;
		} else if (is_kfunc && (reg->type == PTR_TO_BTF_ID ||
			   (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) {
			const struct btf_type *reg_ref_t;
@@ -5806,7 +5813,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
			if (reg->type == PTR_TO_BTF_ID) {
				reg_btf = reg->btf;
				reg_ref_id = reg->btf_id;
				/* Ensure only one argument is referenced PTR_TO_BTF_ID */
				/* Ensure only one argument is referenced
				 * PTR_TO_BTF_ID, check_func_arg_reg_off relies
				 * on only one referenced register being allowed
				 * for kfuncs.
				 */
				if (reg->ref_obj_id) {
					if (ref_obj_id) {
						bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
@@ -5888,19 +5899,16 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,

	/* Either both are set, or neither */
	WARN_ON_ONCE((ref_obj_id && !ref_regno) || (!ref_obj_id && ref_regno));
	if (is_kfunc) {
		rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog),
						BTF_KFUNC_TYPE_RELEASE, func_id);
		/* We already made sure ref_obj_id is set only for one argument */
	/* We already made sure ref_obj_id is set only for one argument. We do
	 * allow (!rel && ref_obj_id), so that passing such referenced
	 * PTR_TO_BTF_ID to other kfuncs works. Note that rel is only true when
	 * is_kfunc is true.
	 */
	if (rel && !ref_obj_id) {
		bpf_log(log, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
			func_name);
		return -EINVAL;
	}
		/* Allow (!rel && ref_obj_id), so that passing such referenced PTR_TO_BTF_ID to
		 * other kfuncs works
		 */
	}
	/* returns argument register number > 0 in case of reference release kfunc */
	return rel ? ref_regno : 0;
}
Loading