[RFC][PATCH v8 04/12] btf: Introduce __maybe_null suffix for kfunc parameter declaration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Since kfuncs don't have a prototype defined, the BTF code has to infer the
information in a different way. For example, naming a parameter with the
__sz suffix is currently the way to declare such parameter as a memory
size.

Another feature not available without a prototype was flagging an argument
as PTR_MAYBE_NULL, meaning that the function is trusted to correctly handle
a NULL pointer.

Similarly to the __sz suffix, introduce the __maybe_null suffix to have an
equivalent feature of setting the PTR_MAYBE_NULL flag.

Cc: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx>
Cc: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>
Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
 kernel/bpf/btf.c | 48 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 34 insertions(+), 14 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 9e94571d1626..80e3098fcb48 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -6142,18 +6142,13 @@ static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log,
 	return true;
 }
 
-static bool is_kfunc_arg_mem_size(const struct btf *btf,
-				  const struct btf_param *arg,
-				  const struct bpf_reg_state *reg)
+static bool btf_param_match_suffix(const struct btf *btf,
+				   const struct btf_param *arg,
+				   const char *suffix)
 {
-	int len, sfx_len = sizeof("__sz") - 1;
-	const struct btf_type *t;
+	int len, sfx_len = strlen(suffix);
 	const char *param_name;
 
-	t = btf_type_skip_modifiers(btf, arg->type, NULL);
-	if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
-		return false;
-
 	/* In the future, this can be ported to use BTF tagging */
 	param_name = btf_name_by_offset(btf, arg->name_off);
 	if (str_is_empty(param_name))
@@ -6162,12 +6157,25 @@ static bool is_kfunc_arg_mem_size(const struct btf *btf,
 	if (len < sfx_len)
 		return false;
 	param_name += len - sfx_len;
-	if (strncmp(param_name, "__sz", sfx_len))
+	if (strncmp(param_name, suffix, sfx_len))
 		return false;
 
 	return true;
 }
 
+static bool is_kfunc_arg_mem_size(const struct btf *btf,
+				  const struct btf_param *arg,
+				  const struct bpf_reg_state *reg)
+{
+	const struct btf_type *t;
+
+	t = btf_type_skip_modifiers(btf, arg->type, NULL);
+	if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
+		return false;
+
+	return btf_param_match_suffix(btf, arg, "__sz");
+}
+
 static int btf_check_func_arg_match(struct bpf_verifier_env *env,
 				    const struct btf *btf, u32 func_id,
 				    struct bpf_reg_state *regs,
@@ -6302,8 +6310,12 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
 					i, btf_type_str(t));
 				return -EINVAL;
 			}
-		} else if (is_kfunc && (reg->type == PTR_TO_BTF_ID ||
-			   (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) {
+		} else if (is_kfunc && ((reg->type == PTR_TO_BTF_ID ||
+			   (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type))) ||
+			   (btf_param_match_suffix(btf, &args[i], "__maybe_null") &&
+			    (reg->type == PTR_TO_BTF_ID_OR_NULL ||
+			    (reg2btf_ids[base_type(reg->type)] &&
+			     type_flag(reg->type) == PTR_MAYBE_NULL))))) {
 			const struct btf_type *reg_ref_t;
 			const struct btf *reg_btf;
 			const char *reg_ref_tname;
@@ -6316,7 +6328,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
 				return -EINVAL;
 			}
 
-			if (reg->type == PTR_TO_BTF_ID) {
+			if (base_type(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 */
@@ -6354,6 +6366,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
 			if (is_kfunc) {
 				bool arg_mem_size = i + 1 < nargs && is_kfunc_arg_mem_size(btf, &args[i + 1], &regs[regno + 1]);
 				bool arg_dynptr = !strcmp(ref_tname, "bpf_dynptr_kern");
+				bool arg_null_ok = btf_param_match_suffix(btf, &args[i],
+									  "__maybe_null") &&
+						   reg->type == SCALAR_VALUE &&
+						   tnum_equals_const(reg->var_off, 0);
 
 				/* Permit pointer to mem, but only when argument
 				 * type is pointer to scalar, or struct composed
@@ -6363,7 +6379,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
 				 */
 				if (!btf_type_is_scalar(ref_t) &&
 				    !__btf_type_is_scalar_struct(log, btf, ref_t, 0) &&
-				    !arg_dynptr &&
+				    !arg_dynptr && !arg_null_ok &&
 				    (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) {
 					bpf_log(log,
 						"arg#%d pointer type %s %s must point to %sscalar, or struct with scalar\n",
@@ -6371,6 +6387,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
 					return -EINVAL;
 				}
 
+				/* kfunc accepts NULL pointer. */
+				if (arg_null_ok)
+					continue;
+
 				/* Assume initialized dynptr. */
 				if (arg_dynptr) {
 					if (!is_dynptr_reg_valid_init(env, reg,
-- 
2.25.1




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux