From: Mykyta Yatsenko <yatsenko@xxxxxxxx> Introducing bpf_dynptr_copy kfunc allowing copying data from one dynptr to another. This functionality is useful in scenarios such as capturing XDP data to a ring buffer. The implementation consists of 4 branches: * A fast branch for contiguous buffer capacity in both source and destination dynptrs * 3 branches utilizing __bpf_dynptr_read and __bpf_dynptr_write to copy data to/from non-contiguous buffer Signed-off-by: Mykyta Yatsenko <yatsenko@xxxxxxxx> --- kernel/bpf/helpers.c | 37 +++++++++++++++++++++++++++++++++++++ kernel/bpf/verifier.c | 3 +++ 2 files changed, 40 insertions(+) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 2833558c3009..ac5fbdfc504d 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2770,6 +2770,42 @@ __bpf_kfunc int bpf_dynptr_clone(const struct bpf_dynptr *p, return 0; } +__bpf_kfunc int bpf_dynptr_copy(struct bpf_dynptr *dst_ptr, u32 dst_off, + struct bpf_dynptr *src_ptr, u32 src_off, u32 size) +{ + struct bpf_dynptr_kern *dst = (struct bpf_dynptr_kern *)dst_ptr; + struct bpf_dynptr_kern *src = (struct bpf_dynptr_kern *)src_ptr; + __u8 *src_slice, *dst_slice; + int err = 0; + + src_slice = bpf_dynptr_slice(src_ptr, src_off, NULL, size); + dst_slice = bpf_dynptr_slice_rdwr(dst_ptr, dst_off, NULL, size); + + if (src_slice && dst_slice) { + memmove(dst_slice, src_slice, size); + } else if (src_slice) { + err = __bpf_dynptr_write(dst, dst_off, src_slice, size, 0); + } else if (dst_slice) { + err = __bpf_dynptr_read(dst_slice, size, src, src_off, 0); + } else { + u32 off = 0; + char buf[256]; + + if (bpf_dynptr_check_off_len(dst, dst_off, size) || + bpf_dynptr_check_off_len(src, src_off, size)) + return -E2BIG; + + while (err == 0 && off < size) { + u32 chunk_sz = min(sizeof(buf), size - off); + + err = err ?: __bpf_dynptr_read(buf, chunk_sz, src, src_off + off, 0); + err = err ?: __bpf_dynptr_write(dst, dst_off + off, buf, chunk_sz, 0); + off += chunk_sz; + } + } + return err; +} + __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj) { return obj; @@ -3174,6 +3210,7 @@ BTF_ID_FLAGS(func, bpf_dynptr_is_null) BTF_ID_FLAGS(func, bpf_dynptr_is_rdonly) BTF_ID_FLAGS(func, bpf_dynptr_size) BTF_ID_FLAGS(func, bpf_dynptr_clone) +BTF_ID_FLAGS(func, bpf_dynptr_copy) #ifdef CONFIG_NET BTF_ID_FLAGS(func, bpf_modify_return_test_tp) #endif diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e7bc74171c99..3c567bfcc582 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -11781,6 +11781,7 @@ enum special_kfunc_type { KF_bpf_dynptr_slice, KF_bpf_dynptr_slice_rdwr, KF_bpf_dynptr_clone, + KF_bpf_dynptr_copy, KF_bpf_percpu_obj_new_impl, KF_bpf_percpu_obj_drop_impl, KF_bpf_throw, @@ -11819,6 +11820,7 @@ BTF_ID(func, bpf_dynptr_from_xdp) BTF_ID(func, bpf_dynptr_slice) BTF_ID(func, bpf_dynptr_slice_rdwr) BTF_ID(func, bpf_dynptr_clone) +BTF_ID(func, bpf_dynptr_copy) BTF_ID(func, bpf_percpu_obj_new_impl) BTF_ID(func, bpf_percpu_obj_drop_impl) BTF_ID(func, bpf_throw) @@ -11857,6 +11859,7 @@ BTF_ID_UNUSED BTF_ID(func, bpf_dynptr_slice) BTF_ID(func, bpf_dynptr_slice_rdwr) BTF_ID(func, bpf_dynptr_clone) +BTF_ID(func, bpf_dynptr_copy) BTF_ID(func, bpf_percpu_obj_new_impl) BTF_ID(func, bpf_percpu_obj_drop_impl) BTF_ID(func, bpf_throw) -- 2.48.1