1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Facebook */
3 
4 #include <string.h>
5 #include <linux/bpf.h>
6 #include <bpf/bpf_helpers.h>
7 #include "bpf_misc.h"
8 #include "bpf_kfuncs.h"
9 #include "errno.h"
10 
11 char _license[] SEC("license") = "GPL";
12 
13 int pid, err, val;
14 
15 struct sample {
16 	int pid;
17 	int seq;
18 	long value;
19 	char comm[16];
20 };
21 
22 struct {
23 	__uint(type, BPF_MAP_TYPE_RINGBUF);
24 	__uint(max_entries, 4096);
25 } ringbuf SEC(".maps");
26 
27 struct {
28 	__uint(type, BPF_MAP_TYPE_ARRAY);
29 	__uint(max_entries, 1);
30 	__type(key, __u32);
31 	__type(value, __u32);
32 } array_map SEC(".maps");
33 
34 SEC("?tp/syscalls/sys_enter_nanosleep")
35 int test_read_write(void *ctx)
36 {
37 	char write_data[64] = "hello there, world!!";
38 	char read_data[64] = {};
39 	struct bpf_dynptr ptr;
40 	int i;
41 
42 	if (bpf_get_current_pid_tgid() >> 32 != pid)
43 		return 0;
44 
45 	bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(write_data), 0, &ptr);
46 
47 	/* Write data into the dynptr */
48 	err = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);
49 
50 	/* Read the data that was written into the dynptr */
51 	err = err ?: bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0, 0);
52 
53 	/* Ensure the data we read matches the data we wrote */
54 	for (i = 0; i < sizeof(read_data); i++) {
55 		if (read_data[i] != write_data[i]) {
56 			err = 1;
57 			break;
58 		}
59 	}
60 
61 	bpf_ringbuf_discard_dynptr(&ptr, 0);
62 	return 0;
63 }
64 
65 SEC("?tp/syscalls/sys_enter_nanosleep")
66 int test_dynptr_data(void *ctx)
67 {
68 	__u32 key = 0, val = 235, *map_val;
69 	struct bpf_dynptr ptr;
70 	__u32 map_val_size;
71 	void *data;
72 
73 	map_val_size = sizeof(*map_val);
74 
75 	if (bpf_get_current_pid_tgid() >> 32 != pid)
76 		return 0;
77 
78 	bpf_map_update_elem(&array_map, &key, &val, 0);
79 
80 	map_val = bpf_map_lookup_elem(&array_map, &key);
81 	if (!map_val) {
82 		err = 1;
83 		return 0;
84 	}
85 
86 	bpf_dynptr_from_mem(map_val, map_val_size, 0, &ptr);
87 
88 	/* Try getting a data slice that is out of range */
89 	data = bpf_dynptr_data(&ptr, map_val_size + 1, 1);
90 	if (data) {
91 		err = 2;
92 		return 0;
93 	}
94 
95 	/* Try getting more bytes than available */
96 	data = bpf_dynptr_data(&ptr, 0, map_val_size + 1);
97 	if (data) {
98 		err = 3;
99 		return 0;
100 	}
101 
102 	data = bpf_dynptr_data(&ptr, 0, sizeof(__u32));
103 	if (!data) {
104 		err = 4;
105 		return 0;
106 	}
107 
108 	*(__u32 *)data = 999;
109 
110 	err = bpf_probe_read_kernel(&val, sizeof(val), data);
111 	if (err)
112 		return 0;
113 
114 	if (val != *(int *)data)
115 		err = 5;
116 
117 	return 0;
118 }
119 
120 static int ringbuf_callback(__u32 index, void *data)
121 {
122 	struct sample *sample;
123 
124 	struct bpf_dynptr *ptr = (struct bpf_dynptr *)data;
125 
126 	sample = bpf_dynptr_data(ptr, 0, sizeof(*sample));
127 	if (!sample)
128 		err = 2;
129 	else
130 		sample->pid += index;
131 
132 	return 0;
133 }
134 
135 SEC("?tp/syscalls/sys_enter_nanosleep")
136 int test_ringbuf(void *ctx)
137 {
138 	struct bpf_dynptr ptr;
139 	struct sample *sample;
140 
141 	if (bpf_get_current_pid_tgid() >> 32 != pid)
142 		return 0;
143 
144 	val = 100;
145 
146 	/* check that you can reserve a dynamic size reservation */
147 	err = bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
148 
149 	sample = err ? NULL : bpf_dynptr_data(&ptr, 0, sizeof(*sample));
150 	if (!sample) {
151 		err = 1;
152 		goto done;
153 	}
154 
155 	sample->pid = 10;
156 
157 	/* Can pass dynptr to callback functions */
158 	bpf_loop(10, ringbuf_callback, &ptr, 0);
159 
160 	if (sample->pid != 55)
161 		err = 2;
162 
163 done:
164 	bpf_ringbuf_discard_dynptr(&ptr, 0);
165 	return 0;
166 }
167 
168 SEC("?cgroup_skb/egress")
169 int test_skb_readonly(struct __sk_buff *skb)
170 {
171 	__u8 write_data[2] = {1, 2};
172 	struct bpf_dynptr ptr;
173 	int ret;
174 
175 	if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
176 		err = 1;
177 		return 1;
178 	}
179 
180 	/* since cgroup skbs are read only, writes should fail */
181 	ret = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);
182 	if (ret != -EINVAL) {
183 		err = 2;
184 		return 1;
185 	}
186 
187 	return 1;
188 }
189 
190 SEC("?cgroup_skb/egress")
191 int test_dynptr_skb_data(struct __sk_buff *skb)
192 {
193 	struct bpf_dynptr ptr;
194 	__u64 *data;
195 
196 	if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
197 		err = 1;
198 		return 1;
199 	}
200 
201 	/* This should return NULL. Must use bpf_dynptr_slice API */
202 	data = bpf_dynptr_data(&ptr, 0, 1);
203 	if (data) {
204 		err = 2;
205 		return 1;
206 	}
207 
208 	return 1;
209 }
210