1/*
2 * Copyright (c) 2015-2017 Dmitry V. Levin <ldv@altlinux.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "tests.h"
29#include <asm/unistd.h>
30
31#if defined HAVE_UNION_BPF_ATTR_LOG_BUF && defined __NR_bpf
32# include <stdio.h>
33# include <stdint.h>
34# include <unistd.h>
35# include <linux/bpf.h>
36
37static const struct bpf_insn insns[] = {
38	{ .code = BPF_JMP | BPF_EXIT }
39};
40
41static const char *errstr;
42static char log_buf[4096];
43
44static long
45sys_bpf(kernel_ulong_t cmd, kernel_ulong_t attr, kernel_ulong_t size)
46{
47	long rc = syscall(__NR_bpf, cmd, attr, size);
48	errstr = sprintrc(rc);
49	return rc;
50}
51
52static int
53map_create(void)
54{
55	union bpf_attr attr = {
56		.key_size = 4,
57		.value_size = 8,
58		.max_entries = 256
59	};
60	void *const t_attr = tail_memdup(&attr, sizeof(attr));
61	return sys_bpf(BPF_MAP_CREATE, (unsigned long) t_attr, sizeof(attr));
62}
63
64static int
65map_any(int cmd)
66{
67	union bpf_attr attr = {
68		.map_fd = -1,
69		.key = 0xdeadbeef,
70		.value = 0xbadc0ded
71	};
72	void *const t_attr = tail_memdup(&attr, sizeof(attr));
73	return sys_bpf(cmd, (unsigned long) t_attr, sizeof(attr));
74}
75
76static int
77prog_load(void)
78{
79	union bpf_attr attr = {
80		.insn_cnt = sizeof(insns) / sizeof(insns[0]),
81		.insns = (unsigned long) insns,
82		.license = (unsigned long) "GPL",
83		.log_level = 42,
84		.log_size = sizeof(log_buf),
85		.log_buf = (unsigned long) log_buf
86	};
87	void *const t_attr = tail_memdup(&attr, sizeof(attr));
88	return sys_bpf(BPF_PROG_LOAD, (unsigned long) t_attr, sizeof(attr));
89}
90
91/*
92 * bpf() syscall and its first six commands were introduced in Linux kernel
93 * 3.18. Some additional commands were added afterwards, so we need to take
94 * precautions to make sure the tests compile.
95 *
96 * BPF_OBJ_PIN and BPF_OBJ_GET commands appear in kernel 4.4.
97 */
98# ifdef HAVE_UNION_BPF_ATTR_BPF_FD
99static int
100obj_manage(int cmd)
101{
102	union bpf_attr attr = {
103		.pathname = (unsigned long) "/sys/fs/bpf/foo/bar",
104		.bpf_fd = -1
105	};
106	void *const t_attr = tail_memdup(&attr, sizeof(attr));
107	return sys_bpf(cmd, (unsigned long) t_attr, sizeof(attr));
108}
109# endif
110
111/* BPF_PROG_ATTACH and BPF_PROG_DETACH commands appear in kernel 4.10. */
112# ifdef HAVE_UNION_BPF_ATTR_ATTACH_FLAGS
113static int
114prog_cgroup(int cmd)
115{
116	union bpf_attr attr = {
117		.target_fd = -1,
118		.attach_bpf_fd = -1,
119		.attach_type = 0,
120		.attach_flags = 1
121	};
122	void *const t_attr = tail_memdup(&attr, sizeof(attr));
123	return sys_bpf(cmd, (unsigned long) t_attr, sizeof(attr));
124}
125# endif
126
127static unsigned long efault;
128
129static void
130bogus_bpf(int cmd, const char *name)
131{
132	const unsigned long bogus_size = 1024;
133	const unsigned long bogus_addr = efault - bogus_size;
134
135	sys_bpf(cmd, efault, 4);
136	printf("bpf(%s, %#lx, %lu) = %s\n",
137	       name, efault, 4UL, errstr);
138
139	sys_bpf(cmd, efault, bogus_size);
140	printf("bpf(%s, %#lx, %lu) = %s\n",
141	       name, efault, bogus_size, errstr);
142
143	sys_bpf(cmd, bogus_addr, 0);
144	printf("bpf(%s, %#lx, %lu) = %s\n",
145	       name, bogus_addr, 0UL, errstr);
146}
147
148#define BOGUS_BPF(cmd)	bogus_bpf(cmd, #cmd)
149
150int
151main(void)
152{
153	efault = (unsigned long) tail_alloc(1) + 1;
154
155	map_create();
156	printf("bpf(BPF_MAP_CREATE"
157	       ", {map_type=BPF_MAP_TYPE_UNSPEC, key_size=4"
158	       ", value_size=8, max_entries=256}, %u) = %s\n",
159	       (unsigned) sizeof(union bpf_attr), errstr);
160	BOGUS_BPF(BPF_MAP_CREATE);
161
162	map_any(BPF_MAP_LOOKUP_ELEM);
163	printf("bpf(BPF_MAP_LOOKUP_ELEM"
164	       ", {map_fd=-1, key=0xdeadbeef}, %u) = %s\n",
165	       (unsigned) sizeof(union bpf_attr), errstr);
166	BOGUS_BPF(BPF_MAP_LOOKUP_ELEM);
167
168	map_any(BPF_MAP_UPDATE_ELEM);
169	printf("bpf(BPF_MAP_UPDATE_ELEM"
170	       ", {map_fd=-1, key=0xdeadbeef"
171	       ", value=0xbadc0ded, flags=BPF_ANY}, %u) = %s\n",
172	       (unsigned) sizeof(union bpf_attr), errstr);
173	BOGUS_BPF(BPF_MAP_UPDATE_ELEM);
174
175	map_any(BPF_MAP_DELETE_ELEM);
176	printf("bpf(BPF_MAP_DELETE_ELEM"
177	       ", {map_fd=-1, key=0xdeadbeef}, %u) = %s\n",
178	       (unsigned) sizeof(union bpf_attr), errstr);
179	BOGUS_BPF(BPF_MAP_DELETE_ELEM);
180
181	map_any(BPF_MAP_GET_NEXT_KEY);
182	printf("bpf(BPF_MAP_GET_NEXT_KEY"
183	       ", {map_fd=-1, key=0xdeadbeef}, %u) = %s\n",
184	       (unsigned) sizeof(union bpf_attr), errstr);
185	BOGUS_BPF(BPF_MAP_GET_NEXT_KEY);
186
187	prog_load();
188	printf("bpf(BPF_PROG_LOAD"
189	       ", {prog_type=BPF_PROG_TYPE_UNSPEC, insn_cnt=1, insns=%p"
190	       ", license=\"GPL\", log_level=42, log_size=4096, log_buf=%p"
191	       ", kern_version=0}, %u) = %s\n",
192	       insns, log_buf, (unsigned) sizeof(union bpf_attr), errstr);
193	BOGUS_BPF(BPF_PROG_LOAD);
194
195# ifdef HAVE_UNION_BPF_ATTR_BPF_FD
196	obj_manage(BPF_OBJ_PIN);
197	printf("bpf(BPF_OBJ_PIN"
198	       ", {pathname=\"/sys/fs/bpf/foo/bar\", bpf_fd=-1}, %u) = %s\n",
199	       (unsigned) sizeof(union bpf_attr), errstr);
200	BOGUS_BPF(BPF_OBJ_PIN);
201
202	obj_manage(BPF_OBJ_GET);
203	printf("bpf(BPF_OBJ_GET"
204	       ", {pathname=\"/sys/fs/bpf/foo/bar\", bpf_fd=-1}, %u) = %s\n",
205	       (unsigned) sizeof(union bpf_attr), errstr);
206	BOGUS_BPF(BPF_OBJ_GET);
207# endif
208
209# ifdef HAVE_UNION_BPF_ATTR_ATTACH_FLAGS
210	prog_cgroup(BPF_PROG_ATTACH);
211	printf("bpf(BPF_PROG_ATTACH"
212	       ", {target_fd=-1, attach_bpf_fd=-1"
213	       ", attach_type=BPF_CGROUP_INET_INGRESS"
214	       ", attach_flags=BPF_F_ALLOW_OVERRIDE}, %u) = %s\n",
215	       (unsigned) sizeof(union bpf_attr), errstr);
216	BOGUS_BPF(BPF_PROG_ATTACH);
217
218	prog_cgroup(BPF_PROG_DETACH);
219	printf("bpf(BPF_PROG_DETACH"
220	       ", {target_fd=-1, attach_type=BPF_CGROUP_INET_INGRESS}, %u)"
221	       " = %s\n",
222	       (unsigned) sizeof(union bpf_attr), errstr);
223	BOGUS_BPF(BPF_PROG_DETACH);
224# endif
225
226	bogus_bpf(0xfacefeed, "0xfacefeed /* BPF_??? */");
227
228	puts("+++ exited with 0 +++");
229	return 0;
230}
231
232#else
233
234SKIP_MAIN_UNDEFINED("__NR_bpf")
235
236#endif
237