xref: /linux/tools/testing/selftests/exec/binfmt_misc_transparent.c (revision b9cba7ebfe539f3e4bbdd03a1e0efa3b30b3f592)
17baee96fSChristian Brauner // SPDX-License-Identifier: GPL-2.0
27baee96fSChristian Brauner /*
37baee96fSChristian Brauner  * Test the static transparent flag 'T' of binfmt_misc. A magic-matched
47baee96fSChristian Brauner  * binary is dispatched to an interpreter with the argument vector left
57baee96fSChristian Brauner  * untouched, the binary passed through AT_EXECFD and mm->exe_file labeled
67baee96fSChristian Brauner  * with the binary. The asserting interpreter (binfmt_transparent_interp)
77baee96fSChristian Brauner  * verifies the constructed identity from inside the process and exits 0.
87baee96fSChristian Brauner  *
97baee96fSChristian Brauner  * Needs root for the registration; no bpf toolchain involved.
107baee96fSChristian Brauner  */
117baee96fSChristian Brauner #define _GNU_SOURCE
127baee96fSChristian Brauner #include <stdio.h>
137baee96fSChristian Brauner #include <stdlib.h>
147baee96fSChristian Brauner 
157baee96fSChristian Brauner #include "binfmt_misc_common.h"
167baee96fSChristian Brauner #include "kselftest_harness.h"
177baee96fSChristian Brauner 
187baee96fSChristian Brauner #define MAGIC		"#TRANSPARENT-SELFTEST#"
197baee96fSChristian Brauner #define TARGET_PATH	"/tmp/binfmt_transparent_target"
207baee96fSChristian Brauner #define INTERP_PATH	"/tmp/binfmt_transparent_interp"
217baee96fSChristian Brauner #define ENTRY		"test_transparent"
227baee96fSChristian Brauner #define RULE(flags)	":" ENTRY ":M:0:" MAGIC "::" INTERP_PATH ":" flags
237baee96fSChristian Brauner 
247baee96fSChristian Brauner /* The target only has to carry the magic; it is never actually loaded. */
257baee96fSChristian Brauner static int create_target(void)
267baee96fSChristian Brauner {
277baee96fSChristian Brauner 	char buf[128] = MAGIC "\n";
287baee96fSChristian Brauner 	int fd;
297baee96fSChristian Brauner 
307baee96fSChristian Brauner 	unlink(TARGET_PATH);
317baee96fSChristian Brauner 	fd = open(TARGET_PATH, O_WRONLY | O_CREAT | O_EXCL, 0755);
327baee96fSChristian Brauner 	if (fd < 0)
337baee96fSChristian Brauner 		return -1;
347baee96fSChristian Brauner 	if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) {
357baee96fSChristian Brauner 		close(fd);
367baee96fSChristian Brauner 		return -1;
377baee96fSChristian Brauner 	}
387baee96fSChristian Brauner 	close(fd);
397baee96fSChristian Brauner 	return 0;
407baee96fSChristian Brauner }
417baee96fSChristian Brauner 
427baee96fSChristian Brauner FIXTURE(transparent) {
437baee96fSChristian Brauner };
447baee96fSChristian Brauner 
457baee96fSChristian Brauner FIXTURE_SETUP(transparent)
467baee96fSChristian Brauner {
477baee96fSChristian Brauner 	char src[PATH_MAX];
487baee96fSChristian Brauner 
497baee96fSChristian Brauner 	if (getuid() != 0)
507baee96fSChristian Brauner 		SKIP(return, "test must be run as root");
517baee96fSChristian Brauner 	if (!binfmt_misc_available())
527baee96fSChristian Brauner 		SKIP(return, "no binfmt_misc");
537baee96fSChristian Brauner 
547baee96fSChristian Brauner 	ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_transparent_interp"), 0);
557baee96fSChristian Brauner 	ASSERT_EQ(copy_file(src, INTERP_PATH), 0);
567baee96fSChristian Brauner 	ASSERT_EQ(create_target(), 0);
577baee96fSChristian Brauner 
587baee96fSChristian Brauner 	/* Skip the whole suite on a kernel that does not know 'T'. */
59*686585ecSChristian Brauner 	if (!binfmt_flag_supported('T')) {
607baee96fSChristian Brauner 		ASSERT_EQ(errno, EINVAL);
617baee96fSChristian Brauner 		SKIP(return, "kernel without the 'T' flag");
627baee96fSChristian Brauner 	}
637baee96fSChristian Brauner }
647baee96fSChristian Brauner 
657baee96fSChristian Brauner FIXTURE_TEARDOWN(transparent)
667baee96fSChristian Brauner {
677baee96fSChristian Brauner 	unregister(ENTRY);
687baee96fSChristian Brauner 	unlink(TARGET_PATH);
697baee96fSChristian Brauner 	unlink(INTERP_PATH);
707baee96fSChristian Brauner }
717baee96fSChristian Brauner 
727baee96fSChristian Brauner /* Grammar sanity check: the same entry without 'T' has to register. */
737baee96fSChristian Brauner TEST_F(transparent, plain_entry_registers)
747baee96fSChristian Brauner {
757baee96fSChristian Brauner 	ASSERT_EQ(write_reg(RULE("")), 0);
767baee96fSChristian Brauner }
777baee96fSChristian Brauner 
787baee96fSChristian Brauner /* 'T' preserves the whole argv, so combining it with 'P' is rejected. */
797baee96fSChristian Brauner TEST_F(transparent, rejects_preserve_argv0)
807baee96fSChristian Brauner {
817baee96fSChristian Brauner 	ASSERT_NE(write_reg(RULE("TP")), 0);
827baee96fSChristian Brauner 	EXPECT_EQ(errno, EINVAL);
837baee96fSChristian Brauner }
847baee96fSChristian Brauner 
857baee96fSChristian Brauner /* The interpreter asserts the identity the kernel built for it. */
867baee96fSChristian Brauner TEST_F(transparent, dispatch)
877baee96fSChristian Brauner {
887baee96fSChristian Brauner 	ASSERT_EQ(write_reg(RULE("T")), 0);
897baee96fSChristian Brauner 
907baee96fSChristian Brauner 	setenv("BINFMT_TEST_BINARY", TARGET_PATH, 1);
917baee96fSChristian Brauner 	setenv("BINFMT_TEST_ARGV0", PAYLOAD_ARGV0, 1);
927baee96fSChristian Brauner 	EXPECT_EQ(run_payload(TARGET_PATH), 0);
937baee96fSChristian Brauner }
947baee96fSChristian Brauner 
957baee96fSChristian Brauner TEST_HARNESS_MAIN
96