diff mbox series

[PATCHv19,bpf-next,2/6] bpf: add a new bpf argument type ARG_CONST_MAP_PTR_OR_NULL

Message ID 20210208104747.573461-3-liuhangbin@gmail.com
State New
Headers show
Series xdp: add a new helper for dev map multicast support | expand

Commit Message

Hangbin Liu Feb. 8, 2021, 10:47 a.m. UTC
Add a new bpf argument type ARG_CONST_MAP_PTR_OR_NULL which could be
used when we want to allow NULL pointer for map parameter. The bpf helper
need to take care and check if the map is NULL when use this type.

Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

---
v13-v19: no update
v11-v12: rebase the patch to latest bpf-next
v10: remove useless CONST_PTR_TO_MAP_OR_NULL and Copy-paste comment.
v9: merge the patch from [1] in to this series.
v1-v8: no this patch

[1] https://lore.kernel.org/bpf/20200715070001.2048207-1-liuhangbin@gmail.com/
---
 include/linux/bpf.h   |  1 +
 kernel/bpf/verifier.c | 10 ++++++----
 2 files changed, 7 insertions(+), 4 deletions(-)

Comments

Daniel Borkmann Feb. 15, 2021, 10:03 p.m. UTC | #1
On 2/8/21 11:47 AM, Hangbin Liu wrote:
> Add a new bpf argument type ARG_CONST_MAP_PTR_OR_NULL which could be

> used when we want to allow NULL pointer for map parameter. The bpf helper

> need to take care and check if the map is NULL when use this type.

> 

> Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>

> Acked-by: John Fastabend <john.fastabend@gmail.com>

> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

[...]
> @@ -4259,9 +4261,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,

>   		meta->ref_obj_id = reg->ref_obj_id;

>   	}

>   

> -	if (arg_type == ARG_CONST_MAP_PTR) {

> -		/* bpf_map_xxx(map_ptr) call: remember that map_ptr */

> -		meta->map_ptr = reg->map_ptr;

> +	if (arg_type == ARG_CONST_MAP_PTR ||

> +	    arg_type == ARG_CONST_MAP_PTR_OR_NULL) {

> +		meta->map_ptr = register_is_null(reg) ? NULL : reg->map_ptr;


Sorry, but after re-reading this is unfortunately still broken :( Looking at your
helper func proto:

+static const struct bpf_func_proto bpf_xdp_redirect_map_multi_proto = {
+	.func           = bpf_xdp_redirect_map_multi,
+	.gpl_only       = false,
+	.ret_type       = RET_INTEGER,
+	.arg1_type      = ARG_CONST_MAP_PTR,
+	.arg2_type      = ARG_CONST_MAP_PTR_OR_NULL,
+	.arg3_type      = ARG_ANYTHING,
+};

So in check_helper_call() first meta->map_ptr is set to arg1 map, then when
checking arg2 map it can either be const NULL or a valid map ptr, but then
later on in check_map_func_compatibility() we only end up checking compatibility
of the /2nd/ map (e.g. on !meta->map_ptr we just return 0). This means, we
can pass whatever map type as arg1 map and it will pass the verifier just fine.
diff mbox series

Patch

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 321966fc35db..b0777c8c03fd 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -296,6 +296,7 @@  enum bpf_arg_type {
 	ARG_CONST_ALLOC_SIZE_OR_ZERO,	/* number of allocated bytes requested */
 	ARG_PTR_TO_BTF_ID_SOCK_COMMON,	/* pointer to in-kernel sock_common or bpf-mirrored bpf_sock */
 	ARG_PTR_TO_PERCPU_BTF_ID,	/* pointer to in-kernel percpu type */
+	ARG_CONST_MAP_PTR_OR_NULL,	/* const argument used as pointer to bpf_map or NULL */
 	__BPF_ARG_TYPE_MAX,
 };
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 15694246f854..adc7a2b02a60 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -451,7 +451,8 @@  static bool arg_type_may_be_null(enum bpf_arg_type type)
 	       type == ARG_PTR_TO_MEM_OR_NULL ||
 	       type == ARG_PTR_TO_CTX_OR_NULL ||
 	       type == ARG_PTR_TO_SOCKET_OR_NULL ||
-	       type == ARG_PTR_TO_ALLOC_MEM_OR_NULL;
+	       type == ARG_PTR_TO_ALLOC_MEM_OR_NULL ||
+	       type == ARG_CONST_MAP_PTR_OR_NULL;
 }
 
 /* Determine whether the function releases some resources allocated by another
@@ -4114,6 +4115,7 @@  static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
 	[ARG_CONST_SIZE_OR_ZERO]	= &scalar_types,
 	[ARG_CONST_ALLOC_SIZE_OR_ZERO]	= &scalar_types,
 	[ARG_CONST_MAP_PTR]		= &const_map_ptr_types,
+	[ARG_CONST_MAP_PTR_OR_NULL]	= &const_map_ptr_types,
 	[ARG_PTR_TO_CTX]		= &context_types,
 	[ARG_PTR_TO_CTX_OR_NULL]	= &context_types,
 	[ARG_PTR_TO_SOCK_COMMON]	= &sock_types,
@@ -4259,9 +4261,9 @@  static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
 		meta->ref_obj_id = reg->ref_obj_id;
 	}
 
-	if (arg_type == ARG_CONST_MAP_PTR) {
-		/* bpf_map_xxx(map_ptr) call: remember that map_ptr */
-		meta->map_ptr = reg->map_ptr;
+	if (arg_type == ARG_CONST_MAP_PTR ||
+	    arg_type == ARG_CONST_MAP_PTR_OR_NULL) {
+		meta->map_ptr = register_is_null(reg) ? NULL : reg->map_ptr;
 	} else if (arg_type == ARG_PTR_TO_MAP_KEY) {
 		/* bpf_map_xxx(..., map_ptr, ..., key) call:
 		 * check that [key, key + map->key_size) are within