Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173// SPDX-License-Identifier: GPL-2.0/* * Test the 'D' (register disabled) flag of binfmt_misc. An entry * registered with it exists but cannot be matched until userspace enables * it, which splits a registration into create and activate. * * 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 "#DISABLED-SELFTEST#"#define TARGET_PATH "/tmp/binfmt_disabled_target"#define INTERP_PATH "/tmp/binfmt_disabled_interp.sh"#define ENTRY "test_disabled"#define RULE(flags) ":" ENTRY ":M:0:" MAGIC "::" INTERP_PATH ":" flags
/* The interpreter exits with a code the harness can recognise. */#define EXIT_INTERP 7
/* 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;}
static int create_interp(void){ char buf[64]; int fd;
unlink(INTERP_PATH); fd = open(INTERP_PATH, O_WRONLY | O_CREAT | O_EXCL, 0755); if (fd < 0) return -1; snprintf(buf, sizeof(buf), "#!/bin/sh\nexit %d\n", EXIT_INTERP); if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { close(fd); return -1; } return close(fd);}
FIXTURE(disabled) {};
FIXTURE_SETUP(disabled){ if (getuid() != 0) SKIP(return, "test must be run as root"); if (!binfmt_misc_available()) SKIP(return, "no binfmt_misc");
/* Skip the whole suite on a kernel that does not know 'D'. */ if (!binfmt_flag_supported('D')) { ASSERT_EQ(errno, EINVAL); SKIP(return, "kernel without the 'D' flag"); }
ASSERT_EQ(create_interp(), 0); ASSERT_EQ(create_target(), 0);}
FIXTURE_TEARDOWN(disabled){ unregister(ENTRY); unlink(TARGET_PATH); unlink(INTERP_PATH);}
/* The entry exists but does not dispatch until it is enabled. */TEST_F(disabled, inert_until_enabled){ ASSERT_EQ(write_reg(RULE("D")), 0); EXPECT_TRUE(entry_shows(ENTRY, "disabled"));
/* Nothing matches it, so no binary format claims the target. */ EXPECT_EQ(run_payload(TARGET_PATH), RUN_ENOEXEC);
ASSERT_EQ(entry_command(ENTRY, "1\n"), 0); EXPECT_TRUE(entry_shows(ENTRY, "enabled")); EXPECT_EQ(run_payload(TARGET_PATH), EXIT_INTERP);}
/* Without 'D' an entry is matchable the moment it is registered. */TEST_F(disabled, enabled_without_the_flag){ ASSERT_EQ(write_reg(RULE("")), 0); EXPECT_TRUE(entry_shows(ENTRY, "enabled")); EXPECT_EQ(run_payload(TARGET_PATH), EXIT_INTERP);}
/* 'D' is spent on the registration: the entry does not report it back. */TEST_F(disabled, flag_not_reported){ ASSERT_EQ(write_reg(RULE("D")), 0); EXPECT_FALSE(entry_shows(ENTRY, "flags: D")); EXPECT_TRUE(entry_shows(ENTRY, "flags: "));}
/* A disabled entry can be disabled and enabled like any other. */TEST_F(disabled, toggles_like_any_entry){ ASSERT_EQ(write_reg(RULE("D")), 0);
ASSERT_EQ(entry_command(ENTRY, "1\n"), 0); ASSERT_EQ(run_payload(TARGET_PATH), EXIT_INTERP); ASSERT_EQ(entry_command(ENTRY, "0\n"), 0); EXPECT_EQ(run_payload(TARGET_PATH), RUN_ENOEXEC); ASSERT_EQ(entry_command(ENTRY, "1\n"), 0); EXPECT_EQ(run_payload(TARGET_PATH), EXIT_INTERP);}
/* 'D' composes with the invocation flags a static entry can carry. */TEST_F(disabled, composes_with_invocation_flags){ ASSERT_EQ(write_reg(RULE("PD")), 0); EXPECT_TRUE(entry_shows(ENTRY, "disabled")); EXPECT_TRUE(entry_shows(ENTRY, "flags: P"));}
/* '-1' to the status file sweeps a staged entry with everything else. */TEST_F(disabled, removed_by_remove_all){ int fd;
ASSERT_EQ(write_reg(RULE("D")), 0); EXPECT_TRUE(entry_shows(ENTRY, "disabled"));
fd = open(BINFMT_DIR "/status", O_WRONLY | O_CLOEXEC); ASSERT_GE(fd, 0); ASSERT_EQ(write(fd, "-1", 2), 2); close(fd);
EXPECT_NE(access(BINFMT_DIR "/" ENTRY, F_OK), 0);}
/* A file handle held across a removal cannot resurrect the entry. */TEST_F(disabled, no_resurrection_after_remove){ int fd;
ASSERT_EQ(write_reg(RULE("D")), 0); fd = open(BINFMT_DIR "/" ENTRY, O_WRONLY | O_CLOEXEC); ASSERT_GE(fd, 0);
ASSERT_EQ(write(fd, "-1", 2), 2); EXPECT_NE(access(BINFMT_DIR "/" ENTRY, F_OK), 0);
/* Accepted like any toggle of a removed entry, but publishes nothing. */ EXPECT_EQ(write(fd, "1", 1), 1); EXPECT_EQ(run_payload(TARGET_PATH), RUN_ENOEXEC); close(fd);}
TEST_HARNESS_MAIN