| From 6fad274f06f038c29660aa53fbad14241c9fd976 Mon Sep 17 00:00:00 2001 |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| Date: Mon, 21 Oct 2024 17:28:05 +0200 |
| Subject: bpf: Add MEM_WRITE attribute |
| |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| |
| commit 6fad274f06f038c29660aa53fbad14241c9fd976 upstream. |
| |
| 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> |
| Link: https://lore.kernel.org/r/20241021152809.33343-1-daniel@iogearbox.net |
| Signed-off-by: Alexei Starovoitov <ast@kernel.org> |
| Signed-off-by: BRUNO VERNAY <bruno.vernay@se.com> |
| Signed-off-by: Hugo SIMELIERE <hsimeliere.opensource@witekio.com> |
| Stable-dep-of: 8ea607330a39 ("bpf: Fix overloading of MEM_UNINIT's meaning") |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| 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(-) |
| |
| --- a/include/linux/bpf.h |
| +++ b/include/linux/bpf.h |
| @@ -464,6 +464,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. */ |
| @@ -480,6 +481,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, |
| }; |
| @@ -537,10 +545,10 @@ enum bpf_arg_type { |
| ARG_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_ALLOC_MEM, |
| 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, |
| |
| --- a/kernel/bpf/helpers.c |
| +++ b/kernel/bpf/helpers.c |
| @@ -107,7 +107,7 @@ const struct bpf_func_proto bpf_map_pop_ |
| .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) |
| @@ -120,7 +120,7 @@ const struct bpf_func_proto bpf_map_peek |
| .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) |
| @@ -531,7 +531,7 @@ const struct bpf_func_proto bpf_strtol_p |
| .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), |
| }; |
| |
| @@ -561,7 +561,7 @@ const struct bpf_func_proto bpf_strtoul_ |
| .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), |
| }; |
| |
| @@ -1502,7 +1502,7 @@ static const struct bpf_func_proto bpf_d |
| .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, struct bpf_dynptr_kern *, src, |
| --- a/kernel/bpf/ringbuf.c |
| +++ b/kernel/bpf/ringbuf.c |
| @@ -618,7 +618,7 @@ const struct bpf_func_proto bpf_ringbuf_ |
| .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) |
| --- a/kernel/bpf/syscall.c |
| +++ b/kernel/bpf/syscall.c |
| @@ -5265,7 +5265,7 @@ static const struct bpf_func_proto bpf_k |
| .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), |
| }; |
| |
| --- a/kernel/trace/bpf_trace.c |
| +++ b/kernel/trace/bpf_trace.c |
| @@ -1192,7 +1192,7 @@ static const struct bpf_func_proto bpf_g |
| .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), |
| }; |
| |
| @@ -1209,7 +1209,7 @@ static const struct bpf_func_proto bpf_g |
| .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), |
| }; |
| |
| --- a/net/core/filter.c |
| +++ b/net/core/filter.c |
| @@ -6243,7 +6243,7 @@ static const struct bpf_func_proto bpf_s |
| .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, |
| @@ -6255,7 +6255,7 @@ static const struct bpf_func_proto bpf_x |
| .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, |