xref: /linux/tools/testing/selftests/exec/loader.bpf.c (revision b9cba7ebfe539f3e4bbdd03a1e0efa3b30b3f592)
1*87c50a58SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*87c50a58SChristian Brauner /*
3*87c50a58SChristian Brauner  * binfmt_misc_ops handler for the loader-substitution case: match the
4*87c50a58SChristian Brauner  * marker the harness poked into the payload's e_ident padding and ask for
5*87c50a58SChristian Brauner  * the selected interpreter to be substituted for the binary's PT_INTERP,
6*87c50a58SChristian Brauner  * so the binary itself runs as a fully native exec.
7*87c50a58SChristian Brauner  */
8*87c50a58SChristian Brauner #include "vmlinux.h"
9*87c50a58SChristian Brauner #include <bpf/bpf_helpers.h>
10*87c50a58SChristian Brauner #include <bpf/bpf_tracing.h>
11*87c50a58SChristian Brauner 
12*87c50a58SChristian Brauner char _license[] SEC("license") = "GPL";
13*87c50a58SChristian Brauner 
14*87c50a58SChristian Brauner #define EI_CLASS	4
15*87c50a58SChristian Brauner #define EI_PAD		9
16*87c50a58SChristian Brauner #define ELFCLASS64	2
17*87c50a58SChristian Brauner 
18*87c50a58SChristian Brauner extern int bpf_binprm_set_interp(struct linux_binprm *bprm, const char *path,
19*87c50a58SChristian Brauner 				 size_t path__sz) __ksym;
20*87c50a58SChristian Brauner extern int bpf_binprm_set_flags(struct linux_binprm *bprm,
21*87c50a58SChristian Brauner 				enum bpf_binprm_flags flags) __ksym;
22*87c50a58SChristian Brauner 
23*87c50a58SChristian Brauner SEC("struct_ops.s/match")
24*87c50a58SChristian Brauner bool BPF_PROG(loader_match, struct linux_binprm *bprm)
25*87c50a58SChristian Brauner {
26*87c50a58SChristian Brauner 	if (bprm->buf[0] != 0x7f || bprm->buf[1] != 'E' ||
27*87c50a58SChristian Brauner 	    bprm->buf[2] != 'L' || bprm->buf[3] != 'F' ||
28*87c50a58SChristian Brauner 	    bprm->buf[EI_CLASS] != ELFCLASS64)
29*87c50a58SChristian Brauner 		return false;
30*87c50a58SChristian Brauner 
31*87c50a58SChristian Brauner 	/* The harness marks the payload with "LDRTST" at EI_PAD. */
32*87c50a58SChristian Brauner 	return bprm->buf[EI_PAD + 0] == 'L' && bprm->buf[EI_PAD + 1] == 'D' &&
33*87c50a58SChristian Brauner 	       bprm->buf[EI_PAD + 2] == 'R' && bprm->buf[EI_PAD + 3] == 'T' &&
34*87c50a58SChristian Brauner 	       bprm->buf[EI_PAD + 4] == 'S' && bprm->buf[EI_PAD + 5] == 'T';
35*87c50a58SChristian Brauner }
36*87c50a58SChristian Brauner 
37*87c50a58SChristian Brauner SEC("struct_ops.s/load")
38*87c50a58SChristian Brauner int BPF_PROG(loader_load, struct linux_binprm *bprm)
39*87c50a58SChristian Brauner {
40*87c50a58SChristian Brauner 	char interp[] = "/tmp/binfmt_loader_interp";
41*87c50a58SChristian Brauner 	int err;
42*87c50a58SChristian Brauner 
43*87c50a58SChristian Brauner 	err = bpf_binprm_set_flags(bprm, BPF_BINPRM_LOADER);
44*87c50a58SChristian Brauner 	if (err)
45*87c50a58SChristian Brauner 		return err;
46*87c50a58SChristian Brauner 
47*87c50a58SChristian Brauner 	/* @path__sz includes the terminating NUL; 0 commits the selection. */
48*87c50a58SChristian Brauner 	return bpf_binprm_set_interp(bprm, interp, sizeof(interp));
49*87c50a58SChristian Brauner }
50*87c50a58SChristian Brauner 
51*87c50a58SChristian Brauner SEC(".struct_ops.link")
52*87c50a58SChristian Brauner struct binfmt_misc_ops loader = {
53*87c50a58SChristian Brauner 	.match = (void *)loader_match,
54*87c50a58SChristian Brauner 	.load = (void *)loader_load,
55*87c50a58SChristian Brauner 	.name = "loader",
56*87c50a58SChristian Brauner };
57