1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7 #include "test_user_ringbuf.h"
8 
9 char _license[] SEC("license") = "GPL";
10 
11 struct {
12 	__uint(type, BPF_MAP_TYPE_USER_RINGBUF);
13 } user_ringbuf SEC(".maps");
14 
15 struct {
16 	__uint(type, BPF_MAP_TYPE_RINGBUF);
17 } kernel_ringbuf SEC(".maps");
18 
19 /* inputs */
20 int pid, err, val;
21 
22 int read = 0;
23 
24 /* Counter used for end-to-end protocol test */
25 __u64 kern_mutated = 0;
26 __u64 user_mutated = 0;
27 __u64 expected_user_mutated = 0;
28 
29 static int
30 is_test_process(void)
31 {
32 	int cur_pid = bpf_get_current_pid_tgid() >> 32;
33 
34 	return cur_pid == pid;
35 }
36 
37 static long
38 record_sample(struct bpf_dynptr *dynptr, void *context)
39 {
40 	const struct sample *sample = NULL;
41 	struct sample stack_sample;
42 	int status;
43 	static int num_calls;
44 
45 	if (num_calls++ % 2 == 0) {
46 		status = bpf_dynptr_read(&stack_sample, sizeof(stack_sample), dynptr, 0, 0);
47 		if (status) {
48 			bpf_printk("bpf_dynptr_read() failed: %d\n", status);
49 			err = 1;
50 			return 1;
51 		}
52 	} else {
53 		sample = bpf_dynptr_data(dynptr, 0, sizeof(*sample));
54 		if (!sample) {
55 			bpf_printk("Unexpectedly failed to get sample\n");
56 			err = 2;
57 			return 1;
58 		}
59 		stack_sample = *sample;
60 	}
61 
62 	__sync_fetch_and_add(&read, 1);
63 	return 0;
64 }
65 
66 static void
67 handle_sample_msg(const struct test_msg *msg)
68 {
69 	switch (msg->msg_op) {
70 	case TEST_MSG_OP_INC64:
71 		kern_mutated += msg->operand_64;
72 		break;
73 	case TEST_MSG_OP_INC32:
74 		kern_mutated += msg->operand_32;
75 		break;
76 	case TEST_MSG_OP_MUL64:
77 		kern_mutated *= msg->operand_64;
78 		break;
79 	case TEST_MSG_OP_MUL32:
80 		kern_mutated *= msg->operand_32;
81 		break;
82 	default:
83 		bpf_printk("Unrecognized op %d\n", msg->msg_op);
84 		err = 2;
85 	}
86 }
87 
88 static long
89 read_protocol_msg(struct bpf_dynptr *dynptr, void *context)
90 {
91 	const struct test_msg *msg = NULL;
92 
93 	msg = bpf_dynptr_data(dynptr, 0, sizeof(*msg));
94 	if (!msg) {
95 		err = 1;
96 		bpf_printk("Unexpectedly failed to get msg\n");
97 		return 0;
98 	}
99 
100 	handle_sample_msg(msg);
101 
102 	return 0;
103 }
104 
105 static int publish_next_kern_msg(__u32 index, void *context)
106 {
107 	struct test_msg *msg = NULL;
108 	int operand_64 = TEST_OP_64;
109 	int operand_32 = TEST_OP_32;
110 
111 	msg = bpf_ringbuf_reserve(&kernel_ringbuf, sizeof(*msg), 0);
112 	if (!msg) {
113 		err = 4;
114 		return 1;
115 	}
116 
117 	switch (index % TEST_MSG_OP_NUM_OPS) {
118 	case TEST_MSG_OP_INC64:
119 		msg->operand_64 = operand_64;
120 		msg->msg_op = TEST_MSG_OP_INC64;
121 		expected_user_mutated += operand_64;
122 		break;
123 	case TEST_MSG_OP_INC32:
124 		msg->operand_32 = operand_32;
125 		msg->msg_op = TEST_MSG_OP_INC32;
126 		expected_user_mutated += operand_32;
127 		break;
128 	case TEST_MSG_OP_MUL64:
129 		msg->operand_64 = operand_64;
130 		msg->msg_op = TEST_MSG_OP_MUL64;
131 		expected_user_mutated *= operand_64;
132 		break;
133 	case TEST_MSG_OP_MUL32:
134 		msg->operand_32 = operand_32;
135 		msg->msg_op = TEST_MSG_OP_MUL32;
136 		expected_user_mutated *= operand_32;
137 		break;
138 	default:
139 		bpf_ringbuf_discard(msg, 0);
140 		err = 5;
141 		return 1;
142 	}
143 
144 	bpf_ringbuf_submit(msg, 0);
145 
146 	return 0;
147 }
148 
149 static void
150 publish_kern_messages(void)
151 {
152 	if (expected_user_mutated != user_mutated) {
153 		bpf_printk("%lu != %lu\n", expected_user_mutated, user_mutated);
154 		err = 3;
155 		return;
156 	}
157 
158 	bpf_loop(8, publish_next_kern_msg, NULL, 0);
159 }
160 
161 SEC("fentry/" SYS_PREFIX "sys_prctl")
162 int test_user_ringbuf_protocol(void *ctx)
163 {
164 	long status = 0;
165 	struct sample *sample = NULL;
166 	struct bpf_dynptr ptr;
167 
168 	if (!is_test_process())
169 		return 0;
170 
171 	status = bpf_user_ringbuf_drain(&user_ringbuf, read_protocol_msg, NULL, 0);
172 	if (status < 0) {
173 		bpf_printk("Drain returned: %ld\n", status);
174 		err = 1;
175 		return 0;
176 	}
177 
178 	publish_kern_messages();
179 
180 	return 0;
181 }
182 
183 SEC("fentry/" SYS_PREFIX "sys_getpgid")
184 int test_user_ringbuf(void *ctx)
185 {
186 	int status = 0;
187 	struct sample *sample = NULL;
188 	struct bpf_dynptr ptr;
189 
190 	if (!is_test_process())
191 		return 0;
192 
193 	err = bpf_user_ringbuf_drain(&user_ringbuf, record_sample, NULL, 0);
194 
195 	return 0;
196 }
197 
198 static long
199 do_nothing_cb(struct bpf_dynptr *dynptr, void *context)
200 {
201 	__sync_fetch_and_add(&read, 1);
202 	return 0;
203 }
204 
205 SEC("fentry/" SYS_PREFIX "sys_getrlimit")
206 int test_user_ringbuf_epoll(void *ctx)
207 {
208 	long num_samples;
209 
210 	if (!is_test_process())
211 		return 0;
212 
213 	num_samples = bpf_user_ringbuf_drain(&user_ringbuf, do_nothing_cb, NULL, 0);
214 	if (num_samples <= 0)
215 		err = 1;
216 
217 	return 0;
218 }
219