// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include "bpf_misc.h" #include "test_user_ringbuf.h" char _license[] SEC("license") = "GPL"; struct { __uint(type, BPF_MAP_TYPE_USER_RINGBUF); } user_ringbuf SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); } kernel_ringbuf SEC(".maps"); /* inputs */ int pid, err, val; int read = 0; /* Counter used for end-to-end protocol test */ __u64 kern_mutated = 0; __u64 user_mutated = 0; __u64 expected_user_mutated = 0; static int is_test_process(void) { int cur_pid = bpf_get_current_pid_tgid() >> 32; return cur_pid == pid; } static long record_sample(struct bpf_dynptr *dynptr, void *context) { const struct sample *sample = NULL; struct sample stack_sample; int status; static int num_calls; if (num_calls++ % 2 == 0) { status = bpf_dynptr_read(&stack_sample, sizeof(stack_sample), dynptr, 0, 0); if (status) { bpf_printk("bpf_dynptr_read() failed: %d\n", status); err = 1; return 1; } } else { sample = bpf_dynptr_data(dynptr, 0, sizeof(*sample)); if (!sample) { bpf_printk("Unexpectedly failed to get sample\n"); err = 2; return 1; } stack_sample = *sample; } __sync_fetch_and_add(&read, 1); return 0; } static void handle_sample_msg(const struct test_msg *msg) { switch (msg->msg_op) { case TEST_MSG_OP_INC64: kern_mutated += msg->operand_64; break; case TEST_MSG_OP_INC32: kern_mutated += msg->operand_32; break; case TEST_MSG_OP_MUL64: kern_mutated *= msg->operand_64; break; case TEST_MSG_OP_MUL32: kern_mutated *= msg->operand_32; break; default: bpf_printk("Unrecognized op %d\n", msg->msg_op); err = 2; } } static long read_protocol_msg(struct bpf_dynptr *dynptr, void *context) { const struct test_msg *msg = NULL; msg = bpf_dynptr_data(dynptr, 0, sizeof(*msg)); if (!msg) { err = 1; bpf_printk("Unexpectedly failed to get msg\n"); return 0; } handle_sample_msg(msg); return 0; } static int publish_next_kern_msg(__u32 index, void *context) { struct test_msg *msg = NULL; int operand_64 = TEST_OP_64; int operand_32 = TEST_OP_32; msg = bpf_ringbuf_reserve(&kernel_ringbuf, sizeof(*msg), 0); if (!msg) { err = 4; return 1; } switch (index % TEST_MSG_OP_NUM_OPS) { case TEST_MSG_OP_INC64: msg->operand_64 = operand_64; msg->msg_op = TEST_MSG_OP_INC64; expected_user_mutated += operand_64; break; case TEST_MSG_OP_INC32: msg->operand_32 = operand_32; msg->msg_op = TEST_MSG_OP_INC32; expected_user_mutated += operand_32; break; case TEST_MSG_OP_MUL64: msg->operand_64 = operand_64; msg->msg_op = TEST_MSG_OP_MUL64; expected_user_mutated *= operand_64; break; case TEST_MSG_OP_MUL32: msg->operand_32 = operand_32; msg->msg_op = TEST_MSG_OP_MUL32; expected_user_mutated *= operand_32; break; default: bpf_ringbuf_discard(msg, 0); err = 5; return 1; } bpf_ringbuf_submit(msg, 0); return 0; } static void publish_kern_messages(void) { if (expected_user_mutated != user_mutated) { bpf_printk("%lu != %lu\n", expected_user_mutated, user_mutated); err = 3; return; } bpf_loop(8, publish_next_kern_msg, NULL, 0); } SEC("fentry/" SYS_PREFIX "sys_prctl") int test_user_ringbuf_protocol(void *ctx) { long status = 0; if (!is_test_process()) return 0; status = bpf_user_ringbuf_drain(&user_ringbuf, read_protocol_msg, NULL, 0); if (status < 0) { bpf_printk("Drain returned: %ld\n", status); err = 1; return 0; } publish_kern_messages(); return 0; } SEC("fentry/" SYS_PREFIX "sys_getpgid") int test_user_ringbuf(void *ctx) { if (!is_test_process()) return 0; err = bpf_user_ringbuf_drain(&user_ringbuf, record_sample, NULL, 0); return 0; } static long do_nothing_cb(struct bpf_dynptr *dynptr, void *context) { __sync_fetch_and_add(&read, 1); return 0; } SEC("fentry/" SYS_PREFIX "sys_prlimit64") int test_user_ringbuf_epoll(void *ctx) { long num_samples; if (!is_test_process()) return 0; num_samples = bpf_user_ringbuf_drain(&user_ringbuf, do_nothing_cb, NULL, 0); if (num_samples <= 0) err = 1; return 0; }