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_TYPE
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	};
121	void *const t_attr = tail_memdup(&attr, sizeof(attr));
122	return sys_bpf(cmd, (unsigned long) t_attr, sizeof(attr));
123}
124# endif
125
126static unsigned long efault;
127
128static void
129bogus_bpf(int cmd, const char *name)
130{
131	const unsigned long bogus_size = 1024;
132	const unsigned long bogus_addr = efault - bogus_size;
133
134	sys_bpf(cmd, efault, 4);
135	printf("bpf(%s, %#lx, %lu) = %s\n",
136	       name, efault, 4UL, errstr);
137
138	sys_bpf(cmd, efault, bogus_size);
139	printf("bpf(%s, %#lx, %lu) = %s\n",
140	       name, efault, bogus_size, errstr);
141
142	sys_bpf(cmd, bogus_addr, 0);
143	printf("bpf(%s, %#lx, %lu) = %s\n",
144	       name, bogus_addr, 0UL, errstr);
145}
146
147#define BOGUS_BPF(cmd)	bogus_bpf(cmd, #cmd)
148
149int
150main(void)
151{
152	efault = (unsigned long) tail_alloc(1) + 1;
153
154	map_create();
155	printf("bpf(BPF_MAP_CREATE"
156	       ", {map_type=BPF_MAP_TYPE_UNSPEC, key_size=4"
157	       ", value_size=8, max_entries=256}, %u) = %s\n",
158	       (unsigned) sizeof(union bpf_attr), errstr);
159	BOGUS_BPF(BPF_MAP_CREATE);
160
161	map_any(BPF_MAP_LOOKUP_ELEM);
162	printf("bpf(BPF_MAP_LOOKUP_ELEM"
163	       ", {map_fd=-1, key=0xdeadbeef}, %u) = %s\n",
164	       (unsigned) sizeof(union bpf_attr), errstr);
165	BOGUS_BPF(BPF_MAP_LOOKUP_ELEM);
166
167	map_any(BPF_MAP_UPDATE_ELEM);
168	printf("bpf(BPF_MAP_UPDATE_ELEM"
169	       ", {map_fd=-1, key=0xdeadbeef"
170	       ", value=0xbadc0ded, flags=BPF_ANY}, %u) = %s\n",
171	       (unsigned) sizeof(union bpf_attr), errstr);
172	BOGUS_BPF(BPF_MAP_UPDATE_ELEM);
173
174	map_any(BPF_MAP_DELETE_ELEM);
175	printf("bpf(BPF_MAP_DELETE_ELEM"
176	       ", {map_fd=-1, key=0xdeadbeef}, %u) = %s\n",
177	       (unsigned) sizeof(union bpf_attr), errstr);
178	BOGUS_BPF(BPF_MAP_DELETE_ELEM);
179
180	map_any(BPF_MAP_GET_NEXT_KEY);
181	printf("bpf(BPF_MAP_GET_NEXT_KEY"
182	       ", {map_fd=-1, key=0xdeadbeef}, %u) = %s\n",
183	       (unsigned) sizeof(union bpf_attr), errstr);
184	BOGUS_BPF(BPF_MAP_GET_NEXT_KEY);
185
186	prog_load();
187	printf("bpf(BPF_PROG_LOAD"
188	       ", {prog_type=BPF_PROG_TYPE_UNSPEC, insn_cnt=1, insns=%p"
189	       ", license=\"GPL\", log_level=42, log_size=4096, log_buf=%p"
190	       ", kern_version=0}, %u) = %s\n",
191	       insns, log_buf, (unsigned) sizeof(union bpf_attr), errstr);
192	BOGUS_BPF(BPF_PROG_LOAD);
193
194# ifdef HAVE_UNION_BPF_ATTR_BPF_FD
195	obj_manage(BPF_OBJ_PIN);
196	printf("bpf(BPF_OBJ_PIN"
197	       ", {pathname=\"/sys/fs/bpf/foo/bar\", bpf_fd=-1}, %u) = %s\n",
198	       (unsigned) sizeof(union bpf_attr), errstr);
199	BOGUS_BPF(BPF_OBJ_PIN);
200
201	obj_manage(BPF_OBJ_GET);
202	printf("bpf(BPF_OBJ_GET"
203	       ", {pathname=\"/sys/fs/bpf/foo/bar\", bpf_fd=-1}, %u) = %s\n",
204	       (unsigned) sizeof(union bpf_attr), errstr);
205	BOGUS_BPF(BPF_OBJ_GET);
206# endif
207
208# ifdef HAVE_UNION_BPF_ATTR_ATTACH_TYPE
209	prog_cgroup(BPF_PROG_ATTACH);
210	printf("bpf(BPF_PROG_ATTACH"
211	       ", {target_fd=-1, attach_bpf_fd=-1"
212	       ", attach_type=BPF_CGROUP_INET_INGRESS}, %u) = %s\n",
213	       (unsigned) sizeof(union bpf_attr), errstr);
214	BOGUS_BPF(BPF_PROG_ATTACH);
215
216	prog_cgroup(BPF_PROG_DETACH);
217	printf("bpf(BPF_PROG_DETACH"
218	       ", {target_fd=-1, attach_type=BPF_CGROUP_INET_INGRESS}, %u)"
219	       " = %s\n",
220	       (unsigned) sizeof(union bpf_attr), errstr);
221	BOGUS_BPF(BPF_PROG_DETACH);
222# endif
223
224	bogus_bpf(0xfacefeed, "0xfacefeed /* BPF_??? */");
225
226	puts("+++ exited with 0 +++");
227	return 0;
228}
229
230#else
231
232SKIP_MAIN_UNDEFINED("__NR_bpf")
233
234#endif
235