BPF List
 help / color / mirror / Atom feed
* [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute
@ 2024-10-21 15:28 Daniel Borkmann
  2024-10-21 15:28 ` [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning Daniel Borkmann
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Daniel Borkmann @ 2024-10-21 15:28 UTC (permalink / raw)
  To: ast; +Cc: andrii, kongln9170, memxor, bpf

Add a MEM_WRITE attribute for BPF helper functions which can be used in
bpf_func_proto to annotate an argument type in order to let the verifier
know that the helper writes into the memory passed as an argument. In
the past MEM_UNINIT has been (ab)used for this function, but the latter
merely tells the verifier that the passed memory can be uninitialized.

There have been bugs with overloading the latter but aside from that
there are also cases where the passed memory is read + written which
currently cannot be expressed, see also 4b3786a6c539 ("bpf: Zero former
ARG_PTR_TO_{LONG,INT} args in case of error").

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 include/linux/bpf.h      | 14 +++++++++++---
 kernel/bpf/helpers.c     | 10 +++++-----
 kernel/bpf/ringbuf.c     |  2 +-
 kernel/bpf/syscall.c     |  2 +-
 kernel/trace/bpf_trace.c |  4 ++--
 net/core/filter.c        |  4 ++--
 6 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 19d8ca8ac960..bdadb0bb6cec 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -635,6 +635,7 @@ enum bpf_type_flag {
 	 */
 	PTR_UNTRUSTED		= BIT(6 + BPF_BASE_TYPE_BITS),
 
+	/* MEM can be uninitialized. */
 	MEM_UNINIT		= BIT(7 + BPF_BASE_TYPE_BITS),
 
 	/* DYNPTR points to memory local to the bpf program. */
@@ -700,6 +701,13 @@ enum bpf_type_flag {
 	 */
 	MEM_ALIGNED		= BIT(17 + BPF_BASE_TYPE_BITS),
 
+	/* MEM is being written to, often combined with MEM_UNINIT. Non-presence
+	 * of MEM_WRITE means that MEM is only being read. MEM_WRITE without the
+	 * MEM_UNINIT means that memory needs to be initialized since it is also
+	 * read.
+	 */
+	MEM_WRITE		= BIT(18 + BPF_BASE_TYPE_BITS),
+
 	__BPF_TYPE_FLAG_MAX,
 	__BPF_TYPE_LAST_FLAG	= __BPF_TYPE_FLAG_MAX - 1,
 };
@@ -758,10 +766,10 @@ enum bpf_arg_type {
 	ARG_PTR_TO_SOCKET_OR_NULL	= PTR_MAYBE_NULL | ARG_PTR_TO_SOCKET,
 	ARG_PTR_TO_STACK_OR_NULL	= PTR_MAYBE_NULL | ARG_PTR_TO_STACK,
 	ARG_PTR_TO_BTF_ID_OR_NULL	= PTR_MAYBE_NULL | ARG_PTR_TO_BTF_ID,
-	/* pointer to memory does not need to be initialized, helper function must fill
-	 * all bytes or clear them in error case.
+	/* Pointer to memory does not need to be initialized, since helper function
+	 * fills all bytes or clears them in error case.
 	 */
-	ARG_PTR_TO_UNINIT_MEM		= MEM_UNINIT | ARG_PTR_TO_MEM,
+	ARG_PTR_TO_UNINIT_MEM		= MEM_UNINIT | MEM_WRITE | ARG_PTR_TO_MEM,
 	/* Pointer to valid memory of size known at compile time. */
 	ARG_PTR_TO_FIXED_SIZE_MEM	= MEM_FIXED_SIZE | ARG_PTR_TO_MEM,
 
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 1a43d06eab28..ca3f0a2e5ed5 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -111,7 +111,7 @@ const struct bpf_func_proto bpf_map_pop_elem_proto = {
 	.gpl_only	= false,
 	.ret_type	= RET_INTEGER,
 	.arg1_type	= ARG_CONST_MAP_PTR,
-	.arg2_type	= ARG_PTR_TO_MAP_VALUE | MEM_UNINIT,
+	.arg2_type	= ARG_PTR_TO_MAP_VALUE | MEM_UNINIT | MEM_WRITE,
 };
 
 BPF_CALL_2(bpf_map_peek_elem, struct bpf_map *, map, void *, value)
@@ -124,7 +124,7 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = {
 	.gpl_only	= false,
 	.ret_type	= RET_INTEGER,
 	.arg1_type	= ARG_CONST_MAP_PTR,
-	.arg2_type	= ARG_PTR_TO_MAP_VALUE | MEM_UNINIT,
+	.arg2_type	= ARG_PTR_TO_MAP_VALUE | MEM_UNINIT | MEM_WRITE,
 };
 
 BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu)
@@ -538,7 +538,7 @@ const struct bpf_func_proto bpf_strtol_proto = {
 	.arg1_type	= ARG_PTR_TO_MEM | MEM_RDONLY,
 	.arg2_type	= ARG_CONST_SIZE,
 	.arg3_type	= ARG_ANYTHING,
-	.arg4_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg4_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg4_size	= sizeof(s64),
 };
 
@@ -566,7 +566,7 @@ const struct bpf_func_proto bpf_strtoul_proto = {
 	.arg1_type	= ARG_PTR_TO_MEM | MEM_RDONLY,
 	.arg2_type	= ARG_CONST_SIZE,
 	.arg3_type	= ARG_ANYTHING,
-	.arg4_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg4_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg4_size	= sizeof(u64),
 };
 
@@ -1742,7 +1742,7 @@ static const struct bpf_func_proto bpf_dynptr_from_mem_proto = {
 	.arg1_type	= ARG_PTR_TO_UNINIT_MEM,
 	.arg2_type	= ARG_CONST_SIZE_OR_ZERO,
 	.arg3_type	= ARG_ANYTHING,
-	.arg4_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT,
+	.arg4_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT | MEM_WRITE,
 };
 
 BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern *, src,
diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c
index de3b681d1d13..e1cfe890e0be 100644
--- a/kernel/bpf/ringbuf.c
+++ b/kernel/bpf/ringbuf.c
@@ -632,7 +632,7 @@ const struct bpf_func_proto bpf_ringbuf_reserve_dynptr_proto = {
 	.arg1_type	= ARG_CONST_MAP_PTR,
 	.arg2_type	= ARG_ANYTHING,
 	.arg3_type	= ARG_ANYTHING,
-	.arg4_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | MEM_UNINIT,
+	.arg4_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | MEM_UNINIT | MEM_WRITE,
 };
 
 BPF_CALL_2(bpf_ringbuf_submit_dynptr, struct bpf_dynptr_kern *, ptr, u64, flags)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a8f1808a1ca5..161ac70e01d2 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5877,7 +5877,7 @@ static const struct bpf_func_proto bpf_kallsyms_lookup_name_proto = {
 	.arg1_type	= ARG_PTR_TO_MEM,
 	.arg2_type	= ARG_CONST_SIZE_OR_ZERO,
 	.arg3_type	= ARG_ANYTHING,
-	.arg4_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg4_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg4_size	= sizeof(u64),
 };
 
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index a582cd25ca87..ef954abe7d74 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1202,7 +1202,7 @@ static const struct bpf_func_proto bpf_get_func_arg_proto = {
 	.ret_type	= RET_INTEGER,
 	.arg1_type	= ARG_PTR_TO_CTX,
 	.arg2_type	= ARG_ANYTHING,
-	.arg3_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg3_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg3_size	= sizeof(u64),
 };
 
@@ -1219,7 +1219,7 @@ static const struct bpf_func_proto bpf_get_func_ret_proto = {
 	.func		= get_func_ret,
 	.ret_type	= RET_INTEGER,
 	.arg1_type	= ARG_PTR_TO_CTX,
-	.arg2_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg2_type	= ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg2_size	= sizeof(u64),
 };
 
diff --git a/net/core/filter.c b/net/core/filter.c
index 4e3f42cc6611..6be0c0b86049 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -6368,7 +6368,7 @@ static const struct bpf_func_proto bpf_skb_check_mtu_proto = {
 	.ret_type	= RET_INTEGER,
 	.arg1_type      = ARG_PTR_TO_CTX,
 	.arg2_type      = ARG_ANYTHING,
-	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg3_size	= sizeof(u32),
 	.arg4_type      = ARG_ANYTHING,
 	.arg5_type      = ARG_ANYTHING,
@@ -6380,7 +6380,7 @@ static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {
 	.ret_type	= RET_INTEGER,
 	.arg1_type      = ARG_PTR_TO_CTX,
 	.arg2_type      = ARG_ANYTHING,
-	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_ALIGNED,
+	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
 	.arg3_size	= sizeof(u32),
 	.arg4_type      = ARG_ANYTHING,
 	.arg5_type      = ARG_ANYTHING,
-- 
2.43.0


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

* [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning
  2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
@ 2024-10-21 15:28 ` Daniel Borkmann
  2024-10-22  0:46   ` Kumar Kartikeya Dwivedi
  2024-10-21 15:28 ` [PATCH bpf 3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers Daniel Borkmann
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Daniel Borkmann @ 2024-10-21 15:28 UTC (permalink / raw)
  To: ast; +Cc: andrii, kongln9170, memxor, bpf

Lonial reported an issue in the BPF verifier where check_mem_size_reg()
has the following code:

    if (!tnum_is_const(reg->var_off))
        /* For unprivileged variable accesses, disable raw
         * mode so that the program is required to
         * initialize all the memory that the helper could
         * just partially fill up.
         */
         meta = NULL;

This means that writes are not checked when the register containing the
size of the passed buffer has not a fixed size. Through this bug, a BPF
program can write to a map which is marked as read-only, for example,
.rodata global maps.

The problem is that MEM_UNINIT's initial meaning that "the passed buffer
to the BPF helper does not need to be initialized" which was added back
in commit 435faee1aae9 ("bpf, verifier: add ARG_PTR_TO_RAW_STACK type")
got overloaded over time with "the passed buffer is being written to".

The problem however is that checks such as the above which were added later
via 06c1c049721a ("bpf: allow helpers access to variable memory") set meta
to NULL in order force the user to always initialize the passed buffer to
the helper. Due to the current double meaning of MEM_UNINIT, this bypasses
verifier write checks to the memory (not boundary checks though) and only
assumes the latter memory is read instead.

Fix this by reverting MEM_UNINIT back to its original meaning, and having
MEM_WRITE as an annotation to BPF helpers in order to then trigger the
BPF verifier checks for writing to memory.

Some notes: check_arg_pair_ok() ensures that for ARG_CONST_SIZE{,_OR_ZERO}
we can access fn->arg_type[arg - 1] since it must contain a preceding
ARG_PTR_TO_MEM. For check_mem_reg() the meta argument can be removed
altogether since we do check both BPF_READ and BPF_WRITE. Same for the
equivalent check_kfunc_mem_size_reg().

Fixes: 7b3552d3f9f6 ("bpf: Reject writes for PTR_TO_MAP_KEY in check_helper_mem_access")
Fixes: 97e6d7dab1ca ("bpf: Check PTR_TO_MEM | MEM_RDONLY in check_helper_mem_access")
Fixes: 15baa55ff5b0 ("bpf/verifier: allow all functions to read user provided context")
Reported-by: Lonial Con <kongln9170@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 kernel/bpf/verifier.c | 73 +++++++++++++++++++++----------------------
 1 file changed, 35 insertions(+), 38 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 434de48cd24b..98d4e1aab624 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7432,7 +7432,8 @@ static int check_stack_range_initialized(
 }
 
 static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
-				   int access_size, bool zero_size_allowed,
+				   int access_size, enum bpf_access_type access_type,
+				   bool zero_size_allowed,
 				   struct bpf_call_arg_meta *meta)
 {
 	struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
@@ -7444,7 +7445,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 		return check_packet_access(env, regno, reg->off, access_size,
 					   zero_size_allowed);
 	case PTR_TO_MAP_KEY:
-		if (meta && meta->raw_mode) {
+		if (access_type == BPF_WRITE) {
 			verbose(env, "R%d cannot write into %s\n", regno,
 				reg_type_str(env, reg->type));
 			return -EACCES;
@@ -7452,15 +7453,13 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 		return check_mem_region_access(env, regno, reg->off, access_size,
 					       reg->map_ptr->key_size, false);
 	case PTR_TO_MAP_VALUE:
-		if (check_map_access_type(env, regno, reg->off, access_size,
-					  meta && meta->raw_mode ? BPF_WRITE :
-					  BPF_READ))
+		if (check_map_access_type(env, regno, reg->off, access_size, access_type))
 			return -EACCES;
 		return check_map_access(env, regno, reg->off, access_size,
 					zero_size_allowed, ACCESS_HELPER);
 	case PTR_TO_MEM:
 		if (type_is_rdonly_mem(reg->type)) {
-			if (meta && meta->raw_mode) {
+			if (access_type == BPF_WRITE) {
 				verbose(env, "R%d cannot write into %s\n", regno,
 					reg_type_str(env, reg->type));
 				return -EACCES;
@@ -7471,7 +7470,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 					       zero_size_allowed);
 	case PTR_TO_BUF:
 		if (type_is_rdonly_mem(reg->type)) {
-			if (meta && meta->raw_mode) {
+			if (access_type == BPF_WRITE) {
 				verbose(env, "R%d cannot write into %s\n", regno,
 					reg_type_str(env, reg->type));
 				return -EACCES;
@@ -7499,7 +7498,6 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 		 * Dynamically check it now.
 		 */
 		if (!env->ops->convert_ctx_access) {
-			enum bpf_access_type atype = meta && meta->raw_mode ? BPF_WRITE : BPF_READ;
 			int offset = access_size - 1;
 
 			/* Allow zero-byte read from PTR_TO_CTX */
@@ -7507,7 +7505,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 				return zero_size_allowed ? 0 : -EACCES;
 
 			return check_mem_access(env, env->insn_idx, regno, offset, BPF_B,
-						atype, -1, false, false);
+						access_type, -1, false, false);
 		}
 
 		fallthrough;
@@ -7532,6 +7530,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
  */
 static int check_mem_size_reg(struct bpf_verifier_env *env,
 			      struct bpf_reg_state *reg, u32 regno,
+			      enum bpf_access_type access_type,
 			      bool zero_size_allowed,
 			      struct bpf_call_arg_meta *meta)
 {
@@ -7547,15 +7546,12 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
 	 */
 	meta->msize_max_value = reg->umax_value;
 
-	/* The register is SCALAR_VALUE; the access check
-	 * happens using its boundaries.
+	/* The register is SCALAR_VALUE; the access check happens using
+	 * its boundaries. For unprivileged variable accesses, disable
+	 * raw mode so that the program is required to initialize all
+	 * the memory that the helper could just partially fill up.
 	 */
 	if (!tnum_is_const(reg->var_off))
-		/* For unprivileged variable accesses, disable raw
-		 * mode so that the program is required to
-		 * initialize all the memory that the helper could
-		 * just partially fill up.
-		 */
 		meta = NULL;
 
 	if (reg->smin_value < 0) {
@@ -7575,9 +7571,8 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
 			regno);
 		return -EACCES;
 	}
-	err = check_helper_mem_access(env, regno - 1,
-				      reg->umax_value,
-				      zero_size_allowed, meta);
+	err = check_helper_mem_access(env, regno - 1, reg->umax_value,
+				      access_type, zero_size_allowed, meta);
 	if (!err)
 		err = mark_chain_precision(env, regno);
 	return err;
@@ -7588,13 +7583,11 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
 {
 	bool may_be_null = type_may_be_null(reg->type);
 	struct bpf_reg_state saved_reg;
-	struct bpf_call_arg_meta meta;
 	int err;
 
 	if (register_is_null(reg))
 		return 0;
 
-	memset(&meta, 0, sizeof(meta));
 	/* 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.
@@ -7604,10 +7597,8 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg
 		mark_ptr_not_null_reg(reg);
 	}
 
-	err = check_helper_mem_access(env, regno, mem_size, true, &meta);
-	/* Check access for BPF_WRITE */
-	meta.raw_mode = true;
-	err = err ?: check_helper_mem_access(env, regno, mem_size, true, &meta);
+	err = check_helper_mem_access(env, regno, mem_size, BPF_READ, true, NULL);
+	err = err ?: check_helper_mem_access(env, regno, mem_size, BPF_WRITE, true, NULL);
 
 	if (may_be_null)
 		*reg = saved_reg;
@@ -7633,13 +7624,12 @@ static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg
 		mark_ptr_not_null_reg(mem_reg);
 	}
 
-	err = check_mem_size_reg(env, reg, regno, true, &meta);
-	/* Check access for BPF_WRITE */
-	meta.raw_mode = true;
-	err = err ?: check_mem_size_reg(env, reg, regno, true, &meta);
+	err = check_mem_size_reg(env, reg, regno, BPF_READ, true, &meta);
+	err = err ?: check_mem_size_reg(env, reg, regno, BPF_WRITE, true, &meta);
 
 	if (may_be_null)
 		*mem_reg = saved_reg;
+
 	return err;
 }
 
@@ -8942,9 +8932,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
 			verbose(env, "invalid map_ptr to access map->key\n");
 			return -EACCES;
 		}
-		err = check_helper_mem_access(env, regno,
-					      meta->map_ptr->key_size, false,
-					      NULL);
+		err = check_helper_mem_access(env, regno, meta->map_ptr->key_size,
+					      BPF_READ, false, NULL);
 		break;
 	case ARG_PTR_TO_MAP_VALUE:
 		if (type_may_be_null(arg_type) && register_is_null(reg))
@@ -8959,9 +8948,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
 			return -EACCES;
 		}
 		meta->raw_mode = arg_type & MEM_UNINIT;
-		err = check_helper_mem_access(env, regno,
-					      meta->map_ptr->value_size, false,
-					      meta);
+		err = check_helper_mem_access(env, regno, meta->map_ptr->value_size,
+					      arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
+					      false, meta);
 		break;
 	case ARG_PTR_TO_PERCPU_BTF_ID:
 		if (!reg->btf_id) {
@@ -9003,7 +8992,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
 		 */
 		meta->raw_mode = arg_type & MEM_UNINIT;
 		if (arg_type & MEM_FIXED_SIZE) {
-			err = check_helper_mem_access(env, regno, fn->arg_size[arg], false, meta);
+			err = check_helper_mem_access(env, regno, fn->arg_size[arg],
+						      arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
+						      false, meta);
 			if (err)
 				return err;
 			if (arg_type & MEM_ALIGNED)
@@ -9011,10 +9002,16 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
 		}
 		break;
 	case ARG_CONST_SIZE:
-		err = check_mem_size_reg(env, reg, regno, false, meta);
+		err = check_mem_size_reg(env, reg, regno,
+					 fn->arg_type[arg - 1] & MEM_WRITE ?
+					 BPF_WRITE : BPF_READ,
+					 false, meta);
 		break;
 	case ARG_CONST_SIZE_OR_ZERO:
-		err = check_mem_size_reg(env, reg, regno, true, meta);
+		err = check_mem_size_reg(env, reg, regno,
+					 fn->arg_type[arg - 1] & MEM_WRITE ?
+					 BPF_WRITE : BPF_READ,
+					 true, meta);
 		break;
 	case ARG_PTR_TO_DYNPTR:
 		err = process_dynptr_func(env, regno, insn_idx, arg_type, 0);
-- 
2.43.0


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

* [PATCH bpf 3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers
  2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
  2024-10-21 15:28 ` [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning Daniel Borkmann
@ 2024-10-21 15:28 ` Daniel Borkmann
  2024-10-22  0:46   ` Kumar Kartikeya Dwivedi
  2024-10-21 15:28 ` [PATCH bpf 4/5] selftests/bpf: Add test for writes to .rodata Daniel Borkmann
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Daniel Borkmann @ 2024-10-21 15:28 UTC (permalink / raw)
  To: ast; +Cc: andrii, kongln9170, memxor, bpf

We can now undo parts of 4b3786a6c539 ("bpf: Zero former ARG_PTR_TO_{LONG,INT}
args in case of error") as discussed in [0].

Given the BPF helpers now have MEM_WRITE tag, the MEM_UNINIT can be cleared.

The mtu_len is an input as well as output argument, meaning, the BPF program
has to set it to something. It cannot be uninitialized. Therefore, allowing
uninitialized memory and zeroing it on error would be odd. It was done as
an interim step in 4b3786a6c539 as the desired behavior could not have been
expressed before the introduction of MEM_WRITE tag.

Fixes: 4b3786a6c539 ("bpf: Zero former ARG_PTR_TO_{LONG,INT} args in case of error")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/a86eb76d-f52f-dee4-e5d2-87e45de3e16f@iogearbox.net [0]
---
 net/core/filter.c | 42 +++++++++++++++---------------------------
 1 file changed, 15 insertions(+), 27 deletions(-)

diff --git a/net/core/filter.c b/net/core/filter.c
index 6be0c0b86049..26cc64f99d6a 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -6281,24 +6281,16 @@ BPF_CALL_5(bpf_skb_check_mtu, struct sk_buff *, skb,
 {
 	int ret = BPF_MTU_CHK_RET_FRAG_NEEDED;
 	struct net_device *dev = skb->dev;
-	int skb_len, dev_len;
-	int mtu = 0;
+	int mtu, dev_len, skb_len;
 
-	if (unlikely(flags & ~(BPF_MTU_CHK_SEGS))) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	if (unlikely(flags & BPF_MTU_CHK_SEGS && (len_diff || *mtu_len))) {
-		ret = -EINVAL;
-		goto out;
-	}
+	if (unlikely(flags & ~(BPF_MTU_CHK_SEGS)))
+		return -EINVAL;
+	if (unlikely(flags & BPF_MTU_CHK_SEGS && (len_diff || *mtu_len)))
+		return -EINVAL;
 
 	dev = __dev_via_ifindex(dev, ifindex);
-	if (unlikely(!dev)) {
-		ret = -ENODEV;
-		goto out;
-	}
+	if (unlikely(!dev))
+		return -ENODEV;
 
 	mtu = READ_ONCE(dev->mtu);
 	dev_len = mtu + dev->hard_header_len;
@@ -6333,19 +6325,15 @@ BPF_CALL_5(bpf_xdp_check_mtu, struct xdp_buff *, xdp,
 	struct net_device *dev = xdp->rxq->dev;
 	int xdp_len = xdp->data_end - xdp->data;
 	int ret = BPF_MTU_CHK_RET_SUCCESS;
-	int mtu = 0, dev_len;
+	int mtu, dev_len;
 
 	/* XDP variant doesn't support multi-buffer segment check (yet) */
-	if (unlikely(flags)) {
-		ret = -EINVAL;
-		goto out;
-	}
+	if (unlikely(flags))
+		return -EINVAL;
 
 	dev = __dev_via_ifindex(dev, ifindex);
-	if (unlikely(!dev)) {
-		ret = -ENODEV;
-		goto out;
-	}
+	if (unlikely(!dev))
+		return -ENODEV;
 
 	mtu = READ_ONCE(dev->mtu);
 	dev_len = mtu + dev->hard_header_len;
@@ -6357,7 +6345,7 @@ BPF_CALL_5(bpf_xdp_check_mtu, struct xdp_buff *, xdp,
 	xdp_len += len_diff; /* minus result pass check */
 	if (xdp_len > dev_len)
 		ret = BPF_MTU_CHK_RET_FRAG_NEEDED;
-out:
+
 	*mtu_len = mtu;
 	return ret;
 }
@@ -6368,7 +6356,7 @@ static const struct bpf_func_proto bpf_skb_check_mtu_proto = {
 	.ret_type	= RET_INTEGER,
 	.arg1_type      = ARG_PTR_TO_CTX,
 	.arg2_type      = ARG_ANYTHING,
-	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
+	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_WRITE | MEM_ALIGNED,
 	.arg3_size	= sizeof(u32),
 	.arg4_type      = ARG_ANYTHING,
 	.arg5_type      = ARG_ANYTHING,
@@ -6380,7 +6368,7 @@ static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {
 	.ret_type	= RET_INTEGER,
 	.arg1_type      = ARG_PTR_TO_CTX,
 	.arg2_type      = ARG_ANYTHING,
-	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
+	.arg3_type      = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_WRITE | MEM_ALIGNED,
 	.arg3_size	= sizeof(u32),
 	.arg4_type      = ARG_ANYTHING,
 	.arg5_type      = ARG_ANYTHING,
-- 
2.43.0


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

* [PATCH bpf 4/5] selftests/bpf: Add test for writes to .rodata
  2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
  2024-10-21 15:28 ` [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning Daniel Borkmann
  2024-10-21 15:28 ` [PATCH bpf 3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers Daniel Borkmann
@ 2024-10-21 15:28 ` Daniel Borkmann
  2024-10-22  0:47   ` Kumar Kartikeya Dwivedi
  2024-10-21 15:28 ` [PATCH bpf 5/5] selftests/bpf: Add test for passing in uninit mtu_len Daniel Borkmann
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Daniel Borkmann @ 2024-10-21 15:28 UTC (permalink / raw)
  To: ast; +Cc: andrii, kongln9170, memxor, bpf

Add a small test to write a (verification-time) fixed vs unknown but
bounded-sized buffer into .rodata BPF map and assert that both get
rejected.

  # ./vmtest.sh -- ./test_progs -t verifier_const
  [...]
  ./test_progs -t verifier_const
  [    1.418717] tsc: Refined TSC clocksource calibration: 3407.994 MHz
  [    1.419113] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fcde90a1, max_idle_ns: 440795222066 ns
  [    1.419972] clocksource: Switched to clocksource tsc
  [    1.449596] bpf_testmod: loading out-of-tree module taints kernel.
  [    1.449958] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel
  #475/1   verifier_const/rodata/strtol: write rejected:OK
  #475/2   verifier_const/bss/strtol: write accepted:OK
  #475/3   verifier_const/data/strtol: write accepted:OK
  #475/4   verifier_const/rodata/mtu: write rejected:OK
  #475/5   verifier_const/bss/mtu: write accepted:OK
  #475/6   verifier_const/data/mtu: write accepted:OK
  #475/7   verifier_const/rodata/mark: write with unknown reg rejected:OK
  #475/8   verifier_const/rodata/mark: write with unknown reg rejected:OK
  #475     verifier_const:OK
  #476/1   verifier_const_or/constant register |= constant should keep constant type:OK
  #476/2   verifier_const_or/constant register |= constant should not bypass stack boundary checks:OK
  #476/3   verifier_const_or/constant register |= constant register should keep constant type:OK
  #476/4   verifier_const_or/constant register |= constant register should not bypass stack boundary checks:OK
  #476     verifier_const_or:OK
  Summary: 2/12 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 .../selftests/bpf/progs/verifier_const.c      | 31 ++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/progs/verifier_const.c b/tools/testing/selftests/bpf/progs/verifier_const.c
index 2e533d7eec2f..e118dbb768bf 100644
--- a/tools/testing/selftests/bpf/progs/verifier_const.c
+++ b/tools/testing/selftests/bpf/progs/verifier_const.c
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2024 Isovalent */
 
-#include <linux/bpf.h>
+#include "vmlinux.h"
 #include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
 #include "bpf_misc.h"
 
 const volatile long foo = 42;
@@ -66,4 +67,32 @@ int tcx6(struct __sk_buff *skb)
 	return TCX_PASS;
 }
 
+static inline void write_fixed(volatile void *p, __u32 val)
+{
+	*(volatile __u32 *)p = val;
+}
+
+static inline void write_dyn(void *p, void *val, int len)
+{
+	bpf_copy_from_user(p, len, val);
+}
+
+SEC("tc/ingress")
+__description("rodata/mark: write with unknown reg rejected")
+__failure __msg("write into map forbidden")
+int tcx7(struct __sk_buff *skb)
+{
+	write_fixed((void *)&foo, skb->mark);
+	return TCX_PASS;
+}
+
+SEC("lsm.s/bprm_committed_creds")
+__description("rodata/mark: write with unknown reg rejected")
+__failure __msg("write into map forbidden")
+int BPF_PROG(bprm, struct linux_binprm *bprm)
+{
+	write_dyn((void *)&foo, &bart, bpf_get_prandom_u32() & 3);
+	return 0;
+}
+
 char LICENSE[] SEC("license") = "GPL";
-- 
2.43.0


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

* [PATCH bpf 5/5] selftests/bpf: Add test for passing in uninit mtu_len
  2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
                   ` (2 preceding siblings ...)
  2024-10-21 15:28 ` [PATCH bpf 4/5] selftests/bpf: Add test for writes to .rodata Daniel Borkmann
@ 2024-10-21 15:28 ` Daniel Borkmann
  2024-10-22  0:47   ` Kumar Kartikeya Dwivedi
  2024-10-22  0:46 ` [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Kumar Kartikeya Dwivedi
  2024-10-22 22:50 ` patchwork-bot+netdevbpf
  5 siblings, 1 reply; 11+ messages in thread
From: Daniel Borkmann @ 2024-10-21 15:28 UTC (permalink / raw)
  To: ast; +Cc: andrii, kongln9170, memxor, bpf

Add a small test to pass an uninitialized mtu_len to the bpf_check_mtu()
helper to probe whether the verifier rejects it under !CAP_PERFMON.

  # ./vmtest.sh -- ./test_progs -t verifier_mtu
  [...]
  ./test_progs -t verifier_mtu
  [    1.414712] tsc: Refined TSC clocksource calibration: 3407.993 MHz
  [    1.415327] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fcd52370, max_idle_ns: 440795242006 ns
  [    1.416463] clocksource: Switched to clocksource tsc
  [    1.429842] bpf_testmod: loading out-of-tree module taints kernel.
  [    1.430283] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel
  #510/1   verifier_mtu/uninit/mtu: write rejected:OK
  #510     verifier_mtu:OK
  Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 .../selftests/bpf/prog_tests/verifier.c       | 19 +++++++++++++++++++
 .../selftests/bpf/progs/verifier_mtu.c        | 18 ++++++++++++++++++
 2 files changed, 37 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_mtu.c

diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index e26b5150fc43..4a6ecdcfa6a0 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -53,6 +53,7 @@
 #include "verifier_masking.skel.h"
 #include "verifier_meta_access.skel.h"
 #include "verifier_movsx.skel.h"
+#include "verifier_mtu.skel.h"
 #include "verifier_netfilter_ctx.skel.h"
 #include "verifier_netfilter_retcode.skel.h"
 #include "verifier_bpf_fastcall.skel.h"
@@ -221,6 +222,24 @@ void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_pack
 void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
 void test_verifier_lsm(void)                  { RUN(verifier_lsm); }
 
+void test_verifier_mtu(void)
+{
+	__u64 caps = 0;
+	int ret;
+
+	/* In case CAP_BPF and CAP_PERFMON is not set */
+	ret = cap_enable_effective(1ULL << CAP_BPF | 1ULL << CAP_NET_ADMIN, &caps);
+	if (!ASSERT_OK(ret, "set_cap_bpf_cap_net_admin"))
+		return;
+	ret = cap_disable_effective(1ULL << CAP_SYS_ADMIN | 1ULL << CAP_PERFMON, NULL);
+	if (!ASSERT_OK(ret, "disable_cap_sys_admin"))
+		goto restore_cap;
+	RUN(verifier_mtu);
+restore_cap:
+	if (caps)
+		cap_enable_effective(caps, NULL);
+}
+
 static int init_test_val_map(struct bpf_object *obj, char *map_name)
 {
 	struct test_val value = {
diff --git a/tools/testing/selftests/bpf/progs/verifier_mtu.c b/tools/testing/selftests/bpf/progs/verifier_mtu.c
new file mode 100644
index 000000000000..70c7600a26a0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_mtu.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+SEC("tc/ingress")
+__description("uninit/mtu: write rejected")
+__failure __msg("invalid indirect read from stack")
+int tc_uninit_mtu(struct __sk_buff *ctx)
+{
+	__u32 mtu;
+
+	bpf_check_mtu(ctx, 0, &mtu, 0, 0);
+	return TCX_PASS;
+}
+
+char LICENSE[] SEC("license") = "GPL";
-- 
2.43.0


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

* Re: [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute
  2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
                   ` (3 preceding siblings ...)
  2024-10-21 15:28 ` [PATCH bpf 5/5] selftests/bpf: Add test for passing in uninit mtu_len Daniel Borkmann
@ 2024-10-22  0:46 ` Kumar Kartikeya Dwivedi
  2024-10-22 22:50 ` patchwork-bot+netdevbpf
  5 siblings, 0 replies; 11+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2024-10-22  0:46 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, andrii, kongln9170, bpf

On Mon, 21 Oct 2024 at 17:28, Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> Add a MEM_WRITE attribute for BPF helper functions which can be used in
> bpf_func_proto to annotate an argument type in order to let the verifier
> know that the helper writes into the memory passed as an argument. In
> the past MEM_UNINIT has been (ab)used for this function, but the latter
> merely tells the verifier that the passed memory can be uninitialized.
>
> There have been bugs with overloading the latter but aside from that
> there are also cases where the passed memory is read + written which
> currently cannot be expressed, see also 4b3786a6c539 ("bpf: Zero former
> ARG_PTR_TO_{LONG,INT} args in case of error").
>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---

Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>

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

* Re: [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning
  2024-10-21 15:28 ` [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning Daniel Borkmann
@ 2024-10-22  0:46   ` Kumar Kartikeya Dwivedi
  0 siblings, 0 replies; 11+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2024-10-22  0:46 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, andrii, kongln9170, bpf

On Mon, 21 Oct 2024 at 17:28, Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> Lonial reported an issue in the BPF verifier where check_mem_size_reg()
> has the following code:
>
>     if (!tnum_is_const(reg->var_off))
>         /* For unprivileged variable accesses, disable raw
>          * mode so that the program is required to
>          * initialize all the memory that the helper could
>          * just partially fill up.
>          */
>          meta = NULL;
>
> This means that writes are not checked when the register containing the
> size of the passed buffer has not a fixed size. Through this bug, a BPF
> program can write to a map which is marked as read-only, for example,
> .rodata global maps.
>
> The problem is that MEM_UNINIT's initial meaning that "the passed buffer
> to the BPF helper does not need to be initialized" which was added back
> in commit 435faee1aae9 ("bpf, verifier: add ARG_PTR_TO_RAW_STACK type")
> got overloaded over time with "the passed buffer is being written to".
>
> The problem however is that checks such as the above which were added later
> via 06c1c049721a ("bpf: allow helpers access to variable memory") set meta
> to NULL in order force the user to always initialize the passed buffer to
> the helper. Due to the current double meaning of MEM_UNINIT, this bypasses
> verifier write checks to the memory (not boundary checks though) and only
> assumes the latter memory is read instead.
>
> Fix this by reverting MEM_UNINIT back to its original meaning, and having
> MEM_WRITE as an annotation to BPF helpers in order to then trigger the
> BPF verifier checks for writing to memory.
>
> Some notes: check_arg_pair_ok() ensures that for ARG_CONST_SIZE{,_OR_ZERO}
> we can access fn->arg_type[arg - 1] since it must contain a preceding
> ARG_PTR_TO_MEM. For check_mem_reg() the meta argument can be removed
> altogether since we do check both BPF_READ and BPF_WRITE. Same for the
> equivalent check_kfunc_mem_size_reg().
>
> Fixes: 7b3552d3f9f6 ("bpf: Reject writes for PTR_TO_MAP_KEY in check_helper_mem_access")
> Fixes: 97e6d7dab1ca ("bpf: Check PTR_TO_MEM | MEM_RDONLY in check_helper_mem_access")
> Fixes: 15baa55ff5b0 ("bpf/verifier: allow all functions to read user provided context")
> Reported-by: Lonial Con <kongln9170@gmail.com>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---

Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>

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

* Re: [PATCH bpf 3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers
  2024-10-21 15:28 ` [PATCH bpf 3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers Daniel Borkmann
@ 2024-10-22  0:46   ` Kumar Kartikeya Dwivedi
  0 siblings, 0 replies; 11+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2024-10-22  0:46 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, andrii, kongln9170, bpf

On Mon, 21 Oct 2024 at 17:28, Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> We can now undo parts of 4b3786a6c539 ("bpf: Zero former ARG_PTR_TO_{LONG,INT}
> args in case of error") as discussed in [0].
>
> Given the BPF helpers now have MEM_WRITE tag, the MEM_UNINIT can be cleared.
>
> The mtu_len is an input as well as output argument, meaning, the BPF program
> has to set it to something. It cannot be uninitialized. Therefore, allowing
> uninitialized memory and zeroing it on error would be odd. It was done as
> an interim step in 4b3786a6c539 as the desired behavior could not have been
> expressed before the introduction of MEM_WRITE tag.
>
> Fixes: 4b3786a6c539 ("bpf: Zero former ARG_PTR_TO_{LONG,INT} args in case of error")
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> Link: https://lore.kernel.org/bpf/a86eb76d-f52f-dee4-e5d2-87e45de3e16f@iogearbox.net [0]
> ---

Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>

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

* Re: [PATCH bpf 4/5] selftests/bpf: Add test for writes to .rodata
  2024-10-21 15:28 ` [PATCH bpf 4/5] selftests/bpf: Add test for writes to .rodata Daniel Borkmann
@ 2024-10-22  0:47   ` Kumar Kartikeya Dwivedi
  0 siblings, 0 replies; 11+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2024-10-22  0:47 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, andrii, kongln9170, bpf

On Mon, 21 Oct 2024 at 17:28, Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> Add a small test to write a (verification-time) fixed vs unknown but
> bounded-sized buffer into .rodata BPF map and assert that both get
> rejected.
>
>   # ./vmtest.sh -- ./test_progs -t verifier_const
>   [...]
>   ./test_progs -t verifier_const
>   [    1.418717] tsc: Refined TSC clocksource calibration: 3407.994 MHz
>   [    1.419113] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fcde90a1, max_idle_ns: 440795222066 ns
>   [    1.419972] clocksource: Switched to clocksource tsc
>   [    1.449596] bpf_testmod: loading out-of-tree module taints kernel.
>   [    1.449958] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel
>   #475/1   verifier_const/rodata/strtol: write rejected:OK
>   #475/2   verifier_const/bss/strtol: write accepted:OK
>   #475/3   verifier_const/data/strtol: write accepted:OK
>   #475/4   verifier_const/rodata/mtu: write rejected:OK
>   #475/5   verifier_const/bss/mtu: write accepted:OK
>   #475/6   verifier_const/data/mtu: write accepted:OK
>   #475/7   verifier_const/rodata/mark: write with unknown reg rejected:OK
>   #475/8   verifier_const/rodata/mark: write with unknown reg rejected:OK
>   #475     verifier_const:OK
>   #476/1   verifier_const_or/constant register |= constant should keep constant type:OK
>   #476/2   verifier_const_or/constant register |= constant should not bypass stack boundary checks:OK
>   #476/3   verifier_const_or/constant register |= constant register should keep constant type:OK
>   #476/4   verifier_const_or/constant register |= constant register should not bypass stack boundary checks:OK
>   #476     verifier_const_or:OK
>   Summary: 2/12 PASSED, 0 SKIPPED, 0 FAILED
>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---

Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>

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

* Re: [PATCH bpf 5/5] selftests/bpf: Add test for passing in uninit mtu_len
  2024-10-21 15:28 ` [PATCH bpf 5/5] selftests/bpf: Add test for passing in uninit mtu_len Daniel Borkmann
@ 2024-10-22  0:47   ` Kumar Kartikeya Dwivedi
  0 siblings, 0 replies; 11+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2024-10-22  0:47 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, andrii, kongln9170, bpf

On Mon, 21 Oct 2024 at 17:28, Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> Add a small test to pass an uninitialized mtu_len to the bpf_check_mtu()
> helper to probe whether the verifier rejects it under !CAP_PERFMON.
>
>   # ./vmtest.sh -- ./test_progs -t verifier_mtu
>   [...]
>   ./test_progs -t verifier_mtu
>   [    1.414712] tsc: Refined TSC clocksource calibration: 3407.993 MHz
>   [    1.415327] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fcd52370, max_idle_ns: 440795242006 ns
>   [    1.416463] clocksource: Switched to clocksource tsc
>   [    1.429842] bpf_testmod: loading out-of-tree module taints kernel.
>   [    1.430283] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel
>   #510/1   verifier_mtu/uninit/mtu: write rejected:OK
>   #510     verifier_mtu:OK
>   Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED
>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---

Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>

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

* Re: [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute
  2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
                   ` (4 preceding siblings ...)
  2024-10-22  0:46 ` [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Kumar Kartikeya Dwivedi
@ 2024-10-22 22:50 ` patchwork-bot+netdevbpf
  5 siblings, 0 replies; 11+ messages in thread
From: patchwork-bot+netdevbpf @ 2024-10-22 22:50 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: ast, andrii, kongln9170, memxor, bpf

Hello:

This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Mon, 21 Oct 2024 17:28:05 +0200 you wrote:
> Add a MEM_WRITE attribute for BPF helper functions which can be used in
> bpf_func_proto to annotate an argument type in order to let the verifier
> know that the helper writes into the memory passed as an argument. In
> the past MEM_UNINIT has been (ab)used for this function, but the latter
> merely tells the verifier that the passed memory can be uninitialized.
> 
> There have been bugs with overloading the latter but aside from that
> there are also cases where the passed memory is read + written which
> currently cannot be expressed, see also 4b3786a6c539 ("bpf: Zero former
> ARG_PTR_TO_{LONG,INT} args in case of error").
> 
> [...]

Here is the summary with links:
  - [bpf,1/5] bpf: Add MEM_WRITE attribute
    https://git.kernel.org/bpf/bpf/c/6fad274f06f0
  - [bpf,2/5] bpf: Fix overloading of MEM_UNINIT's meaning
    https://git.kernel.org/bpf/bpf/c/8ea607330a39
  - [bpf,3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers
    https://git.kernel.org/bpf/bpf/c/14a3d3ef02ba
  - [bpf,4/5] selftests/bpf: Add test for writes to .rodata
    https://git.kernel.org/bpf/bpf/c/baa802d2aa5c
  - [bpf,5/5] selftests/bpf: Add test for passing in uninit mtu_len
    https://git.kernel.org/bpf/bpf/c/82bbe133312b

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] 11+ messages in thread

end of thread, other threads:[~2024-10-22 22:50 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-21 15:28 [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Daniel Borkmann
2024-10-21 15:28 ` [PATCH bpf 2/5] bpf: Fix overloading of MEM_UNINIT's meaning Daniel Borkmann
2024-10-22  0:46   ` Kumar Kartikeya Dwivedi
2024-10-21 15:28 ` [PATCH bpf 3/5] bpf: Remove MEM_UNINIT from skb/xdp MTU helpers Daniel Borkmann
2024-10-22  0:46   ` Kumar Kartikeya Dwivedi
2024-10-21 15:28 ` [PATCH bpf 4/5] selftests/bpf: Add test for writes to .rodata Daniel Borkmann
2024-10-22  0:47   ` Kumar Kartikeya Dwivedi
2024-10-21 15:28 ` [PATCH bpf 5/5] selftests/bpf: Add test for passing in uninit mtu_len Daniel Borkmann
2024-10-22  0:47   ` Kumar Kartikeya Dwivedi
2024-10-22  0:46 ` [PATCH bpf 1/5] bpf: Add MEM_WRITE attribute Kumar Kartikeya Dwivedi
2024-10-22 22:50 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox