Add coverage for the stream fixes requested on the capacity rollback
series:

- oversized bpf_stream_printk() returns -E2BIG and does not leak
  capacity for a subsequent successful write;
- bpf_prog_stream_read() returns the successfully copied prefix when
  the userspace buffer straddles an unmapped page.

Signed-off-by: Jianlin Shi <[email protected]>
---
 .../testing/selftests/bpf/prog_tests/stream.c | 70 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/stream.c    | 12 ++++
 2 files changed, 82 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/stream.c 
b/tools/testing/selftests/bpf/prog_tests/stream.c
index c3cce5c292bd..3ae98e3c4909 100644
--- a/tools/testing/selftests/bpf/prog_tests/stream.c
+++ b/tools/testing/selftests/bpf/prog_tests/stream.c
@@ -58,6 +58,76 @@ void test_stream_syscall(void)
        stream__destroy(skel);
 }
 
+void test_stream_oversize(void)
+{
+       LIBBPF_OPTS(bpf_test_run_opts, opts);
+       struct stream *skel;
+       int ret, prog_fd;
+       char buf[8] = {};
+
+       skel = stream__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.stream_oversize);
+       ret = bpf_prog_test_run_opts(prog_fd, &opts);
+       ASSERT_OK(ret, "oversize run");
+       ASSERT_EQ(opts.retval, -E2BIG, "oversize retval");
+
+       /* Oversized push must not permanently consume capacity. */
+       prog_fd = bpf_program__fd(skel->progs.stream_syscall);
+       ret = bpf_prog_test_run_opts(prog_fd, &opts);
+       ASSERT_OK(ret, "syscall run");
+       ASSERT_OK(opts.retval, "syscall retval");
+
+       ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 
sizeof(buf), NULL);
+       ASSERT_EQ(ret, 3, "bytes after oversize");
+       ASSERT_OK(memcmp(buf, "foo", 3), "payload after oversize");
+
+       stream__destroy(skel);
+}
+
+void test_stream_partial_read(void)
+{
+       LIBBPF_OPTS(bpf_test_run_opts, opts);
+       struct stream *skel;
+       int ret, prog_fd;
+       long page_size;
+       char *page, *buf;
+       char rest[8] = {};
+
+       skel = stream__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.stream_syscall);
+       ret = bpf_prog_test_run_opts(prog_fd, &opts);
+       ASSERT_OK(ret, "ret");
+       ASSERT_OK(opts.retval, "retval");
+
+       page_size = sysconf(_SC_PAGESIZE);
+       page = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (!ASSERT_NEQ(page, MAP_FAILED, "mmap")) {
+               stream__destroy(skel);
+               return;
+       }
+       /* Leave only the first page mapped so a straddling copy faults. */
+       ASSERT_OK(munmap(page + page_size, page_size), "munmap second page");
+
+       buf = page + page_size - 1;
+       ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 3, NULL);
+       ASSERT_EQ(ret, 1, "partial bytes");
+       ASSERT_EQ(buf[0], 'f', "first byte");
+
+       ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, rest, 
sizeof(rest), NULL);
+       ASSERT_EQ(ret, 2, "remaining bytes");
+       ASSERT_OK(memcmp(rest, "oo", 2), "remaining data");
+
+       munmap(page, page_size);
+       stream__destroy(skel);
+}
+
 static void test_address(struct bpf_program *prog, unsigned long *fault_addr_p)
 {
        LIBBPF_OPTS(bpf_test_run_opts, opts);
diff --git a/tools/testing/selftests/bpf/progs/stream.c 
b/tools/testing/selftests/bpf/progs/stream.c
index 6f999ba951a3..6fab9b8b21a0 100644
--- a/tools/testing/selftests/bpf/progs/stream.c
+++ b/tools/testing/selftests/bpf/progs/stream.c
@@ -36,7 +36,12 @@ struct {
 } array SEC(".maps");
 
 #define ENOSPC 28
+#define E2BIG 7
 #define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+#define _X64 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+/* 1024 bytes: truncated by bstr_printf, must return -E2BIG. */
+#define _BIG_STR (_X64 _X64 _X64 _X64 _X64 _X64 _X64 _X64 \
+                 _X64 _X64 _X64 _X64 _X64 _X64 _X64 _X64)
 
 int size;
 u64 fault_addr;
@@ -117,6 +122,13 @@ int stream_syscall(void *ctx)
        return 0;
 }
 
+SEC("syscall")
+__success __retval(-E2BIG)
+int stream_oversize(void *ctx)
+{
+       return bpf_stream_printk(BPF_STDOUT, _BIG_STR);
+}
+
 SEC("syscall")
 __arch_x86_64
 __arch_arm64
-- 
2.43.0



Reply via email to