Files
linux/tools/testing/selftests/exec/transparent.bpf.c
T
Christian Brauner 7baee96f83 selftests/exec: test the transparent binfmt_misc mode
Verify the identity a transparent dispatch constructs, from both
activation paths.

- binfmt_misc_transparent: registers a magic entry with the static 'T'
  flag and execs a matched binary with arguments.

- binfmt_misc_bpf: a handler whose load program sets
  BPF_BINPRM_TRANSPARENT.

Both dispatch to a shared asserting interpreter that runs in place of
the binary and checks the contract from the inside:

- AT_FLAGS carries AT_FLAGS_TRANSPARENT_INTERP
- AT_EXECFD refers to the very inode of the binary
- /proc/self/exe resolves to the binary
- argv and /proc/self/cmdline are exactly what the caller passed with
  nothing spliced in
- comm is the binary's basename
- the binary is write-denied while it runs

The static test also validates the registration. 'T' combined with 'P'
must be rejected. A kernel that does not know 'T' turns the test into a
skip. The asserting interpreter and the static test build without the
bpf toolchain so the core transparent semantics stay covered on systems
where the bpf cases are skipped.

The flag support probe, the canonical payload argv with the
run_payload() helper that execs it, and the identity assertions (exe
link, comm, write denial) live in binfmt_misc_common.h; the loader
substitution test reuses all of them.

Link: https://patch.msgid.link/20260721-work-bpf-binfmt_misc-ptinterp-v2-13-e57866e4ae0f@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-08-03 10:08:46 +02:00

58 lines
1.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* binfmt_misc_ops handler for the transparent-mode case: match a synthetic
* riscv ELF header and run the asserting interpreter transparently - the
* argument vector untouched, the binary in AT_EXECFD and mm->exe_file
* labeled with the binary.
*/
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char _license[] SEC("license") = "GPL";
#define EI_CLASS 4
#define ELFCLASS64 2
#define EM_RISCV 243
extern int bpf_binprm_set_interp(struct linux_binprm *bprm, const char *path,
size_t path__sz) __ksym;
extern int bpf_binprm_set_flags(struct linux_binprm *bprm,
enum bpf_binprm_flags flags) __ksym;
SEC("struct_ops.s/match")
bool BPF_PROG(transparent_match, struct linux_binprm *bprm)
{
__u16 machine;
if (bprm->buf[0] != 0x7f || bprm->buf[1] != 'E' ||
bprm->buf[2] != 'L' || bprm->buf[3] != 'F' ||
bprm->buf[EI_CLASS] != ELFCLASS64)
return false;
/* e_machine is a 16-bit little-endian field at offset 18. */
machine = (__u8)bprm->buf[18] | ((__u16)(__u8)bprm->buf[19] << 8);
return machine == EM_RISCV;
}
SEC("struct_ops.s/load")
int BPF_PROG(transparent_load, struct linux_binprm *bprm)
{
char interp[] = "/tmp/binfmt_transparent_interp";
int err;
err = bpf_binprm_set_flags(bprm, BPF_BINPRM_TRANSPARENT);
if (err)
return err;
/* @path__sz includes the terminating NUL; 0 commits the selection. */
return bpf_binprm_set_interp(bprm, interp, sizeof(interp));
}
SEC(".struct_ops.link")
struct binfmt_misc_ops transparent = {
.match = (void *)transparent_match,
.load = (void *)transparent_load,
.name = "transparent",
};