146 lines · 4.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Payload for the binfmt_misc 'L' (loader substitution) selftest. It is * executed as the MAIN image - a fully native exec - with the registered * interpreter substituted for its PT_INTERP, and asserts the native * identity from the inside. Exits 0 when every surface checks out. * * Modes, selected by the orchestrator via the environment: *  - default:                full assertions, path-based ones included *  - BINFMT_TEST_MEMFD=1:    executed from an inaccessible memfd, skip *                            the path-based assertions *  - BINFMT_TEST_STATIC=1:   static build; the override was dropped, so *                            expect no interpreter at all */#define _GNU_SOURCE#include <elf.h>#include <errno.h>#include <fcntl.h>#include <limits.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/auxv.h>#include <unistd.h> #include "binfmt_misc_common.h" /* Start of our own mapped image, courtesy of the linker. */extern const char __ehdr_start[]; /* An image is never this large; used to bracket "within our image". */#define IMAGE_SPAN (16UL << 20) static int failed; static void check(int cond, const char *what){	if (cond)		return;	fprintf(stderr, "[payload] FAILED: %s (errno %d)\n", what, errno);	failed = 1;} /* Return whether /proc/self/maps names a path starting with @prefix. */static int maps_has_prefix(const char *prefix){	char *line = NULL;	size_t len = 0;	int found = 0;	FILE *f; 	f = fopen("/proc/self/maps", "r");	if (!f)		return -1;	while (getline(&line, &len, f) > 0) {		char *path = strchr(line, '/'); 		if (path && !strncmp(path, prefix, strlen(prefix))) {			found = 1;			break;		}	}	free(line);	fclose(f);	return found;} int main(int argc, char *argv[]){	const char *binary = getenv("BINFMT_TEST_BINARY");	const char *interp = getenv("BINFMT_TEST_INTERP");	int memfd_mode = getenv("BINFMT_TEST_MEMFD") != NULL;	int static_mode = getenv("BINFMT_TEST_STATIC") != NULL;	unsigned long self = (unsigned long)__ehdr_start;	unsigned long base = getauxval(AT_BASE);	unsigned long phdr = getauxval(AT_PHDR);	unsigned long entry = getauxval(AT_ENTRY);	unsigned long start_code, end_code; 	/* The argument vector is exactly what the caller built. */	check(argc == 3 && !strcmp(argv[0], PAYLOAD_ARGV0) &&	      !strcmp(argv[1], PAYLOAD_ARG1) && !strcmp(argv[2], PAYLOAD_ARG2),	      "argv was rewritten"); 	/* Native from birth: no execfd, no dispatch marker. */	check(getauxval(AT_EXECFD) == 0, "AT_EXECFD present");	check(getauxval(AT_FLAGS) == 0, "AT_FLAGS not native"); 	if (static_mode) {		/* The override was dropped: no interpreter was loaded. */		check(base == 0, "AT_BASE set for a static payload");	} else {		/* A loader is mapped in the interpreter slot, not our image. */		check(base != 0, "AT_BASE missing");		check(base < self || base >= self + IMAGE_SPAN,		      "AT_BASE inside our own image");	} 	/* We occupy the main-image slot. */	check(phdr >= self && phdr < self + IMAGE_SPAN,	      "AT_PHDR outside our image");	check(entry >= self && entry < self + IMAGE_SPAN,	      "AT_ENTRY outside our image"); 	/* The code statistics markers describe our image, natively placed. */	if (stat_codes(getpid(), &start_code, &end_code) == 0) {		check(start_code >= self && start_code < end_code &&		      end_code < self + IMAGE_SPAN,		      "stat start_code/end_code not our image");		check(entry >= start_code && entry < end_code,		      "AT_ENTRY outside [start_code, end_code)");	} else {		check(0, "cannot parse /proc/self/stat");	} 	if (!memfd_mode && binary) {		const char *execfn = (const char *)getauxval(AT_EXECFN);		const char *base_name = strrchr(binary, '/'); 		base_name = base_name ? base_name + 1 : binary; 		/* exe link, AT_EXECFN and comm all follow the binary. */		check(exe_is(binary), "/proc/self/exe");		check(execfn && !strcmp(execfn, binary), "AT_EXECFN");		check(comm_is(base_name), "comm"); 		/* The running binary is write-denied, natively. */		check(write_denied(binary), "no ETXTBSY on the binary");	} 	if (interp) {		int found = maps_has_prefix(interp); 		if (static_mode)			/* Nothing was substituted, nothing may be mapped. */			check(found == 0, "loader mapped for a static payload");		else			/* The substituted loader shows under its real path. */			check(found == 1, "loader path not in /proc/self/maps");	} 	if (failed)		return 1;	printf("[payload] native identity checks out\n");	return 0;}