Skip to content

Commit c61bcd2

Browse files
mykyta5Alexei Starovoitov
authored andcommitted
selftests/bpf: introduce tests for dynptr copy kfuncs
Introduce selftests verifying newly-added dynptr copy kfuncs. Covering contiguous and non-contiguous memory backed dynptrs. Disable test_probe_read_user_str_dynptr that triggers bug in strncpy_from_user_nofault. Patch to fix the issue [1]. [1] https://patchwork.kernel.org/project/linux-mm/patch/[email protected]/ Acked-by: Andrii Nakryiko <[email protected]> Signed-off-by: Mykyta Yatsenko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent a498ee7 commit c61bcd2

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed

tools/testing/selftests/bpf/DENYLIST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# TEMPORARY
22
# Alphabetical order
3+
dynptr/test_probe_read_user_str_dynptr # disabled until https://patchwork.kernel.org/project/linux-mm/patch/[email protected]/ makes it into the bpf-next
34
get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge
45
stacktrace_build_id
56
stacktrace_build_id_nmi

tools/testing/selftests/bpf/prog_tests/dynptr.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ static struct {
3333
{"test_dynptr_skb_no_buff", SETUP_SKB_PROG},
3434
{"test_dynptr_skb_strcmp", SETUP_SKB_PROG},
3535
{"test_dynptr_skb_tp_btf", SETUP_SKB_PROG_TP},
36+
{"test_probe_read_user_dynptr", SETUP_XDP_PROG},
37+
{"test_probe_read_kernel_dynptr", SETUP_XDP_PROG},
38+
{"test_probe_read_user_str_dynptr", SETUP_XDP_PROG},
39+
{"test_probe_read_kernel_str_dynptr", SETUP_XDP_PROG},
40+
{"test_copy_from_user_dynptr", SETUP_SYSCALL_SLEEP},
41+
{"test_copy_from_user_str_dynptr", SETUP_SYSCALL_SLEEP},
42+
{"test_copy_from_user_task_dynptr", SETUP_SYSCALL_SLEEP},
43+
{"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP},
3644
};
3745

3846
static void verify_success(const char *prog_name, enum test_setup_type setup_type)
3947
{
48+
char user_data[384] = {[0 ... 382] = 'a', '\0'};
4049
struct dynptr_success *skel;
4150
struct bpf_program *prog;
4251
struct bpf_link *link;
@@ -58,6 +67,10 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ
5867
if (!ASSERT_OK(err, "dynptr_success__load"))
5968
goto cleanup;
6069

70+
skel->bss->user_ptr = user_data;
71+
skel->data->test_len[0] = sizeof(user_data);
72+
memcpy(skel->bss->expected_str, user_data, sizeof(user_data));
73+
6174
switch (setup_type) {
6275
case SETUP_SYSCALL_SLEEP:
6376
link = bpf_program__attach(prog);

tools/testing/selftests/bpf/progs/dynptr_success.c

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,3 +680,233 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp)
680680
bpf_ringbuf_discard_dynptr(&ptr_buf, 0);
681681
return XDP_DROP;
682682
}
683+
684+
void *user_ptr;
685+
/* Contains the copy of the data pointed by user_ptr.
686+
* Size 384 to make it not fit into a single kernel chunk when copying
687+
* but less than the maximum bpf stack size (512).
688+
*/
689+
char expected_str[384];
690+
__u32 test_len[7] = {0/* placeholder */, 0, 1, 2, 255, 256, 257};
691+
692+
typedef int (*bpf_read_dynptr_fn_t)(struct bpf_dynptr *dptr, u32 off,
693+
u32 size, const void *unsafe_ptr);
694+
695+
/* Returns the offset just before the end of the maximum sized xdp fragment.
696+
* Any write larger than 32 bytes will be split between 2 fragments.
697+
*/
698+
__u32 xdp_near_frag_end_offset(void)
699+
{
700+
const __u32 headroom = 256;
701+
const __u32 max_frag_size = __PAGE_SIZE - headroom - sizeof(struct skb_shared_info);
702+
703+
/* 32 bytes before the approximate end of the fragment */
704+
return max_frag_size - 32;
705+
}
706+
707+
/* Use __always_inline on test_dynptr_probe[_str][_xdp]() and callbacks
708+
* of type bpf_read_dynptr_fn_t to prevent compiler from generating
709+
* indirect calls that make program fail to load with "unknown opcode" error.
710+
*/
711+
static __always_inline void test_dynptr_probe(void *ptr, bpf_read_dynptr_fn_t bpf_read_dynptr_fn)
712+
{
713+
char buf[sizeof(expected_str)];
714+
struct bpf_dynptr ptr_buf;
715+
int i;
716+
717+
if (bpf_get_current_pid_tgid() >> 32 != pid)
718+
return;
719+
720+
err = bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(buf), 0, &ptr_buf);
721+
722+
bpf_for(i, 0, ARRAY_SIZE(test_len)) {
723+
__u32 len = test_len[i];
724+
725+
err = err ?: bpf_read_dynptr_fn(&ptr_buf, 0, test_len[i], ptr);
726+
if (len > sizeof(buf))
727+
break;
728+
err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0);
729+
730+
if (err || bpf_memcmp(expected_str, buf, len))
731+
err = 1;
732+
733+
/* Reset buffer and dynptr */
734+
__builtin_memset(buf, 0, sizeof(buf));
735+
err = err ?: bpf_dynptr_write(&ptr_buf, 0, buf, len, 0);
736+
}
737+
bpf_ringbuf_discard_dynptr(&ptr_buf, 0);
738+
}
739+
740+
static __always_inline void test_dynptr_probe_str(void *ptr,
741+
bpf_read_dynptr_fn_t bpf_read_dynptr_fn)
742+
{
743+
char buf[sizeof(expected_str)];
744+
struct bpf_dynptr ptr_buf;
745+
__u32 cnt, i;
746+
747+
if (bpf_get_current_pid_tgid() >> 32 != pid)
748+
return;
749+
750+
bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(buf), 0, &ptr_buf);
751+
752+
bpf_for(i, 0, ARRAY_SIZE(test_len)) {
753+
__u32 len = test_len[i];
754+
755+
cnt = bpf_read_dynptr_fn(&ptr_buf, 0, len, ptr);
756+
if (cnt != len)
757+
err = 1;
758+
759+
if (len > sizeof(buf))
760+
continue;
761+
err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0);
762+
if (!len)
763+
continue;
764+
if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0')
765+
err = 1;
766+
}
767+
bpf_ringbuf_discard_dynptr(&ptr_buf, 0);
768+
}
769+
770+
static __always_inline void test_dynptr_probe_xdp(struct xdp_md *xdp, void *ptr,
771+
bpf_read_dynptr_fn_t bpf_read_dynptr_fn)
772+
{
773+
struct bpf_dynptr ptr_xdp;
774+
char buf[sizeof(expected_str)];
775+
__u32 off, i;
776+
777+
if (bpf_get_current_pid_tgid() >> 32 != pid)
778+
return;
779+
780+
off = xdp_near_frag_end_offset();
781+
err = bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp);
782+
783+
bpf_for(i, 0, ARRAY_SIZE(test_len)) {
784+
__u32 len = test_len[i];
785+
786+
err = err ?: bpf_read_dynptr_fn(&ptr_xdp, off, len, ptr);
787+
if (len > sizeof(buf))
788+
continue;
789+
err = err ?: bpf_dynptr_read(&buf, len, &ptr_xdp, off, 0);
790+
if (err || bpf_memcmp(expected_str, buf, len))
791+
err = 1;
792+
/* Reset buffer and dynptr */
793+
__builtin_memset(buf, 0, sizeof(buf));
794+
err = err ?: bpf_dynptr_write(&ptr_xdp, off, buf, len, 0);
795+
}
796+
}
797+
798+
static __always_inline void test_dynptr_probe_str_xdp(struct xdp_md *xdp, void *ptr,
799+
bpf_read_dynptr_fn_t bpf_read_dynptr_fn)
800+
{
801+
struct bpf_dynptr ptr_xdp;
802+
char buf[sizeof(expected_str)];
803+
__u32 cnt, off, i;
804+
805+
if (bpf_get_current_pid_tgid() >> 32 != pid)
806+
return;
807+
808+
off = xdp_near_frag_end_offset();
809+
err = bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp);
810+
if (err)
811+
return;
812+
813+
bpf_for(i, 0, ARRAY_SIZE(test_len)) {
814+
__u32 len = test_len[i];
815+
816+
cnt = bpf_read_dynptr_fn(&ptr_xdp, off, len, ptr);
817+
if (cnt != len)
818+
err = 1;
819+
820+
if (len > sizeof(buf))
821+
continue;
822+
err = err ?: bpf_dynptr_read(&buf, len, &ptr_xdp, off, 0);
823+
824+
if (!len)
825+
continue;
826+
if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0')
827+
err = 1;
828+
829+
__builtin_memset(buf, 0, sizeof(buf));
830+
err = err ?: bpf_dynptr_write(&ptr_xdp, off, buf, len, 0);
831+
}
832+
}
833+
834+
SEC("xdp")
835+
int test_probe_read_user_dynptr(struct xdp_md *xdp)
836+
{
837+
test_dynptr_probe(user_ptr, bpf_probe_read_user_dynptr);
838+
if (!err)
839+
test_dynptr_probe_xdp(xdp, user_ptr, bpf_probe_read_user_dynptr);
840+
return XDP_PASS;
841+
}
842+
843+
SEC("xdp")
844+
int test_probe_read_kernel_dynptr(struct xdp_md *xdp)
845+
{
846+
test_dynptr_probe(expected_str, bpf_probe_read_kernel_dynptr);
847+
if (!err)
848+
test_dynptr_probe_xdp(xdp, expected_str, bpf_probe_read_kernel_dynptr);
849+
return XDP_PASS;
850+
}
851+
852+
SEC("xdp")
853+
int test_probe_read_user_str_dynptr(struct xdp_md *xdp)
854+
{
855+
test_dynptr_probe_str(user_ptr, bpf_probe_read_user_str_dynptr);
856+
if (!err)
857+
test_dynptr_probe_str_xdp(xdp, user_ptr, bpf_probe_read_user_str_dynptr);
858+
return XDP_PASS;
859+
}
860+
861+
SEC("xdp")
862+
int test_probe_read_kernel_str_dynptr(struct xdp_md *xdp)
863+
{
864+
test_dynptr_probe_str(expected_str, bpf_probe_read_kernel_str_dynptr);
865+
if (!err)
866+
test_dynptr_probe_str_xdp(xdp, expected_str, bpf_probe_read_kernel_str_dynptr);
867+
return XDP_PASS;
868+
}
869+
870+
SEC("fentry.s/" SYS_PREFIX "sys_nanosleep")
871+
int test_copy_from_user_dynptr(void *ctx)
872+
{
873+
test_dynptr_probe(user_ptr, bpf_copy_from_user_dynptr);
874+
return 0;
875+
}
876+
877+
SEC("fentry.s/" SYS_PREFIX "sys_nanosleep")
878+
int test_copy_from_user_str_dynptr(void *ctx)
879+
{
880+
test_dynptr_probe_str(user_ptr, bpf_copy_from_user_str_dynptr);
881+
return 0;
882+
}
883+
884+
static int bpf_copy_data_from_user_task(struct bpf_dynptr *dptr, u32 off,
885+
u32 size, const void *unsafe_ptr)
886+
{
887+
struct task_struct *task = bpf_get_current_task_btf();
888+
889+
return bpf_copy_from_user_task_dynptr(dptr, off, size, unsafe_ptr, task);
890+
}
891+
892+
static int bpf_copy_data_from_user_task_str(struct bpf_dynptr *dptr, u32 off,
893+
u32 size, const void *unsafe_ptr)
894+
{
895+
struct task_struct *task = bpf_get_current_task_btf();
896+
897+
return bpf_copy_from_user_task_str_dynptr(dptr, off, size, unsafe_ptr, task);
898+
}
899+
900+
SEC("fentry.s/" SYS_PREFIX "sys_nanosleep")
901+
int test_copy_from_user_task_dynptr(void *ctx)
902+
{
903+
test_dynptr_probe(user_ptr, bpf_copy_data_from_user_task);
904+
return 0;
905+
}
906+
907+
SEC("fentry.s/" SYS_PREFIX "sys_nanosleep")
908+
int test_copy_from_user_task_str_dynptr(void *ctx)
909+
{
910+
test_dynptr_probe_str(user_ptr, bpf_copy_data_from_user_task_str);
911+
return 0;
912+
}

0 commit comments

Comments
 (0)