diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 6be41a44d688a..f6fa17ff188a4 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -55,8 +55,15 @@ static int bpf_test_finish(const union bpf_attr *kattr, { void __user *data_out = u64_to_user_ptr(kattr->test.data_out); int err = -EFAULT; + u32 copy_size = size; - if (data_out && copy_to_user(data_out, data, size)) + /* Clamp copy if the user has provided a size hint, but copy the full + * buffer if not to retain old behaviour. + */ + if (kattr->test.data_size_out && copy_size > kattr->test.data_size_out) + copy_size = kattr->test.data_size_out; + + if (data_out && copy_to_user(data_out, data, copy_size)) goto out; if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size))) goto out; diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 1d6907d379c99..61a130a49547b 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -272,10 +272,12 @@ int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size, attr.test.data_in = ptr_to_u64(data); attr.test.data_out = ptr_to_u64(data_out); attr.test.data_size_in = size; + if (data_out) + attr.test.data_size_out = *size_out; attr.test.repeat = repeat; ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr)); - if (size_out) + if (data_out) *size_out = attr.test.data_size_out; if (retval) *retval = attr.test.retval; diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 1903fb4f45d8d..3d855b29434b4 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -116,6 +116,40 @@ static void test_pkt_access(void) bpf_object__close(obj); } +static void test_output_size_hint(void) +{ + const char *file = "./test_pkt_access.o"; + struct bpf_object *obj; + __u32 retval, size, duration; + int err, prog_fd; + char buf[10]; + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) { + error_cnt++; + return; + } + + memset(buf, 0, sizeof(buf)); + + size = 5; + err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), + buf, &size, &retval, &duration); + CHECK(err || retval, "run", + "err %d errno %d retval %d\n", + err, errno, retval); + + CHECK(size != sizeof(pkt_v4), "out_size", + "incorrect output size, want %lu have %u\n", + sizeof(pkt_v4), size); + + CHECK(buf[5] != 0, "overflow", + "prog_test_run ignored size hint\n"); + + bpf_object__close(obj); +} + + static void test_xdp(void) { struct vip key4 = {.protocol = 6, .family = AF_INET}; @@ -142,6 +176,7 @@ static void test_xdp(void) bpf_map_update_elem(map_fd, &key4, &value4, 0); bpf_map_update_elem(map_fd, &key6, &value6, 0); + size = sizeof(buf); err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), buf, &size, &retval, &duration); @@ -150,6 +185,7 @@ static void test_xdp(void) "err %d errno %d retval %d size %d\n", err, errno, retval, size); + size = sizeof(buf); err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6), buf, &size, &retval, &duration); CHECK(err || errno || retval != XDP_TX || size != 114 || @@ -214,13 +250,15 @@ static void test_l4lb(void) goto out; bpf_map_update_elem(map_fd, &real_num, &real_def, 0); + size = sizeof(buf); err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4), buf, &size, &retval, &duration); CHECK(err || errno || retval != 7/*TC_ACT_REDIRECT*/ || size != 54 || *magic != MAGIC_VAL, "ipv4", "err %d errno %d retval %d size %d magic %x\n", err, errno, retval, size, *magic); - + + size = sizeof(buf); err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6), buf, &size, &retval, &duration); CHECK(err || errno || retval != 7/*TC_ACT_REDIRECT*/ || size != 74 || @@ -502,6 +540,7 @@ int main(void) setrlimit(RLIMIT_MEMLOCK, &rinf); test_pkt_access(); + test_output_size_hint(); test_xdp(); test_l4lb(); test_tcp_estats();