BPF Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v3 bpf-next 0/4] Add support of pointer to struct in global functions
@ 2021-02-12 20:56 Dmitrii Banshchikov
  2021-02-12 20:56 ` [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables Dmitrii Banshchikov
                   ` (3 more replies)
  0 siblings, 4 replies; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-12 20:56 UTC (permalink / raw)
  To: bpf
  Cc: Dmitrii Banshchikov, ast, daniel, andrii, kafai, songliubraving,
	yhs, john.fastabend, kpsingh, rdna

This patchset adds support of pointers to type with known size among
global function arguments.

The motivation is to overcome the limit on the maximum number of allowed
arguments and avoid tricky and unoptimal ways of passing arguments.

A referenced type may contain pointers but access via such pointers
cannot be veirified currently.

v2 -> v3
 - Fix reg ID generation
 - Fix commit description
 - Fix typo
 - Fix tests

v1 -> v2:
 - Allow pointer to any type with known size rather than struct only
 - Allow pointer in global functions only
 - Add more tests
 - Fix wrapping and v1 comments

Dmitrii Banshchikov (4):
  bpf: Rename bpf_reg_state variables
  bpf: Extract nullable reg type conversion into a helper function
  bpf: Support pointers in global func args
  selftests/bpf: Add unit tests for pointers in global functions

 include/linux/bpf_verifier.h                  |   2 +
 kernel/bpf/btf.c                              |  71 +++++++---
 kernel/bpf/verifier.c                         | 113 +++++++++++----
 .../bpf/prog_tests/global_func_args.c         |  60 ++++++++
 .../bpf/prog_tests/test_global_funcs.c        |   8 ++
 .../selftests/bpf/progs/test_global_func10.c  |  29 ++++
 .../selftests/bpf/progs/test_global_func11.c  |  19 +++
 .../selftests/bpf/progs/test_global_func12.c  |  21 +++
 .../selftests/bpf/progs/test_global_func13.c  |  24 ++++
 .../selftests/bpf/progs/test_global_func14.c  |  21 +++
 .../selftests/bpf/progs/test_global_func15.c  |  22 +++
 .../selftests/bpf/progs/test_global_func16.c  |  22 +++
 .../selftests/bpf/progs/test_global_func9.c   | 132 ++++++++++++++++++
 .../bpf/progs/test_global_func_args.c         |  91 ++++++++++++
 14 files changed, 588 insertions(+), 47 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/global_func_args.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func10.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func11.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func12.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func13.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func14.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func15.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func16.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func9.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func_args.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables
  2021-02-12 20:56 [PATCH v3 bpf-next 0/4] Add support of pointer to struct in global functions Dmitrii Banshchikov
@ 2021-02-12 20:56 ` Dmitrii Banshchikov
  2021-02-12 21:11   ` Andrii Nakryiko
  2021-02-12 20:56 ` [PATCH v3 bpf-next 2/4] bpf: Extract nullable reg type conversion into a helper function Dmitrii Banshchikov
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-12 20:56 UTC (permalink / raw)
  To: bpf
  Cc: Dmitrii Banshchikov, ast, daniel, andrii, kafai, songliubraving,
	yhs, john.fastabend, kpsingh, rdna

Using "reg" for an array of bpf_reg_state and "reg[i + 1]" for an
individual bpf_reg_state is error-prone and verbose. Use "regs" for the
former and "reg" for the latter as other code nearby does.

Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
---
 kernel/bpf/btf.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 756a93f534b6..bd5d2c563693 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -5291,7 +5291,7 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr
  * Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
  */
 int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
-			     struct bpf_reg_state *reg)
+			     struct bpf_reg_state *regs)
 {
 	struct bpf_verifier_log *log = &env->log;
 	struct bpf_prog *prog = env->prog;
@@ -5337,17 +5337,19 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 	 * verifier sees.
 	 */
 	for (i = 0; i < nargs; i++) {
+		struct bpf_reg_state *reg = &regs[i + 1];
+
 		t = btf_type_by_id(btf, args[i].type);
 		while (btf_type_is_modifier(t))
 			t = btf_type_by_id(btf, t->type);
 		if (btf_type_is_int(t) || btf_type_is_enum(t)) {
-			if (reg[i + 1].type == SCALAR_VALUE)
+			if (reg->type == SCALAR_VALUE)
 				continue;
 			bpf_log(log, "R%d is not a scalar\n", i + 1);
 			goto out;
 		}
 		if (btf_type_is_ptr(t)) {
-			if (reg[i + 1].type == SCALAR_VALUE) {
+			if (reg->type == SCALAR_VALUE) {
 				bpf_log(log, "R%d is not a pointer\n", i + 1);
 				goto out;
 			}
@@ -5355,13 +5357,13 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 			 * is passing PTR_TO_CTX.
 			 */
 			if (btf_get_prog_ctx_type(log, btf, t, prog->type, i)) {
-				if (reg[i + 1].type != PTR_TO_CTX) {
+				if (reg->type != PTR_TO_CTX) {
 					bpf_log(log,
 						"arg#%d expected pointer to ctx, but got %s\n",
 						i, btf_kind_str[BTF_INFO_KIND(t->info)]);
 					goto out;
 				}
-				if (check_ctx_reg(env, &reg[i + 1], i + 1))
+				if (check_ctx_reg(env, reg, i + 1))
 					goto out;
 				continue;
 			}
@@ -5388,7 +5390,7 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
  * (either PTR_TO_CTX or SCALAR_VALUE).
  */
 int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
-			  struct bpf_reg_state *reg)
+			  struct bpf_reg_state *regs)
 {
 	struct bpf_verifier_log *log = &env->log;
 	struct bpf_prog *prog = env->prog;
@@ -5459,16 +5461,18 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
 	 * Only PTR_TO_CTX and SCALAR are supported atm.
 	 */
 	for (i = 0; i < nargs; i++) {
+		struct bpf_reg_state *reg = &regs[i + 1];
+
 		t = btf_type_by_id(btf, args[i].type);
 		while (btf_type_is_modifier(t))
 			t = btf_type_by_id(btf, t->type);
 		if (btf_type_is_int(t) || btf_type_is_enum(t)) {
-			reg[i + 1].type = SCALAR_VALUE;
+			reg->type = SCALAR_VALUE;
 			continue;
 		}
 		if (btf_type_is_ptr(t) &&
 		    btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
-			reg[i + 1].type = PTR_TO_CTX;
+			reg->type = PTR_TO_CTX;
 			continue;
 		}
 		bpf_log(log, "Arg#%d type %s in %s() is not supported yet.\n",
-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v3 bpf-next 2/4] bpf: Extract nullable reg type conversion into a helper function
  2021-02-12 20:56 [PATCH v3 bpf-next 0/4] Add support of pointer to struct in global functions Dmitrii Banshchikov
  2021-02-12 20:56 ` [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables Dmitrii Banshchikov
@ 2021-02-12 20:56 ` Dmitrii Banshchikov
  2021-02-12 21:12   ` Andrii Nakryiko
  2021-02-12 20:56 ` [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args Dmitrii Banshchikov
  2021-02-12 20:56 ` [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions Dmitrii Banshchikov
  3 siblings, 1 reply; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-12 20:56 UTC (permalink / raw)
  To: bpf
  Cc: Dmitrii Banshchikov, ast, daniel, andrii, kafai, songliubraving,
	yhs, john.fastabend, kpsingh, rdna

Extract conversion from a register's nullable type to a type with a
value. The helper will be used in mark_ptr_not_null_reg().

Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
---
 kernel/bpf/verifier.c | 83 +++++++++++++++++++++++++++----------------
 1 file changed, 52 insertions(+), 31 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 15c15ea0abf5..e391ed325249 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1079,6 +1079,51 @@ static void mark_reg_known_zero(struct bpf_verifier_env *env,
 	__mark_reg_known_zero(regs + regno);
 }
 
+static void mark_ptr_not_null_reg(struct bpf_reg_state *reg)
+{
+	switch (reg->type) {
+	case PTR_TO_MAP_VALUE_OR_NULL: {
+		const struct bpf_map *map = reg->map_ptr;
+
+		if (map->inner_map_meta) {
+			reg->type = CONST_PTR_TO_MAP;
+			reg->map_ptr = map->inner_map_meta;
+		} else if (map->map_type == BPF_MAP_TYPE_XSKMAP) {
+			reg->type = PTR_TO_XDP_SOCK;
+		} else if (map->map_type == BPF_MAP_TYPE_SOCKMAP ||
+			   map->map_type == BPF_MAP_TYPE_SOCKHASH) {
+			reg->type = PTR_TO_SOCKET;
+		} else {
+			reg->type = PTR_TO_MAP_VALUE;
+		}
+		break;
+	}
+	case PTR_TO_SOCKET_OR_NULL:
+		reg->type = PTR_TO_SOCKET;
+		break;
+	case PTR_TO_SOCK_COMMON_OR_NULL:
+		reg->type = PTR_TO_SOCK_COMMON;
+		break;
+	case PTR_TO_TCP_SOCK_OR_NULL:
+		reg->type = PTR_TO_TCP_SOCK;
+		break;
+	case PTR_TO_BTF_ID_OR_NULL:
+		reg->type = PTR_TO_BTF_ID;
+		break;
+	case PTR_TO_MEM_OR_NULL:
+		reg->type = PTR_TO_MEM;
+		break;
+	case PTR_TO_RDONLY_BUF_OR_NULL:
+		reg->type = PTR_TO_RDONLY_BUF;
+		break;
+	case PTR_TO_RDWR_BUF_OR_NULL:
+		reg->type = PTR_TO_RDWR_BUF;
+		break;
+	default:
+		WARN_ON("unknown nullable register type");
+	}
+}
+
 static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg)
 {
 	return type_is_pkt_pointer(reg->type);
@@ -7737,43 +7782,19 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
 		}
 		if (is_null) {
 			reg->type = SCALAR_VALUE;
-		} else if (reg->type == PTR_TO_MAP_VALUE_OR_NULL) {
-			const struct bpf_map *map = reg->map_ptr;
-
-			if (map->inner_map_meta) {
-				reg->type = CONST_PTR_TO_MAP;
-				reg->map_ptr = map->inner_map_meta;
-			} else if (map->map_type == BPF_MAP_TYPE_XSKMAP) {
-				reg->type = PTR_TO_XDP_SOCK;
-			} else if (map->map_type == BPF_MAP_TYPE_SOCKMAP ||
-				   map->map_type == BPF_MAP_TYPE_SOCKHASH) {
-				reg->type = PTR_TO_SOCKET;
-			} else {
-				reg->type = PTR_TO_MAP_VALUE;
-			}
-		} else if (reg->type == PTR_TO_SOCKET_OR_NULL) {
-			reg->type = PTR_TO_SOCKET;
-		} else if (reg->type == PTR_TO_SOCK_COMMON_OR_NULL) {
-			reg->type = PTR_TO_SOCK_COMMON;
-		} else if (reg->type == PTR_TO_TCP_SOCK_OR_NULL) {
-			reg->type = PTR_TO_TCP_SOCK;
-		} else if (reg->type == PTR_TO_BTF_ID_OR_NULL) {
-			reg->type = PTR_TO_BTF_ID;
-		} else if (reg->type == PTR_TO_MEM_OR_NULL) {
-			reg->type = PTR_TO_MEM;
-		} else if (reg->type == PTR_TO_RDONLY_BUF_OR_NULL) {
-			reg->type = PTR_TO_RDONLY_BUF;
-		} else if (reg->type == PTR_TO_RDWR_BUF_OR_NULL) {
-			reg->type = PTR_TO_RDWR_BUF;
-		}
-		if (is_null) {
 			/* We don't need id and ref_obj_id from this point
 			 * onwards anymore, thus we should better reset it,
 			 * so that state pruning has chances to take effect.
 			 */
 			reg->id = 0;
 			reg->ref_obj_id = 0;
-		} else if (!reg_may_point_to_spin_lock(reg)) {
+
+			return;
+		}
+
+		mark_ptr_not_null_reg(reg);
+
+		if (!reg_may_point_to_spin_lock(reg)) {
 			/* For not-NULL ptr, reg->ref_obj_id will be reset
 			 * in release_reg_references().
 			 *
-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args
  2021-02-12 20:56 [PATCH v3 bpf-next 0/4] Add support of pointer to struct in global functions Dmitrii Banshchikov
  2021-02-12 20:56 ` [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables Dmitrii Banshchikov
  2021-02-12 20:56 ` [PATCH v3 bpf-next 2/4] bpf: Extract nullable reg type conversion into a helper function Dmitrii Banshchikov
@ 2021-02-12 20:56 ` Dmitrii Banshchikov
  2021-02-12 21:14   ` Andrii Nakryiko
  2021-02-13  2:09   ` Alexei Starovoitov
  2021-02-12 20:56 ` [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions Dmitrii Banshchikov
  3 siblings, 2 replies; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-12 20:56 UTC (permalink / raw)
  To: bpf
  Cc: Dmitrii Banshchikov, ast, daniel, andrii, kafai, songliubraving,
	yhs, john.fastabend, kpsingh, rdna

Add an ability to pass a pointer to a type with known size in arguments
of a global function. Such pointers may be used to overcome the limit on
the maximum number of arguments, avoid expensive and tricky workarounds
and to have multiple output arguments.

A referenced type may contain pointers but indirect access through them
isn't supported.

The implementation consists of two parts.  If a global function has an
argument that is a pointer to a type with known size then:

  1) In btf_check_func_arg_match(): check that the corresponding
register points to NULL or to a valid memory region that is large enough
to contain the expected argument's type.

  2) In btf_prepare_func_args(): set the corresponding register type to
PTR_TO_MEM_OR_NULL and its size to the size of the expected type.

Only global functions are supported because allowance of pointers for
static functions might break validation. Consider the following
scenario. A static function has a pointer argument. A caller passes
pointer to its stack memory. Because the callee can change referenced
memory verifier cannot longer assume any particular slot type of the
caller's stack memory hence the slot type is changed to SLOT_MISC.  If
there is an operation that relies on slot type other than SLOT_MISC then
verifier won't be able to infer safety of the operation.

When verifier sees a static function that has a pointer argument
different from PTR_TO_CTX then it skips arguments check and continues
with "inline" validation with more information available. The operation
that relies on the particular slot type now succeeds.

Because global functions were not allowed to have pointer arguments
different from PTR_TO_CTX it's not possible to break existing and valid
code.

Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
---
 include/linux/bpf_verifier.h |  2 ++
 kernel/bpf/btf.c             | 55 +++++++++++++++++++++++++++++-------
 kernel/bpf/verifier.c        | 30 ++++++++++++++++++++
 3 files changed, 77 insertions(+), 10 deletions(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 532c97836d0d..971b33aca13d 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -471,6 +471,8 @@ bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt);
 
 int check_ctx_reg(struct bpf_verifier_env *env,
 		  const struct bpf_reg_state *reg, int regno);
+int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+		   u32 regno, u32 mem_size);
 
 /* this lives here instead of in bpf.h because it needs to dereference tgt_prog */
 static inline u64 bpf_trampoline_compute_key(const struct bpf_prog *tgt_prog,
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index bd5d2c563693..2efeb5f4b343 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -5297,9 +5297,10 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 	struct bpf_prog *prog = env->prog;
 	struct btf *btf = prog->aux->btf;
 	const struct btf_param *args;
-	const struct btf_type *t;
-	u32 i, nargs, btf_id;
+	const struct btf_type *t, *ref_t;
+	u32 i, nargs, btf_id, type_size;
 	const char *tname;
+	bool is_global;
 
 	if (!prog->aux->func_info)
 		return -EINVAL;
@@ -5333,6 +5334,8 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 		bpf_log(log, "Function %s has %d > 5 args\n", tname, nargs);
 		goto out;
 	}
+
+	is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
 	/* check that BTF function arguments match actual types that the
 	 * verifier sees.
 	 */
@@ -5349,10 +5352,6 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 			goto out;
 		}
 		if (btf_type_is_ptr(t)) {
-			if (reg->type == SCALAR_VALUE) {
-				bpf_log(log, "R%d is not a pointer\n", i + 1);
-				goto out;
-			}
 			/* If function expects ctx type in BTF check that caller
 			 * is passing PTR_TO_CTX.
 			 */
@@ -5367,6 +5366,25 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
 					goto out;
 				continue;
 			}
+
+			if (!is_global)
+				goto out;
+
+			t = btf_type_skip_modifiers(btf, t->type, NULL);
+
+			ref_t = btf_resolve_size(btf, t, &type_size);
+			if (IS_ERR(ref_t)) {
+				bpf_log(log,
+				    "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
+				    i, btf_type_str(t), btf_name_by_offset(btf, t->name_off),
+					PTR_ERR(ref_t));
+				goto out;
+			}
+
+			if (check_mem_reg(env, reg, i + 1, type_size))
+				goto out;
+
+			continue;
 		}
 		bpf_log(log, "Unrecognized arg#%d type %s\n",
 			i, btf_kind_str[BTF_INFO_KIND(t->info)]);
@@ -5397,7 +5415,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
 	enum bpf_prog_type prog_type = prog->type;
 	struct btf *btf = prog->aux->btf;
 	const struct btf_param *args;
-	const struct btf_type *t;
+	const struct btf_type *t, *ref_t;
 	u32 i, nargs, btf_id;
 	const char *tname;
 
@@ -5470,9 +5488,26 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
 			reg->type = SCALAR_VALUE;
 			continue;
 		}
-		if (btf_type_is_ptr(t) &&
-		    btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
-			reg->type = PTR_TO_CTX;
+		if (btf_type_is_ptr(t)) {
+			if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
+				reg->type = PTR_TO_CTX;
+				continue;
+			}
+
+			t = btf_type_skip_modifiers(btf, t->type, NULL);
+
+			ref_t = btf_resolve_size(btf, t, &reg->mem_size);
+			if (IS_ERR(ref_t)) {
+				bpf_log(log,
+				    "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
+				    i, btf_type_str(t), btf_name_by_offset(btf, t->name_off),
+					PTR_ERR(ref_t));
+				return -EINVAL;
+			}
+
+			reg->type = PTR_TO_MEM_OR_NULL;
+			reg->id = ++env->id_gen;
+
 			continue;
 		}
 		bpf_log(log, "Arg#%d type %s in %s() is not supported yet.\n",
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e391ed325249..ed04999ae247 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4272,6 +4272,29 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 	}
 }
 
+int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+		   u32 regno, u32 mem_size)
+{
+	if (register_is_null(reg))
+		return 0;
+
+	if (reg_type_may_be_null(reg->type)) {
+		/* Assuming that the register contains a value check if the memory
+		 * access is safe. Temporarily save and restore the register's state as
+		 * the conversion shouldn't be visible to a caller.
+		 */
+		const struct bpf_reg_state saved_reg = *reg;
+		int rv;
+
+		mark_ptr_not_null_reg(reg);
+		rv = check_helper_mem_access(env, regno, mem_size, true, NULL);
+		*reg = saved_reg;
+		return rv;
+	}
+
+	return check_helper_mem_access(env, regno, mem_size, true, NULL);
+}
+
 /* Implementation details:
  * bpf_map_lookup returns PTR_TO_MAP_VALUE_OR_NULL
  * Two bpf_map_lookups (even with the same key) will have different reg->id.
@@ -11941,6 +11964,13 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 				mark_reg_known_zero(env, regs, i);
 			else if (regs[i].type == SCALAR_VALUE)
 				mark_reg_unknown(env, regs, i);
+			else if (regs[i].type == PTR_TO_MEM_OR_NULL) {
+				const u32 mem_size = regs[i].mem_size;
+
+				mark_reg_known_zero(env, regs, i);
+				regs[i].mem_size = mem_size;
+				regs[i].id = ++env->id_gen;
+			}
 		}
 	} else {
 		/* 1st arg to a function */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions
  2021-02-12 20:56 [PATCH v3 bpf-next 0/4] Add support of pointer to struct in global functions Dmitrii Banshchikov
                   ` (2 preceding siblings ...)
  2021-02-12 20:56 ` [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args Dmitrii Banshchikov
@ 2021-02-12 20:56 ` Dmitrii Banshchikov
  2021-02-12 21:15   ` Andrii Nakryiko
  2021-02-23  6:43   ` Andrii Nakryiko
  3 siblings, 2 replies; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-12 20:56 UTC (permalink / raw)
  To: bpf
  Cc: Dmitrii Banshchikov, ast, daniel, andrii, kafai, songliubraving,
	yhs, john.fastabend, kpsingh, rdna

test_global_func9  - check valid pointer's scenarios
test_global_func10 - check that a smaller type cannot be passed as a
                     larger one
test_global_func11 - check that CTX pointer cannot be passed
test_global_func12 - check access to a null pointer
test_global_func13 - check access to an arbitrary pointer value
test_global_func14 - check that an opaque pointer cannot be passed
test_global_func15 - check that a variable has an unknown value after
		     it was passed to a global function by pointer
test_global_func16 - check access to uninitialized stack memory

test_global_func_args - check read and write operations through a pointer

Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
---
 .../bpf/prog_tests/global_func_args.c         |  60 ++++++++
 .../bpf/prog_tests/test_global_funcs.c        |   8 ++
 .../selftests/bpf/progs/test_global_func10.c  |  29 ++++
 .../selftests/bpf/progs/test_global_func11.c  |  19 +++
 .../selftests/bpf/progs/test_global_func12.c  |  21 +++
 .../selftests/bpf/progs/test_global_func13.c  |  24 ++++
 .../selftests/bpf/progs/test_global_func14.c  |  21 +++
 .../selftests/bpf/progs/test_global_func15.c  |  22 +++
 .../selftests/bpf/progs/test_global_func16.c  |  22 +++
 .../selftests/bpf/progs/test_global_func9.c   | 132 ++++++++++++++++++
 .../bpf/progs/test_global_func_args.c         |  91 ++++++++++++
 11 files changed, 449 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/global_func_args.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func10.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func11.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func12.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func13.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func14.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func15.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func16.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func9.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_global_func_args.c

diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_args.c b/tools/testing/selftests/bpf/prog_tests/global_func_args.c
new file mode 100644
index 000000000000..8bcc2869102f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/global_func_args.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "test_progs.h"
+#include "network_helpers.h"
+
+static __u32 duration;
+
+static void test_global_func_args0(struct bpf_object *obj)
+{
+	int err, i, map_fd, actual_value;
+	const char *map_name = "values";
+
+	map_fd = bpf_find_map(__func__, obj, map_name);
+	if (CHECK(map_fd < 0, "bpf_find_map", "cannot find BPF map %s: %s\n",
+		map_name, strerror(errno)))
+		return;
+
+	struct {
+		const char *descr;
+		int expected_value;
+	} tests[] = {
+		{"passing NULL pointer", 0},
+		{"returning value", 1},
+		{"reading local variable", 100 },
+		{"writing local variable", 101 },
+		{"reading global variable", 42 },
+		{"writing global variable", 43 },
+		{"writing to pointer-to-pointer", 1 },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+		const int expected_value = tests[i].expected_value;
+
+		err = bpf_map_lookup_elem(map_fd, &i, &actual_value);
+
+		CHECK(err || actual_value != expected_value, tests[i].descr,
+			 "err %d result %d expected %d\n", err, actual_value, expected_value);
+	}
+}
+
+void test_global_func_args(void)
+{
+	const char *file = "./test_global_func_args.o";
+	__u32 retval;
+	struct bpf_object *obj;
+	int err, prog_fd;
+
+	err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
+	if (CHECK(err, "load program", "error %d loading %s\n", err, file))
+		return;
+
+	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
+				NULL, NULL, &retval, &duration);
+	CHECK(err || retval, "pass global func args run",
+	      "err %d errno %d retval %d duration %d\n",
+	      err, errno, retval, duration);
+
+	test_global_func_args0(obj);
+
+	bpf_object__close(obj);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c b/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
index 32e4348b714b..7e13129f593a 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c
@@ -61,6 +61,14 @@ void test_test_global_funcs(void)
 		{ "test_global_func6.o" , "modified ctx ptr R2" },
 		{ "test_global_func7.o" , "foo() doesn't return scalar" },
 		{ "test_global_func8.o" },
+		{ "test_global_func9.o" },
+		{ "test_global_func10.o", "invalid indirect read from stack" },
+		{ "test_global_func11.o", "Caller passes invalid args into func#1" },
+		{ "test_global_func12.o", "invalid mem access 'mem_or_null'" },
+		{ "test_global_func13.o", "Caller passes invalid args into func#1" },
+		{ "test_global_func14.o", "reference type('FWD S') size cannot be determined" },
+		{ "test_global_func15.o", "At program exit the register R0 has value" },
+		{ "test_global_func16.o", "invalid indirect read from stack" },
 	};
 	libbpf_print_fn_t old_print_fn = NULL;
 	int err, i, duration = 0;
diff --git a/tools/testing/selftests/bpf/progs/test_global_func10.c b/tools/testing/selftests/bpf/progs/test_global_func10.c
new file mode 100644
index 000000000000..61c2ae92ce41
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func10.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct Small {
+	int x;
+};
+
+struct Big {
+	int x;
+	int y;
+};
+
+__noinline int foo(const struct Big *big)
+{
+	if (big == 0)
+		return 0;
+
+	return bpf_get_prandom_u32() < big->y;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	const struct Small small = {.x = skb->len };
+
+	return foo((struct Big *)&small) ? 1 : 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func11.c b/tools/testing/selftests/bpf/progs/test_global_func11.c
new file mode 100644
index 000000000000..28488047c849
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func11.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+	int x;
+};
+
+__noinline int foo(const struct S *s)
+{
+	return s ? bpf_get_prandom_u32() < s->x : 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	return foo(skb);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func12.c b/tools/testing/selftests/bpf/progs/test_global_func12.c
new file mode 100644
index 000000000000..62343527cc59
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func12.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+	int x;
+};
+
+__noinline int foo(const struct S *s)
+{
+	return bpf_get_prandom_u32() < s->x;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	const struct S s = {.x = skb->len };
+
+	return foo(&s);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func13.c b/tools/testing/selftests/bpf/progs/test_global_func13.c
new file mode 100644
index 000000000000..ff8897c1ac22
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func13.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+	int x;
+};
+
+__noinline int foo(const struct S *s)
+{
+	if (s)
+		return bpf_get_prandom_u32() < s->x;
+
+	return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	const struct S *s = (const struct S *)(0xbedabeda);
+
+	return foo(s);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func14.c b/tools/testing/selftests/bpf/progs/test_global_func14.c
new file mode 100644
index 000000000000..698c77199ebf
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func14.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S;
+
+__noinline int foo(const struct S *s)
+{
+	if (s)
+		return bpf_get_prandom_u32() < *(const int *) s;
+
+	return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+
+	return foo(NULL);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func15.c b/tools/testing/selftests/bpf/progs/test_global_func15.c
new file mode 100644
index 000000000000..c19c435988d5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func15.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+__noinline int foo(unsigned int *v)
+{
+	if (v)
+		*v = bpf_get_prandom_u32();
+
+	return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	unsigned int v = 1;
+
+	foo(&v);
+
+	return v;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func16.c b/tools/testing/selftests/bpf/progs/test_global_func16.c
new file mode 100644
index 000000000000..0312d1e8d8c0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func16.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+__noinline int foo(int (*arr)[10])
+{
+	if (arr)
+		return (*arr)[9];
+
+	return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	int array[10];
+
+	const int rv = foo(&array);
+
+	return rv ? 1 : 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func9.c b/tools/testing/selftests/bpf/progs/test_global_func9.c
new file mode 100644
index 000000000000..bd233ddede98
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func9.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct S {
+	int x;
+};
+
+struct C {
+	int x;
+	int y;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, __u32);
+	__type(value, struct S);
+} map SEC(".maps");
+
+enum E {
+	E_ITEM
+};
+
+static int global_data_x = 100;
+static int volatile global_data_y = 500;
+
+__noinline int foo(const struct S *s)
+{
+	if (s)
+		return bpf_get_prandom_u32() < s->x;
+
+	return 0;
+}
+
+__noinline int bar(int *x)
+{
+	if (x)
+		*x &= bpf_get_prandom_u32();
+
+	return 0;
+}
+__noinline int baz(volatile int *x)
+{
+	if (x)
+		*x &= bpf_get_prandom_u32();
+
+	return 0;
+}
+
+__noinline int qux(enum E *e)
+{
+	if (e)
+		return *e;
+
+	return 0;
+}
+
+__noinline int quux(int (*arr)[10])
+{
+	if (arr)
+		return (*arr)[9];
+
+	return 0;
+}
+
+__noinline int quuz(int **p)
+{
+	if (p)
+		*p = NULL;
+
+	return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	int result = 0;
+
+	{
+		const struct S s = {.x = skb->len };
+
+		result |= foo(&s);
+	}
+
+	{
+		const __u32 key = 1;
+		const struct S *s = bpf_map_lookup_elem(&map, &key);
+
+		result |= foo(s);
+	}
+
+	{
+		const struct C c = {.x = skb->len, .y = skb->family };
+
+		result |= foo((const struct S *)&c);
+	}
+
+	{
+		result |= foo(NULL);
+	}
+
+	{
+		bar(&result);
+		bar(&global_data_x);
+	}
+
+	{
+		result |= baz(&global_data_y);
+	}
+
+	{
+		enum E e = E_ITEM;
+
+		result |= qux(&e);
+	}
+
+	{
+		int array[10] = {0};
+
+		result |= quux(&array);
+	}
+
+	{
+		int *p;
+
+		result |= quuz(&p);
+	}
+
+	return result ? 1 : 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_global_func_args.c b/tools/testing/selftests/bpf/progs/test_global_func_args.c
new file mode 100644
index 000000000000..cae309538a9e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_global_func_args.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+
+#include <bpf/bpf_helpers.h>
+
+struct S {
+	int v;
+};
+
+static volatile struct S global_variable;
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 7);
+	__type(key, __u32);
+	__type(value, int);
+} values SEC(".maps");
+
+static void save_value(__u32 index, int value)
+{
+	bpf_map_update_elem(&values, &index, &value, 0);
+}
+
+__noinline int foo(__u32 index, struct S *s)
+{
+	if (s) {
+		save_value(index, s->v);
+		return ++s->v;
+	}
+
+	save_value(index, 0);
+
+	return 1;
+}
+
+__noinline int bar(__u32 index, volatile struct S *s)
+{
+	if (s) {
+		save_value(index, s->v);
+		return ++s->v;
+	}
+
+	save_value(index, 0);
+
+	return 1;
+}
+
+__noinline int baz(struct S **s)
+{
+	if (s)
+		*s = 0;
+
+	return 0;
+}
+
+SEC("cgroup_skb/ingress")
+int test_cls(struct __sk_buff *skb)
+{
+	__u32 index = 0;
+
+	{
+		const int v = foo(index++, 0);
+
+		save_value(index++, v);
+	}
+
+	{
+		struct S s = { .v = 100 };
+
+		foo(index++, &s);
+		save_value(index++, s.v);
+	}
+
+	{
+		global_variable.v = 42;
+		bar(index++, &global_variable);
+		save_value(index++, global_variable.v);
+	}
+
+	{
+		struct S v, *p = &v;
+
+		baz(&p);
+		save_value(index++, !p);
+	}
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables
  2021-02-12 20:56 ` [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables Dmitrii Banshchikov
@ 2021-02-12 21:11   ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2021-02-12 21:11 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin Lau, Song Liu, Yonghong Song, john fastabend, KP Singh,
	Andrey Ignatov

On Fri, Feb 12, 2021 at 12:57 PM Dmitrii Banshchikov <me@ubique.spb.ru> wrote:
>
> Using "reg" for an array of bpf_reg_state and "reg[i + 1]" for an
> individual bpf_reg_state is error-prone and verbose. Use "regs" for the
> former and "reg" for the latter as other code nearby does.
>
> Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
> ---

Please carry over Acked-by and Reviewed-by tags next time (unless you
made substantial changes to the patch)

Acked-by: Andrii Nakryiko <andrii@kernel.org>

>  kernel/bpf/btf.c | 20 ++++++++++++--------
>  1 file changed, 12 insertions(+), 8 deletions(-)
>

[...]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 2/4] bpf: Extract nullable reg type conversion into a helper function
  2021-02-12 20:56 ` [PATCH v3 bpf-next 2/4] bpf: Extract nullable reg type conversion into a helper function Dmitrii Banshchikov
@ 2021-02-12 21:12   ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2021-02-12 21:12 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin Lau, Song Liu, Yonghong Song, john fastabend, KP Singh,
	Andrey Ignatov

On Fri, Feb 12, 2021 at 12:57 PM Dmitrii Banshchikov <me@ubique.spb.ru> wrote:
>
> Extract conversion from a register's nullable type to a type with a
> value. The helper will be used in mark_ptr_not_null_reg().
>
> Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
> ---

Acked-by: Andrii Nakryiko <andrii@kernel.org>

>  kernel/bpf/verifier.c | 83 +++++++++++++++++++++++++++----------------
>  1 file changed, 52 insertions(+), 31 deletions(-)
>

[...]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args
  2021-02-12 20:56 ` [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args Dmitrii Banshchikov
@ 2021-02-12 21:14   ` Andrii Nakryiko
  2021-02-13  2:09   ` Alexei Starovoitov
  1 sibling, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2021-02-12 21:14 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin Lau, Song Liu, Yonghong Song, john fastabend, KP Singh,
	Andrey Ignatov

On Fri, Feb 12, 2021 at 12:57 PM Dmitrii Banshchikov <me@ubique.spb.ru> wrote:
>
> Add an ability to pass a pointer to a type with known size in arguments
> of a global function. Such pointers may be used to overcome the limit on
> the maximum number of arguments, avoid expensive and tricky workarounds
> and to have multiple output arguments.
>
> A referenced type may contain pointers but indirect access through them
> isn't supported.
>
> The implementation consists of two parts.  If a global function has an
> argument that is a pointer to a type with known size then:
>
>   1) In btf_check_func_arg_match(): check that the corresponding
> register points to NULL or to a valid memory region that is large enough
> to contain the expected argument's type.
>
>   2) In btf_prepare_func_args(): set the corresponding register type to
> PTR_TO_MEM_OR_NULL and its size to the size of the expected type.
>
> Only global functions are supported because allowance of pointers for
> static functions might break validation. Consider the following
> scenario. A static function has a pointer argument. A caller passes
> pointer to its stack memory. Because the callee can change referenced
> memory verifier cannot longer assume any particular slot type of the
> caller's stack memory hence the slot type is changed to SLOT_MISC.  If
> there is an operation that relies on slot type other than SLOT_MISC then
> verifier won't be able to infer safety of the operation.
>
> When verifier sees a static function that has a pointer argument
> different from PTR_TO_CTX then it skips arguments check and continues
> with "inline" validation with more information available. The operation
> that relies on the particular slot type now succeeds.
>
> Because global functions were not allowed to have pointer arguments
> different from PTR_TO_CTX it's not possible to break existing and valid
> code.
>
> Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
> ---

Acked-by: Andrii Nakryiko <andrii@kernel.org>

>  include/linux/bpf_verifier.h |  2 ++
>  kernel/bpf/btf.c             | 55 +++++++++++++++++++++++++++++-------
>  kernel/bpf/verifier.c        | 30 ++++++++++++++++++++
>  3 files changed, 77 insertions(+), 10 deletions(-)
>

[...]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions
  2021-02-12 20:56 ` [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions Dmitrii Banshchikov
@ 2021-02-12 21:15   ` Andrii Nakryiko
  2021-02-23  6:43   ` Andrii Nakryiko
  1 sibling, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2021-02-12 21:15 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin Lau, Song Liu, Yonghong Song, john fastabend, KP Singh,
	Andrey Ignatov

On Fri, Feb 12, 2021 at 12:57 PM Dmitrii Banshchikov <me@ubique.spb.ru> wrote:
>
> test_global_func9  - check valid pointer's scenarios
> test_global_func10 - check that a smaller type cannot be passed as a
>                      larger one
> test_global_func11 - check that CTX pointer cannot be passed
> test_global_func12 - check access to a null pointer
> test_global_func13 - check access to an arbitrary pointer value
> test_global_func14 - check that an opaque pointer cannot be passed
> test_global_func15 - check that a variable has an unknown value after
>                      it was passed to a global function by pointer
> test_global_func16 - check access to uninitialized stack memory
>
> test_global_func_args - check read and write operations through a pointer
>
> Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
> ---

LGTM.

Acked-by: Andrii Nakryiko <andrii@kernel.org>

>  .../bpf/prog_tests/global_func_args.c         |  60 ++++++++
>  .../bpf/prog_tests/test_global_funcs.c        |   8 ++
>  .../selftests/bpf/progs/test_global_func10.c  |  29 ++++
>  .../selftests/bpf/progs/test_global_func11.c  |  19 +++
>  .../selftests/bpf/progs/test_global_func12.c  |  21 +++
>  .../selftests/bpf/progs/test_global_func13.c  |  24 ++++
>  .../selftests/bpf/progs/test_global_func14.c  |  21 +++
>  .../selftests/bpf/progs/test_global_func15.c  |  22 +++
>  .../selftests/bpf/progs/test_global_func16.c  |  22 +++
>  .../selftests/bpf/progs/test_global_func9.c   | 132 ++++++++++++++++++
>  .../bpf/progs/test_global_func_args.c         |  91 ++++++++++++
>  11 files changed, 449 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/global_func_args.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func10.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func11.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func12.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func13.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func14.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func15.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func16.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func9.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func_args.c
>

[...]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args
  2021-02-12 20:56 ` [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args Dmitrii Banshchikov
  2021-02-12 21:14   ` Andrii Nakryiko
@ 2021-02-13  2:09   ` Alexei Starovoitov
  2021-02-15  6:25     ` Dmitrii Banshchikov
  1 sibling, 1 reply; 14+ messages in thread
From: Alexei Starovoitov @ 2021-02-13  2:09 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, ast, daniel, andrii, kafai, songliubraving, yhs,
	john.fastabend, kpsingh, rdna

On Sat, Feb 13, 2021 at 12:56:41AM +0400, Dmitrii Banshchikov wrote:
> Add an ability to pass a pointer to a type with known size in arguments
> of a global function. Such pointers may be used to overcome the limit on
> the maximum number of arguments, avoid expensive and tricky workarounds
> and to have multiple output arguments.

Thanks a lot for adding this feature and exhaustive tests.
It's a massive improvement in function-by-function verification.
Hopefully it will increase its adoption.
I've applied the set to bpf-next.

> @@ -5349,10 +5352,6 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
>  			goto out;
>  		}
>  		if (btf_type_is_ptr(t)) {
> -			if (reg->type == SCALAR_VALUE) {
> -				bpf_log(log, "R%d is not a pointer\n", i + 1);
> -				goto out;
> -			}

Thanks for nuking this annoying warning along the way.
People complained that the verification log for normal static functions
contains above inexplicable message.

>  			/* If function expects ctx type in BTF check that caller
>  			 * is passing PTR_TO_CTX.
>  			 */
> @@ -5367,6 +5366,25 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
>  					goto out;
>  				continue;
>  			}
> +
> +			if (!is_global)
> +				goto out;
> +
> +			t = btf_type_skip_modifiers(btf, t->type, NULL);
> +
> +			ref_t = btf_resolve_size(btf, t, &type_size);
> +			if (IS_ERR(ref_t)) {
> +				bpf_log(log,
> +				    "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
> +				    i, btf_type_str(t), btf_name_by_offset(btf, t->name_off),
> +					PTR_ERR(ref_t));

Hopefully one annoying message won't get replaced with this annoying message :)
I think the type size should be known most of the time. So it should be fine.

> +		if (btf_type_is_ptr(t)) {
> +			if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
> +				reg->type = PTR_TO_CTX;
> +				continue;
> +			}

Do you think it would make sense to nuke another message in btf_get_prog_ctx_type ?
With this newly gained usability of global function the message
"arg#0 type is not a struct"
is not useful.
It was marginally useful in the past. Because global funcs supported
ptr_to_ctx only it wasn't seen as often.
Now this message probably can simply be removed. wdyt?

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args
  2021-02-13  2:09   ` Alexei Starovoitov
@ 2021-02-15  6:25     ` Dmitrii Banshchikov
  0 siblings, 0 replies; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-15  6:25 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: bpf, ast, daniel, andrii, kafai, songliubraving, yhs,
	john.fastabend, kpsingh, rdna

On Fri, Feb 12, 2021 at 06:09:37PM -0800, Alexei Starovoitov wrote:
> On Sat, Feb 13, 2021 at 12:56:41AM +0400, Dmitrii Banshchikov wrote:
> > Add an ability to pass a pointer to a type with known size in arguments
> > of a global function. Such pointers may be used to overcome the limit on
> > the maximum number of arguments, avoid expensive and tricky workarounds
> > and to have multiple output arguments.
> 
> Thanks a lot for adding this feature and exhaustive tests.
> It's a massive improvement in function-by-function verification.
> Hopefully it will increase its adoption.
> I've applied the set to bpf-next.
> 
> > @@ -5349,10 +5352,6 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
> >  			goto out;
> >  		}
> >  		if (btf_type_is_ptr(t)) {
> > -			if (reg->type == SCALAR_VALUE) {
> > -				bpf_log(log, "R%d is not a pointer\n", i + 1);
> > -				goto out;
> > -			}
> 
> Thanks for nuking this annoying warning along the way.
> People complained that the verification log for normal static functions
> contains above inexplicable message.
> 
> >  			/* If function expects ctx type in BTF check that caller
> >  			 * is passing PTR_TO_CTX.
> >  			 */
> > @@ -5367,6 +5366,25 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
> >  					goto out;
> >  				continue;
> >  			}
> > +
> > +			if (!is_global)
> > +				goto out;
> > +
> > +			t = btf_type_skip_modifiers(btf, t->type, NULL);
> > +
> > +			ref_t = btf_resolve_size(btf, t, &type_size);
> > +			if (IS_ERR(ref_t)) {
> > +				bpf_log(log,
> > +				    "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
> > +				    i, btf_type_str(t), btf_name_by_offset(btf, t->name_off),
> > +					PTR_ERR(ref_t));
> 
> Hopefully one annoying message won't get replaced with this annoying message :)
> I think the type size should be known most of the time. So it should be fine.
> 
> > +		if (btf_type_is_ptr(t)) {
> > +			if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
> > +				reg->type = PTR_TO_CTX;
> > +				continue;
> > +			}
> 
> Do you think it would make sense to nuke another message in btf_get_prog_ctx_type ?
> With this newly gained usability of global function the message
> "arg#0 type is not a struct"
> is not useful.
> It was marginally useful in the past. Because global funcs supported
> ptr_to_ctx only it wasn't seen as often.
> Now this message probably can simply be removed. wdyt?

Yes, I hit this log message while was working on the patch and it
looked confusing but forgot to adjust/remove it.
I will prepare patch.
Thank you.



-- 

Dmitrii Banshchikov

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions
  2021-02-12 20:56 ` [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions Dmitrii Banshchikov
  2021-02-12 21:15   ` Andrii Nakryiko
@ 2021-02-23  6:43   ` Andrii Nakryiko
  2021-02-23  8:22     ` [PATCH] selftests/bpf: Fix a compiler warning in global func test Dmitrii Banshchikov
  1 sibling, 1 reply; 14+ messages in thread
From: Andrii Nakryiko @ 2021-02-23  6:43 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin Lau, Song Liu, Yonghong Song, john fastabend, KP Singh,
	Andrey Ignatov

On Fri, Feb 12, 2021 at 12:57 PM Dmitrii Banshchikov <me@ubique.spb.ru> wrote:
>
> test_global_func9  - check valid pointer's scenarios
> test_global_func10 - check that a smaller type cannot be passed as a
>                      larger one
> test_global_func11 - check that CTX pointer cannot be passed
> test_global_func12 - check access to a null pointer
> test_global_func13 - check access to an arbitrary pointer value
> test_global_func14 - check that an opaque pointer cannot be passed
> test_global_func15 - check that a variable has an unknown value after
>                      it was passed to a global function by pointer
> test_global_func16 - check access to uninitialized stack memory
>
> test_global_func_args - check read and write operations through a pointer
>
> Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
> ---
>  .../bpf/prog_tests/global_func_args.c         |  60 ++++++++
>  .../bpf/prog_tests/test_global_funcs.c        |   8 ++
>  .../selftests/bpf/progs/test_global_func10.c  |  29 ++++
>  .../selftests/bpf/progs/test_global_func11.c  |  19 +++
>  .../selftests/bpf/progs/test_global_func12.c  |  21 +++
>  .../selftests/bpf/progs/test_global_func13.c  |  24 ++++
>  .../selftests/bpf/progs/test_global_func14.c  |  21 +++
>  .../selftests/bpf/progs/test_global_func15.c  |  22 +++
>  .../selftests/bpf/progs/test_global_func16.c  |  22 +++
>  .../selftests/bpf/progs/test_global_func9.c   | 132 ++++++++++++++++++
>  .../bpf/progs/test_global_func_args.c         |  91 ++++++++++++
>  11 files changed, 449 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/global_func_args.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func10.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func11.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func12.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func13.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func14.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func15.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func16.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func9.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_global_func_args.c
>

[...]

> diff --git a/tools/testing/selftests/bpf/progs/test_global_func11.c b/tools/testing/selftests/bpf/progs/test_global_func11.c
> new file mode 100644
> index 000000000000..28488047c849
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/test_global_func11.c
> @@ -0,0 +1,19 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <stddef.h>
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +
> +struct S {
> +       int x;
> +};
> +
> +__noinline int foo(const struct S *s)
> +{
> +       return s ? bpf_get_prandom_u32() < s->x : 0;
> +}
> +
> +SEC("cgroup_skb/ingress")
> +int test_cls(struct __sk_buff *skb)
> +{
> +       return foo(skb);

This needs (void *) cast. It currently generates compilation warning:

progs/test_global_func11.c:18:13: warning: incompatible pointer types
passing 'struct __sk_buff *' to parameter of type 'const struct S *'
[-Wincompatible-pointer-types]
        return foo(skb);
                   ^~~
progs/test_global_func11.c:10:36: note: passing argument to parameter 's' here
__noinline int foo(const struct S *s)
                                   ^

Please send a follow-up patch.

> +}

[...]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH] selftests/bpf: Fix a compiler warning in global func test
  2021-02-23  6:43   ` Andrii Nakryiko
@ 2021-02-23  8:22     ` Dmitrii Banshchikov
  2021-02-24 15:50       ` patchwork-bot+netdevbpf
  0 siblings, 1 reply; 14+ messages in thread
From: Dmitrii Banshchikov @ 2021-02-23  8:22 UTC (permalink / raw)
  To: bpf
  Cc: Dmitrii Banshchikov, ast, daniel, andrii, kafai, songliubraving,
	yhs, john.fastabend, kpsingh, rdna

Add an explicit 'const void *' cast to pass program ctx pointer type into
a global function that expects pointer to structure.

warning: incompatible pointer types
passing 'struct __sk_buff *' to parameter of type 'const struct S *'
[-Wincompatible-pointer-types]
        return foo(skb);
                   ^~~
progs/test_global_func11.c:10:36: note: passing argument to parameter 's' here
__noinline int foo(const struct S *s)
                                   ^

Fixes: 8b08807d039a ("selftests/bpf: Add unit tests for pointers in global functions")
Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
---
 tools/testing/selftests/bpf/progs/test_global_func11.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/progs/test_global_func11.c b/tools/testing/selftests/bpf/progs/test_global_func11.c
index 28488047c849..ef5277d982d9 100644
--- a/tools/testing/selftests/bpf/progs/test_global_func11.c
+++ b/tools/testing/selftests/bpf/progs/test_global_func11.c
@@ -15,5 +15,5 @@ __noinline int foo(const struct S *s)
 SEC("cgroup_skb/ingress")
 int test_cls(struct __sk_buff *skb)
 {
-	return foo(skb);
+	return foo((const void *)skb);
 }
-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] selftests/bpf: Fix a compiler warning in global func test
  2021-02-23  8:22     ` [PATCH] selftests/bpf: Fix a compiler warning in global func test Dmitrii Banshchikov
@ 2021-02-24 15:50       ` patchwork-bot+netdevbpf
  0 siblings, 0 replies; 14+ messages in thread
From: patchwork-bot+netdevbpf @ 2021-02-24 15:50 UTC (permalink / raw)
  To: Dmitrii Banshchikov
  Cc: bpf, ast, daniel, andrii, kafai, songliubraving, yhs,
	john.fastabend, kpsingh, rdna

Hello:

This patch was applied to bpf/bpf.git (refs/heads/master):

On Tue, 23 Feb 2021 12:22:11 +0400 you wrote:
> Add an explicit 'const void *' cast to pass program ctx pointer type into
> a global function that expects pointer to structure.
> 
> warning: incompatible pointer types
> passing 'struct __sk_buff *' to parameter of type 'const struct S *'
> [-Wincompatible-pointer-types]
>         return foo(skb);
>                    ^~~
> progs/test_global_func11.c:10:36: note: passing argument to parameter 's' here
> __noinline int foo(const struct S *s)
>                                    ^
> 
> [...]

Here is the summary with links:
  - selftests/bpf: Fix a compiler warning in global func test
    https://git.kernel.org/bpf/bpf/c/c41d81bfbb45

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, back to index

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-12 20:56 [PATCH v3 bpf-next 0/4] Add support of pointer to struct in global functions Dmitrii Banshchikov
2021-02-12 20:56 ` [PATCH v3 bpf-next 1/4] bpf: Rename bpf_reg_state variables Dmitrii Banshchikov
2021-02-12 21:11   ` Andrii Nakryiko
2021-02-12 20:56 ` [PATCH v3 bpf-next 2/4] bpf: Extract nullable reg type conversion into a helper function Dmitrii Banshchikov
2021-02-12 21:12   ` Andrii Nakryiko
2021-02-12 20:56 ` [PATCH v3 bpf-next 3/4] bpf: Support pointers in global func args Dmitrii Banshchikov
2021-02-12 21:14   ` Andrii Nakryiko
2021-02-13  2:09   ` Alexei Starovoitov
2021-02-15  6:25     ` Dmitrii Banshchikov
2021-02-12 20:56 ` [PATCH v3 bpf-next 4/4] selftests/bpf: Add unit tests for pointers in global functions Dmitrii Banshchikov
2021-02-12 21:15   ` Andrii Nakryiko
2021-02-23  6:43   ` Andrii Nakryiko
2021-02-23  8:22     ` [PATCH] selftests/bpf: Fix a compiler warning in global func test Dmitrii Banshchikov
2021-02-24 15:50       ` patchwork-bot+netdevbpf

BPF Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/bpf/0 bpf/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 bpf bpf/ https://lore.kernel.org/bpf \
		bpf@vger.kernel.org
	public-inbox-index bpf

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.bpf


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git