mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 17:35:18 -04:00
Exercise the 'L' flag end to end. The payload runs as the main image
with a copy of the system loader substituted for its PT_INTERP, and
asserts the native identity from inside:
- argv exactly as the caller built it
- no AT_EXECFD
- AT_FLAGS clear
- AT_BASE set but outside its own image
- AT_PHDR/AT_ENTRY inside it
- /proc/self/{exe,comm,stat} and AT_EXECFN all describing the binary
- ETXTBSY on the running binary
- the substituted loader visible in /proc/self/maps under its real path
Magic matching pokes a marker into the ELF header's e_ident padding
(EI_PAD, offset 9), which sits inside the match window and is ignored by
kernel and loader alike. the same binary is also matched by extension.
Two cases cover the paths where the substitution does not happen. A '#!'
file that matched an 'L' entry is claimed by binfmt_script rather than by
binfmt_elf, so the staged substitute has to be released when the
interpreter replaces the file; the test opens the loader for writing
afterwards, which fails with ETXTBSY if the write denial was leaked
instead. A relative interpreter path is rejected at registration for both
'L' and 'C', neither of which may resolve one against the working
directory of whoever runs the binary.
The bpf-side BPF_BINPRM_LOADER path shares all machinery past the flag
mapping. A harness case for it can join the bpf runtime coverage of
the transparent series.
Link: https://patch.msgid.link/20260721-work-bpf-binfmt_misc-ptinterp-v2-20-e57866e4ae0f@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
57 lines
1.7 KiB
C
57 lines
1.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* binfmt_misc_ops handler for the loader-substitution case: match the
|
|
* marker the harness poked into the payload's e_ident padding and ask for
|
|
* the selected interpreter to be substituted for the binary's PT_INTERP,
|
|
* so the binary itself runs as a fully native exec.
|
|
*/
|
|
#include "vmlinux.h"
|
|
#include <bpf/bpf_helpers.h>
|
|
#include <bpf/bpf_tracing.h>
|
|
|
|
char _license[] SEC("license") = "GPL";
|
|
|
|
#define EI_CLASS 4
|
|
#define EI_PAD 9
|
|
#define ELFCLASS64 2
|
|
|
|
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(loader_match, struct linux_binprm *bprm)
|
|
{
|
|
if (bprm->buf[0] != 0x7f || bprm->buf[1] != 'E' ||
|
|
bprm->buf[2] != 'L' || bprm->buf[3] != 'F' ||
|
|
bprm->buf[EI_CLASS] != ELFCLASS64)
|
|
return false;
|
|
|
|
/* The harness marks the payload with "LDRTST" at EI_PAD. */
|
|
return bprm->buf[EI_PAD + 0] == 'L' && bprm->buf[EI_PAD + 1] == 'D' &&
|
|
bprm->buf[EI_PAD + 2] == 'R' && bprm->buf[EI_PAD + 3] == 'T' &&
|
|
bprm->buf[EI_PAD + 4] == 'S' && bprm->buf[EI_PAD + 5] == 'T';
|
|
}
|
|
|
|
SEC("struct_ops.s/load")
|
|
int BPF_PROG(loader_load, struct linux_binprm *bprm)
|
|
{
|
|
char interp[] = "/tmp/binfmt_loader_interp";
|
|
int err;
|
|
|
|
err = bpf_binprm_set_flags(bprm, BPF_BINPRM_LOADER);
|
|
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 loader = {
|
|
.match = (void *)loader_match,
|
|
.load = (void *)loader_load,
|
|
.name = "loader",
|
|
};
|