Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'introduce-bpf_dynptr_copy-kfunc'

Mykyta Yatsenko says:

====================
introduce bpf_dynptr_copy kfunc

From: Mykyta Yatsenko <yatsenko@meta.com>

Introduce a new kfunc, bpf_dynptr_copy, which enables copying of
data from one dynptr to another. This functionality may be useful in
scenarios such as capturing XDP data to a ring buffer.
The patch set is split into 3 patches:
1. Refactor bpf_dynptr_read and bpf_dynptr_write by extracting code into
static functions, that allows calling them with no compiler warnings
2. Introduce bpf_dynptr_copy
3. Add tests for bpf_dynptr_copy

v2->v3:
* Implemented bpf_memcmp in dynptr_success.c test, as __builtin_memcmp
was not inlined on GCC-BPF.
====================

Link: https://patch.msgid.link/20250226183201.332713-1-mykyta.yatsenko5@gmail.com
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
ee2fdd02 b0f2ee60

+211 -9
+72 -4
kernel/bpf/helpers.c
··· 1759 1759 .arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT | MEM_WRITE, 1760 1760 }; 1761 1761 1762 - BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern *, src, 1763 - u32, offset, u64, flags) 1762 + static int __bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr_kern *src, 1763 + u32 offset, u64 flags) 1764 1764 { 1765 1765 enum bpf_dynptr_type type; 1766 1766 int err; ··· 1793 1793 } 1794 1794 } 1795 1795 1796 + BPF_CALL_5(bpf_dynptr_read, void *, dst, u32, len, const struct bpf_dynptr_kern *, src, 1797 + u32, offset, u64, flags) 1798 + { 1799 + return __bpf_dynptr_read(dst, len, src, offset, flags); 1800 + } 1801 + 1796 1802 static const struct bpf_func_proto bpf_dynptr_read_proto = { 1797 1803 .func = bpf_dynptr_read, 1798 1804 .gpl_only = false, ··· 1810 1804 .arg5_type = ARG_ANYTHING, 1811 1805 }; 1812 1806 1813 - BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, void *, src, 1814 - u32, len, u64, flags) 1807 + static int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src, 1808 + u32 len, u64 flags) 1815 1809 { 1816 1810 enum bpf_dynptr_type type; 1817 1811 int err; ··· 1847 1841 WARN_ONCE(true, "bpf_dynptr_write: unknown dynptr type %d\n", type); 1848 1842 return -EFAULT; 1849 1843 } 1844 + } 1845 + 1846 + BPF_CALL_5(bpf_dynptr_write, const struct bpf_dynptr_kern *, dst, u32, offset, void *, src, 1847 + u32, len, u64, flags) 1848 + { 1849 + return __bpf_dynptr_write(dst, offset, src, len, flags); 1850 1850 } 1851 1851 1852 1852 static const struct bpf_func_proto bpf_dynptr_write_proto = { ··· 2770 2758 return 0; 2771 2759 } 2772 2760 2761 + /** 2762 + * bpf_dynptr_copy() - Copy data from one dynptr to another. 2763 + * @dst_ptr: Destination dynptr - where data should be copied to 2764 + * @dst_off: Offset into the destination dynptr 2765 + * @src_ptr: Source dynptr - where data should be copied from 2766 + * @src_off: Offset into the source dynptr 2767 + * @size: Length of the data to copy from source to destination 2768 + * 2769 + * Copies data from source dynptr to destination dynptr. 2770 + * Returns 0 on success; negative error, otherwise. 2771 + */ 2772 + __bpf_kfunc int bpf_dynptr_copy(struct bpf_dynptr *dst_ptr, u32 dst_off, 2773 + struct bpf_dynptr *src_ptr, u32 src_off, u32 size) 2774 + { 2775 + struct bpf_dynptr_kern *dst = (struct bpf_dynptr_kern *)dst_ptr; 2776 + struct bpf_dynptr_kern *src = (struct bpf_dynptr_kern *)src_ptr; 2777 + void *src_slice, *dst_slice; 2778 + char buf[256]; 2779 + u32 off; 2780 + 2781 + src_slice = bpf_dynptr_slice(src_ptr, src_off, NULL, size); 2782 + dst_slice = bpf_dynptr_slice_rdwr(dst_ptr, dst_off, NULL, size); 2783 + 2784 + if (src_slice && dst_slice) { 2785 + memmove(dst_slice, src_slice, size); 2786 + return 0; 2787 + } 2788 + 2789 + if (src_slice) 2790 + return __bpf_dynptr_write(dst, dst_off, src_slice, size, 0); 2791 + 2792 + if (dst_slice) 2793 + return __bpf_dynptr_read(dst_slice, size, src, src_off, 0); 2794 + 2795 + if (bpf_dynptr_check_off_len(dst, dst_off, size) || 2796 + bpf_dynptr_check_off_len(src, src_off, size)) 2797 + return -E2BIG; 2798 + 2799 + off = 0; 2800 + while (off < size) { 2801 + u32 chunk_sz = min_t(u32, sizeof(buf), size - off); 2802 + int err; 2803 + 2804 + err = __bpf_dynptr_read(buf, chunk_sz, src, src_off + off, 0); 2805 + if (err) 2806 + return err; 2807 + err = __bpf_dynptr_write(dst, dst_off + off, buf, chunk_sz, 0); 2808 + if (err) 2809 + return err; 2810 + 2811 + off += chunk_sz; 2812 + } 2813 + return 0; 2814 + } 2815 + 2773 2816 __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj) 2774 2817 { 2775 2818 return obj; ··· 3273 3206 BTF_ID_FLAGS(func, bpf_dynptr_is_rdonly) 3274 3207 BTF_ID_FLAGS(func, bpf_dynptr_size) 3275 3208 BTF_ID_FLAGS(func, bpf_dynptr_clone) 3209 + BTF_ID_FLAGS(func, bpf_dynptr_copy) 3276 3210 #ifdef CONFIG_NET 3277 3211 BTF_ID_FLAGS(func, bpf_modify_return_test_tp) 3278 3212 #endif
+21
tools/testing/selftests/bpf/prog_tests/dynptr.c
··· 10 10 SETUP_SYSCALL_SLEEP, 11 11 SETUP_SKB_PROG, 12 12 SETUP_SKB_PROG_TP, 13 + SETUP_XDP_PROG, 13 14 }; 14 15 15 16 static struct { ··· 19 18 } success_tests[] = { 20 19 {"test_read_write", SETUP_SYSCALL_SLEEP}, 21 20 {"test_dynptr_data", SETUP_SYSCALL_SLEEP}, 21 + {"test_dynptr_copy", SETUP_SYSCALL_SLEEP}, 22 + {"test_dynptr_copy_xdp", SETUP_XDP_PROG}, 22 23 {"test_ringbuf", SETUP_SYSCALL_SLEEP}, 23 24 {"test_skb_readonly", SETUP_SKB_PROG}, 24 25 {"test_dynptr_skb_data", SETUP_SKB_PROG}, ··· 117 114 118 115 err = bpf_prog_test_run_opts(aux_prog_fd, &topts); 119 116 bpf_link__destroy(link); 117 + 118 + if (!ASSERT_OK(err, "test_run")) 119 + goto cleanup; 120 + 121 + break; 122 + } 123 + case SETUP_XDP_PROG: 124 + { 125 + char data[5000]; 126 + int err, prog_fd; 127 + LIBBPF_OPTS(bpf_test_run_opts, opts, 128 + .data_in = &data, 129 + .data_size_in = sizeof(data), 130 + .repeat = 1, 131 + ); 132 + 133 + prog_fd = bpf_program__fd(prog); 134 + err = bpf_prog_test_run_opts(prog_fd, &opts); 120 135 121 136 if (!ASSERT_OK(err, "test_run")) 122 137 goto cleanup;
+118 -5
tools/testing/selftests/bpf/progs/dynptr_success.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* Copyright (c) 2022 Facebook */ 3 3 4 + #include <vmlinux.h> 4 5 #include <string.h> 5 6 #include <stdbool.h> 6 - #include <linux/bpf.h> 7 7 #include <bpf/bpf_helpers.h> 8 8 #include <bpf/bpf_tracing.h> 9 9 #include "bpf_misc.h" 10 - #include "bpf_kfuncs.h" 11 10 #include "errno.h" 12 11 13 12 char _license[] SEC("license") = "GPL"; 14 13 15 14 int pid, err, val; 16 15 17 - struct sample { 16 + struct ringbuf_sample { 18 17 int pid; 19 18 int seq; 20 19 long value; ··· 120 121 121 122 static int ringbuf_callback(__u32 index, void *data) 122 123 { 123 - struct sample *sample; 124 + struct ringbuf_sample *sample; 124 125 125 126 struct bpf_dynptr *ptr = (struct bpf_dynptr *)data; 126 127 ··· 137 138 int test_ringbuf(void *ctx) 138 139 { 139 140 struct bpf_dynptr ptr; 140 - struct sample *sample; 141 + struct ringbuf_sample *sample; 141 142 142 143 if (bpf_get_current_pid_tgid() >> 32 != pid) 143 144 return 0; ··· 565 566 } 566 567 567 568 return 1; 569 + } 570 + 571 + static inline int bpf_memcmp(const char *a, const char *b, u32 size) 572 + { 573 + int i; 574 + 575 + bpf_for(i, 0, size) { 576 + if (a[i] != b[i]) 577 + return a[i] < b[i] ? -1 : 1; 578 + } 579 + return 0; 580 + } 581 + 582 + SEC("?tp/syscalls/sys_enter_nanosleep") 583 + int test_dynptr_copy(void *ctx) 584 + { 585 + char data[] = "hello there, world!!"; 586 + char buf[32] = {'\0'}; 587 + __u32 sz = sizeof(data); 588 + struct bpf_dynptr src, dst; 589 + 590 + bpf_ringbuf_reserve_dynptr(&ringbuf, sz, 0, &src); 591 + bpf_ringbuf_reserve_dynptr(&ringbuf, sz, 0, &dst); 592 + 593 + /* Test basic case of copying contiguous memory backed dynptrs */ 594 + err = bpf_dynptr_write(&src, 0, data, sz, 0); 595 + err = err ?: bpf_dynptr_copy(&dst, 0, &src, 0, sz); 596 + err = err ?: bpf_dynptr_read(buf, sz, &dst, 0, 0); 597 + err = err ?: bpf_memcmp(data, buf, sz); 598 + 599 + /* Test that offsets are handled correctly */ 600 + err = err ?: bpf_dynptr_copy(&dst, 3, &src, 5, sz - 5); 601 + err = err ?: bpf_dynptr_read(buf, sz - 5, &dst, 3, 0); 602 + err = err ?: bpf_memcmp(data + 5, buf, sz - 5); 603 + 604 + bpf_ringbuf_discard_dynptr(&src, 0); 605 + bpf_ringbuf_discard_dynptr(&dst, 0); 606 + return 0; 607 + } 608 + 609 + SEC("xdp") 610 + int test_dynptr_copy_xdp(struct xdp_md *xdp) 611 + { 612 + struct bpf_dynptr ptr_buf, ptr_xdp; 613 + char data[] = "qwertyuiopasdfghjkl"; 614 + char buf[32] = {'\0'}; 615 + __u32 len = sizeof(data); 616 + int i, chunks = 200; 617 + 618 + /* ptr_xdp is backed by non-contiguous memory */ 619 + bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp); 620 + bpf_ringbuf_reserve_dynptr(&ringbuf, len * chunks, 0, &ptr_buf); 621 + 622 + /* Destination dynptr is backed by non-contiguous memory */ 623 + bpf_for(i, 0, chunks) { 624 + err = bpf_dynptr_write(&ptr_buf, i * len, data, len, 0); 625 + if (err) 626 + goto out; 627 + } 628 + 629 + err = bpf_dynptr_copy(&ptr_xdp, 0, &ptr_buf, 0, len * chunks); 630 + if (err) 631 + goto out; 632 + 633 + bpf_for(i, 0, chunks) { 634 + __builtin_memset(buf, 0, sizeof(buf)); 635 + err = bpf_dynptr_read(&buf, len, &ptr_xdp, i * len, 0); 636 + if (err) 637 + goto out; 638 + if (bpf_memcmp(data, buf, len) != 0) 639 + goto out; 640 + } 641 + 642 + /* Source dynptr is backed by non-contiguous memory */ 643 + __builtin_memset(buf, 0, sizeof(buf)); 644 + bpf_for(i, 0, chunks) { 645 + err = bpf_dynptr_write(&ptr_buf, i * len, buf, len, 0); 646 + if (err) 647 + goto out; 648 + } 649 + 650 + err = bpf_dynptr_copy(&ptr_buf, 0, &ptr_xdp, 0, len * chunks); 651 + if (err) 652 + goto out; 653 + 654 + bpf_for(i, 0, chunks) { 655 + __builtin_memset(buf, 0, sizeof(buf)); 656 + err = bpf_dynptr_read(&buf, len, &ptr_buf, i * len, 0); 657 + if (err) 658 + goto out; 659 + if (bpf_memcmp(data, buf, len) != 0) 660 + goto out; 661 + } 662 + 663 + /* Both source and destination dynptrs are backed by non-contiguous memory */ 664 + err = bpf_dynptr_copy(&ptr_xdp, 2, &ptr_xdp, len, len * (chunks - 1)); 665 + if (err) 666 + goto out; 667 + 668 + bpf_for(i, 0, chunks - 1) { 669 + __builtin_memset(buf, 0, sizeof(buf)); 670 + err = bpf_dynptr_read(&buf, len, &ptr_xdp, 2 + i * len, 0); 671 + if (err) 672 + goto out; 673 + if (bpf_memcmp(data, buf, len) != 0) 674 + goto out; 675 + } 676 + 677 + if (bpf_dynptr_copy(&ptr_xdp, 2000, &ptr_xdp, 0, len * chunks) != -E2BIG) 678 + err = 1; 679 + 680 + out: 681 + bpf_ringbuf_discard_dynptr(&ptr_buf, 0); 682 + return XDP_DROP; 568 683 }