// SPDX-License-Identifier: GPL-2.0
/*
 * Test the static transparent flag 'T' of binfmt_misc. A magic-matched
 * binary is dispatched to an interpreter with the argument vector left
 * untouched, the binary passed through AT_EXECFD and mm->exe_file labeled
 * with the binary. The asserting interpreter (binfmt_transparent_interp)
 * verifies the constructed identity from inside the process and exits 0.
 *
 * Needs root for the registration; no bpf toolchain involved.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

#include "binfmt_misc_common.h"
#include "kselftest_harness.h"

#define MAGIC		"#TRANSPARENT-SELFTEST#"
#define TARGET_PATH	"/tmp/binfmt_transparent_target"
#define INTERP_PATH	"/tmp/binfmt_transparent_interp"
#define ENTRY		"test_transparent"
#define RULE(flags)	":" ENTRY ":M:0:" MAGIC "::" INTERP_PATH ":" flags

/* The target only has to carry the magic; it is never actually loaded. */
static int create_target(void)
{
	char buf[128] = MAGIC "\n";
	int fd;

	unlink(TARGET_PATH);
	fd = open(TARGET_PATH, O_WRONLY | O_CREAT | O_EXCL, 0755);
	if (fd < 0)
		return -1;
	if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) {
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

FIXTURE(transparent) {
};

FIXTURE_SETUP(transparent)
{
	char src[PATH_MAX];

	if (getuid() != 0)
		SKIP(return, "test must be run as root");
	if (!binfmt_misc_available())
		SKIP(return, "no binfmt_misc");

	ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_transparent_interp"), 0);
	ASSERT_EQ(copy_file(src, INTERP_PATH), 0);
	ASSERT_EQ(create_target(), 0);

	/* Skip the whole suite on a kernel that does not know 'T'. */
	if (!binfmt_flag_supported('T')) {
		ASSERT_EQ(errno, EINVAL);
		SKIP(return, "kernel without the 'T' flag");
	}
}

FIXTURE_TEARDOWN(transparent)
{
	unregister(ENTRY);
	unlink(TARGET_PATH);
	unlink(INTERP_PATH);
}

/* Grammar sanity check: the same entry without 'T' has to register. */
TEST_F(transparent, plain_entry_registers)
{
	ASSERT_EQ(write_reg(RULE("")), 0);
}

/* 'T' preserves the whole argv, so combining it with 'P' is rejected. */
TEST_F(transparent, rejects_preserve_argv0)
{
	ASSERT_NE(write_reg(RULE("TP")), 0);
	EXPECT_EQ(errno, EINVAL);
}

/* The interpreter asserts the identity the kernel built for it. */
TEST_F(transparent, dispatch)
{
	ASSERT_EQ(write_reg(RULE("T")), 0);

	setenv("BINFMT_TEST_BINARY", TARGET_PATH, 1);
	setenv("BINFMT_TEST_ARGV0", PAYLOAD_ARGV0, 1);
	EXPECT_EQ(run_payload(TARGET_PATH), 0);
}

TEST_HARNESS_MAIN
