xref: /linux/tools/testing/selftests/exec/binfmt_misc_bpf.c (revision 277d787feb2edeb3adae81a99fc47294d523a106)
1*277d787fSFarid Zakaria // SPDX-License-Identifier: GPL-2.0
2*277d787fSFarid Zakaria /*
3*277d787fSFarid Zakaria  * Selftest for binfmt_misc bpf-backed ('B') handlers.
4*277d787fSFarid Zakaria  *
5*277d787fSFarid Zakaria  * A handler is a struct binfmt_misc_ops struct_ops map with a sleepable match
6*277d787fSFarid Zakaria  * and a sleepable load program. Attaching it publishes it by name in the
7*277d787fSFarid Zakaria  * caller's user namespace; a 'B' entry referencing it by name in the
8*277d787fSFarid Zakaria  * interpreter field activates it:
9*277d787fSFarid Zakaria  *
10*277d787fSFarid Zakaria  *     echo ':name:B::::<handler>:' > /proc/sys/fs/binfmt_misc/register
11*277d787fSFarid Zakaria  *
12*277d787fSFarid Zakaria  * Two self-contained cases are exercised:
13*277d787fSFarid Zakaria  *
14*277d787fSFarid Zakaria  *   1. bpf_interp: the match program matches a synthetic aarch64 ELF header
15*277d787fSFarid Zakaria  *      from the prefetched bprm->buf and the load program routes it to a
16*277d787fSFarid Zakaria  *      fixed interpreter of its choosing.
17*277d787fSFarid Zakaria  *   2. nix_origin: the match program reads the binary's program headers to
18*277d787fSFarid Zakaria  *      commit only to a "$ORIGIN/..."-relative PT_INTERP and the load program
19*277d787fSFarid Zakaria  *      resolves it to an interpreter co-located with the binary (the
20*277d787fSFarid Zakaria  *      relocatable-loader case the kernel ELF loader cannot express).
21*277d787fSFarid Zakaria  *
22*277d787fSFarid Zakaria  * Both route to a test interpreter that prints BPF_INTERP_RAN, proving the
23*277d787fSFarid Zakaria  * program's chosen interpreter actually ran.
24*277d787fSFarid Zakaria  */
25*277d787fSFarid Zakaria #define _GNU_SOURCE
26*277d787fSFarid Zakaria #include <stdio.h>
27*277d787fSFarid Zakaria #include <stdlib.h>
28*277d787fSFarid Zakaria #include <string.h>
29*277d787fSFarid Zakaria #include <unistd.h>
30*277d787fSFarid Zakaria #include <fcntl.h>
31*277d787fSFarid Zakaria #include <libgen.h>
32*277d787fSFarid Zakaria #include <sys/mount.h>
33*277d787fSFarid Zakaria #include <sys/stat.h>
34*277d787fSFarid Zakaria 
35*277d787fSFarid Zakaria #include <bpf/btf.h>
36*277d787fSFarid Zakaria #include <bpf/libbpf.h>
37*277d787fSFarid Zakaria 
38*277d787fSFarid Zakaria #define INTERP_PATH	"/tmp/binfmt_bpf_interp"
39*277d787fSFarid Zakaria #define AARCH64_PATH	"/tmp/binfmt_bpf_aarch64"
40*277d787fSFarid Zakaria #define RELOC_DIR	"/tmp/binfmt_reloc"
41*277d787fSFarid Zakaria #define BINFMT_REG	"/proc/sys/fs/binfmt_misc/register"
42*277d787fSFarid Zakaria #define EXPECT		"BPF_INTERP_RAN"
43*277d787fSFarid Zakaria 
44*277d787fSFarid Zakaria static char testdir[512]; /* directory holding this test's built artifacts */
45*277d787fSFarid Zakaria 
46*277d787fSFarid Zakaria static int copy_file(const char *src, const char *dst)
47*277d787fSFarid Zakaria {
48*277d787fSFarid Zakaria 	char buf[4096];
49*277d787fSFarid Zakaria 	int in, out;
50*277d787fSFarid Zakaria 	ssize_t n;
51*277d787fSFarid Zakaria 
52*277d787fSFarid Zakaria 	in = open(src, O_RDONLY);
53*277d787fSFarid Zakaria 	if (in < 0)
54*277d787fSFarid Zakaria 		return -1;
55*277d787fSFarid Zakaria 	out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0755);
56*277d787fSFarid Zakaria 	if (out < 0) {
57*277d787fSFarid Zakaria 		close(in);
58*277d787fSFarid Zakaria 		return -1;
59*277d787fSFarid Zakaria 	}
60*277d787fSFarid Zakaria 	while ((n = read(in, buf, sizeof(buf))) > 0) {
61*277d787fSFarid Zakaria 		if (write(out, buf, n) != n) {
62*277d787fSFarid Zakaria 			close(in);
63*277d787fSFarid Zakaria 			close(out);
64*277d787fSFarid Zakaria 			return -1;
65*277d787fSFarid Zakaria 		}
66*277d787fSFarid Zakaria 	}
67*277d787fSFarid Zakaria 	close(in);
68*277d787fSFarid Zakaria 	close(out);
69*277d787fSFarid Zakaria 	return n < 0 ? -1 : 0;
70*277d787fSFarid Zakaria }
71*277d787fSFarid Zakaria 
72*277d787fSFarid Zakaria /* A minimal 64-bit little-endian aarch64 ELF header, padded to the read size. */
73*277d787fSFarid Zakaria static int create_fake_aarch64(const char *path)
74*277d787fSFarid Zakaria {
75*277d787fSFarid Zakaria 	unsigned char hdr[256] = {0};
76*277d787fSFarid Zakaria 	int fd;
77*277d787fSFarid Zakaria 
78*277d787fSFarid Zakaria 	hdr[0] = 0x7f; hdr[1] = 'E'; hdr[2] = 'L'; hdr[3] = 'F';
79*277d787fSFarid Zakaria 	hdr[4] = 2;			/* ELFCLASS64 */
80*277d787fSFarid Zakaria 	hdr[5] = 1;			/* ELFDATA2LSB */
81*277d787fSFarid Zakaria 	hdr[6] = 1;			/* EV_CURRENT */
82*277d787fSFarid Zakaria 	hdr[16] = 2;			/* e_type = ET_EXEC */
83*277d787fSFarid Zakaria 	hdr[18] = 183 & 0xff;		/* e_machine = EM_AARCH64 */
84*277d787fSFarid Zakaria 	hdr[19] = (183 >> 8) & 0xff;
85*277d787fSFarid Zakaria 	hdr[20] = 1;			/* e_version */
86*277d787fSFarid Zakaria 
87*277d787fSFarid Zakaria 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0755);
88*277d787fSFarid Zakaria 	if (fd < 0)
89*277d787fSFarid Zakaria 		return -1;
90*277d787fSFarid Zakaria 	if (write(fd, hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) {
91*277d787fSFarid Zakaria 		close(fd);
92*277d787fSFarid Zakaria 		return -1;
93*277d787fSFarid Zakaria 	}
94*277d787fSFarid Zakaria 	close(fd);
95*277d787fSFarid Zakaria 	return 0;
96*277d787fSFarid Zakaria }
97*277d787fSFarid Zakaria 
98*277d787fSFarid Zakaria static int register_entry(const char *name, const char *handler)
99*277d787fSFarid Zakaria {
100*277d787fSFarid Zakaria 	char rule[128];
101*277d787fSFarid Zakaria 	int fd;
102*277d787fSFarid Zakaria 	ssize_t n;
103*277d787fSFarid Zakaria 
104*277d787fSFarid Zakaria 	snprintf(rule, sizeof(rule), ":%s:B::::%s:", name, handler);
105*277d787fSFarid Zakaria 	fd = open(BINFMT_REG, O_WRONLY);
106*277d787fSFarid Zakaria 	if (fd < 0)
107*277d787fSFarid Zakaria 		return -1;
108*277d787fSFarid Zakaria 	n = write(fd, rule, strlen(rule));
109*277d787fSFarid Zakaria 	close(fd);
110*277d787fSFarid Zakaria 	return n < 0 ? -1 : 0;
111*277d787fSFarid Zakaria }
112*277d787fSFarid Zakaria 
113*277d787fSFarid Zakaria static void unregister_entry(const char *name)
114*277d787fSFarid Zakaria {
115*277d787fSFarid Zakaria 	char path[128];
116*277d787fSFarid Zakaria 	int fd;
117*277d787fSFarid Zakaria 
118*277d787fSFarid Zakaria 	snprintf(path, sizeof(path), "/proc/sys/fs/binfmt_misc/%s", name);
119*277d787fSFarid Zakaria 	fd = open(path, O_WRONLY);
120*277d787fSFarid Zakaria 	if (fd >= 0) {
121*277d787fSFarid Zakaria 		if (write(fd, "-1", 2) < 0)
122*277d787fSFarid Zakaria 			; /* best effort */
123*277d787fSFarid Zakaria 		close(fd);
124*277d787fSFarid Zakaria 	}
125*277d787fSFarid Zakaria }
126*277d787fSFarid Zakaria 
127*277d787fSFarid Zakaria static int check_output(const char *cmd, const char *expected)
128*277d787fSFarid Zakaria {
129*277d787fSFarid Zakaria 	char buf[128];
130*277d787fSFarid Zakaria 	FILE *fp;
131*277d787fSFarid Zakaria 
132*277d787fSFarid Zakaria 	fp = popen(cmd, "r");
133*277d787fSFarid Zakaria 	if (!fp)
134*277d787fSFarid Zakaria 		return -1;
135*277d787fSFarid Zakaria 	if (!fgets(buf, sizeof(buf), fp)) {
136*277d787fSFarid Zakaria 		pclose(fp);
137*277d787fSFarid Zakaria 		return -1;
138*277d787fSFarid Zakaria 	}
139*277d787fSFarid Zakaria 	pclose(fp);
140*277d787fSFarid Zakaria 	return strncmp(buf, expected, strlen(expected)) ? -1 : 0;
141*277d787fSFarid Zakaria }
142*277d787fSFarid Zakaria 
143*277d787fSFarid Zakaria /*
144*277d787fSFarid Zakaria  * Load @objfile, attach its struct_ops map @handler (which publishes the
145*277d787fSFarid Zakaria  * handler), activate a 'B' entry named @entry that references it, run @target
146*277d787fSFarid Zakaria  * and check it produced @expect.
147*277d787fSFarid Zakaria  */
148*277d787fSFarid Zakaria static int run_case(const char *objfile, const char *handler,
149*277d787fSFarid Zakaria 		    const char *entry, const char *target, const char *expect)
150*277d787fSFarid Zakaria {
151*277d787fSFarid Zakaria 	struct bpf_object *obj;
152*277d787fSFarid Zakaria 	struct bpf_map *map;
153*277d787fSFarid Zakaria 	struct bpf_link *link;
154*277d787fSFarid Zakaria 	int ret = -1;
155*277d787fSFarid Zakaria 
156*277d787fSFarid Zakaria 	obj = bpf_object__open_file(objfile, NULL);
157*277d787fSFarid Zakaria 	if (!obj || libbpf_get_error(obj)) {
158*277d787fSFarid Zakaria 		fprintf(stderr, "open %s failed\n", objfile);
159*277d787fSFarid Zakaria 		return -1;
160*277d787fSFarid Zakaria 	}
161*277d787fSFarid Zakaria 	if (bpf_object__load(obj)) {
162*277d787fSFarid Zakaria 		fprintf(stderr, "load %s failed (check dmesg for the verifier log)\n",
163*277d787fSFarid Zakaria 			objfile);
164*277d787fSFarid Zakaria 		goto close;
165*277d787fSFarid Zakaria 	}
166*277d787fSFarid Zakaria 	map = bpf_object__find_map_by_name(obj, handler);
167*277d787fSFarid Zakaria 	if (!map) {
168*277d787fSFarid Zakaria 		fprintf(stderr, "no struct_ops map '%s' in %s\n", handler, objfile);
169*277d787fSFarid Zakaria 		goto close;
170*277d787fSFarid Zakaria 	}
171*277d787fSFarid Zakaria 	link = bpf_map__attach_struct_ops(map);
172*277d787fSFarid Zakaria 	if (!link || libbpf_get_error(link)) {
173*277d787fSFarid Zakaria 		fprintf(stderr, "attach struct_ops '%s' failed\n", handler);
174*277d787fSFarid Zakaria 		goto close;
175*277d787fSFarid Zakaria 	}
176*277d787fSFarid Zakaria 	if (register_entry(entry, handler)) {
177*277d787fSFarid Zakaria 		fprintf(stderr, "register 'B' entry '%s' failed\n", entry);
178*277d787fSFarid Zakaria 		goto detach;
179*277d787fSFarid Zakaria 	}
180*277d787fSFarid Zakaria 	ret = check_output(target, expect);
181*277d787fSFarid Zakaria 	unregister_entry(entry);
182*277d787fSFarid Zakaria detach:
183*277d787fSFarid Zakaria 	bpf_link__destroy(link);
184*277d787fSFarid Zakaria close:
185*277d787fSFarid Zakaria 	bpf_object__close(obj);
186*277d787fSFarid Zakaria 	return ret;
187*277d787fSFarid Zakaria }
188*277d787fSFarid Zakaria 
189*277d787fSFarid Zakaria int main(void)
190*277d787fSFarid Zakaria {
191*277d787fSFarid Zakaria 	char src[600], obj[600], appdst[600], interpdst[600];
192*277d787fSFarid Zakaria 	char exe[512];
193*277d787fSFarid Zakaria 	ssize_t n;
194*277d787fSFarid Zakaria 	int fail = 0;
195*277d787fSFarid Zakaria 	struct stat st;
196*277d787fSFarid Zakaria 	struct btf *btf;
197*277d787fSFarid Zakaria 
198*277d787fSFarid Zakaria 	if (getuid() != 0) {
199*277d787fSFarid Zakaria 		fprintf(stderr, "Skipping: test must be run as root\n");
200*277d787fSFarid Zakaria 		return 4; /* KSFT_SKIP */
201*277d787fSFarid Zakaria 	}
202*277d787fSFarid Zakaria 
203*277d787fSFarid Zakaria 	/* The kernel must know struct binfmt_misc_ops (CONFIG_BINFMT_MISC_BPF). */
204*277d787fSFarid Zakaria 	btf = btf__load_vmlinux_btf();
205*277d787fSFarid Zakaria 	if (!btf || btf__find_by_name_kind(btf, "binfmt_misc_ops",
206*277d787fSFarid Zakaria 					   BTF_KIND_STRUCT) < 0) {
207*277d787fSFarid Zakaria 		fprintf(stderr,
208*277d787fSFarid Zakaria 			"Skipping: no struct binfmt_misc_ops in the kernel BTF (CONFIG_BINFMT_MISC_BPF)\n");
209*277d787fSFarid Zakaria 		btf__free(btf);
210*277d787fSFarid Zakaria 		return 4; /* KSFT_SKIP */
211*277d787fSFarid Zakaria 	}
212*277d787fSFarid Zakaria 	btf__free(btf);
213*277d787fSFarid Zakaria 
214*277d787fSFarid Zakaria 	n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
215*277d787fSFarid Zakaria 	if (n < 0) {
216*277d787fSFarid Zakaria 		perror("readlink");
217*277d787fSFarid Zakaria 		return 1;
218*277d787fSFarid Zakaria 	}
219*277d787fSFarid Zakaria 	exe[n] = '\0';
220*277d787fSFarid Zakaria 	snprintf(testdir, sizeof(testdir), "%s", dirname(exe));
221*277d787fSFarid Zakaria 
222*277d787fSFarid Zakaria 	if (stat("/sys/fs/bpf", &st) < 0)
223*277d787fSFarid Zakaria 		mkdir("/sys/fs/bpf", 0755);
224*277d787fSFarid Zakaria 	mount("bpf", "/sys/fs/bpf", "bpf", 0, NULL);
225*277d787fSFarid Zakaria 	if (access(BINFMT_REG, F_OK) < 0)
226*277d787fSFarid Zakaria 		mount("binfmt_misc", "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, NULL);
227*277d787fSFarid Zakaria 
228*277d787fSFarid Zakaria 	/* Shared test interpreter. */
229*277d787fSFarid Zakaria 	snprintf(src, sizeof(src), "%s/binfmt_bpf_interp", testdir);
230*277d787fSFarid Zakaria 	if (copy_file(src, INTERP_PATH)) {
231*277d787fSFarid Zakaria 		fprintf(stderr, "cannot install %s\n", INTERP_PATH);
232*277d787fSFarid Zakaria 		return 1;
233*277d787fSFarid Zakaria 	}
234*277d787fSFarid Zakaria 
235*277d787fSFarid Zakaria 	/* Case 1: match a synthetic aarch64 header -> fixed interpreter. */
236*277d787fSFarid Zakaria 	printf("[*] case 1: match aarch64 header -> program-chosen interpreter\n");
237*277d787fSFarid Zakaria 	if (create_fake_aarch64(AARCH64_PATH)) {
238*277d787fSFarid Zakaria 		fprintf(stderr, "cannot create %s\n", AARCH64_PATH);
239*277d787fSFarid Zakaria 		return 1;
240*277d787fSFarid Zakaria 	}
241*277d787fSFarid Zakaria 	snprintf(obj, sizeof(obj), "%s/bpf_interp.bpf.o", testdir);
242*277d787fSFarid Zakaria 	if (run_case(obj, "bpf_interp", "test_bpf_interp", AARCH64_PATH, EXPECT) == 0)
243*277d787fSFarid Zakaria 		printf("[+] case 1 passed\n");
244*277d787fSFarid Zakaria 	else {
245*277d787fSFarid Zakaria 		printf("[-] case 1 FAILED\n");
246*277d787fSFarid Zakaria 		fail = 1;
247*277d787fSFarid Zakaria 	}
248*277d787fSFarid Zakaria 	unlink(AARCH64_PATH);
249*277d787fSFarid Zakaria 
250*277d787fSFarid Zakaria 	/* Case 2: $ORIGIN-relative PT_INTERP -> co-located interpreter. */
251*277d787fSFarid Zakaria 	printf("[*] case 2: $ORIGIN interpreter resolved relative to the binary\n");
252*277d787fSFarid Zakaria 	mkdir(RELOC_DIR, 0755);
253*277d787fSFarid Zakaria 	snprintf(appdst, sizeof(appdst), "%s/app", RELOC_DIR);
254*277d787fSFarid Zakaria 	snprintf(interpdst, sizeof(interpdst), "%s/binfmt_bpf_interp", RELOC_DIR);
255*277d787fSFarid Zakaria 	snprintf(src, sizeof(src), "%s/binfmt_bpf_app", testdir);
256*277d787fSFarid Zakaria 	if (copy_file(src, appdst) ||
257*277d787fSFarid Zakaria 	    copy_file(INTERP_PATH, interpdst)) {
258*277d787fSFarid Zakaria 		fprintf(stderr, "cannot set up %s\n", RELOC_DIR);
259*277d787fSFarid Zakaria 		fail = 1;
260*277d787fSFarid Zakaria 	} else {
261*277d787fSFarid Zakaria 		snprintf(obj, sizeof(obj), "%s/nix_origin.bpf.o", testdir);
262*277d787fSFarid Zakaria 		if (run_case(obj, "nix_origin", "test_bpf_origin", appdst, EXPECT) == 0)
263*277d787fSFarid Zakaria 			printf("[+] case 2 passed\n");
264*277d787fSFarid Zakaria 		else {
265*277d787fSFarid Zakaria 			printf("[-] case 2 FAILED\n");
266*277d787fSFarid Zakaria 			fail = 1;
267*277d787fSFarid Zakaria 		}
268*277d787fSFarid Zakaria 	}
269*277d787fSFarid Zakaria 	unlink(appdst);
270*277d787fSFarid Zakaria 	unlink(interpdst);
271*277d787fSFarid Zakaria 	rmdir(RELOC_DIR);
272*277d787fSFarid Zakaria 	unlink(INTERP_PATH);
273*277d787fSFarid Zakaria 
274*277d787fSFarid Zakaria 	if (!fail)
275*277d787fSFarid Zakaria 		printf("[*] all binfmt_misc bpf cases passed\n");
276*277d787fSFarid Zakaria 	return fail;
277*277d787fSFarid Zakaria }
278