// SPDX-License-Identifier: GPL-2.0 /* * Test which characters may delimit the fields of a register string. */ #define _GNU_SOURCE #include #include #include "binfmt_misc_common.h" #include "kselftest_harness.h" #define ENTRY "bmdelim" /* Shares no character with the sets below, or a refusal proves nothing. */ #define MAGIC "bmmagic" #define INTERP "/bin/true" /* * ASCII punctuation without '\' and '/'. The backslash is refused because * it would cut a magic that uses \x to escape short. '/' is accepted * but cannot delimit a rule that names an absolute interpreter. */ #define PUNCTUATION "!\"#$%&'()*+,-.:;<=>?@[]^_`{|}~" /* 'M', 'E' and 'B' name types, 'P' through 'D' are the flags. */ #define LETTERS "MEBPOCFTLDqz" #define DIGITS "0157" #define WHITESPACE " \t\n" #define CONTROL "\001\033\177" #define NON_ASCII "\200\244\377" /* ':bmdelim:E::bmmagic::/bin/true:' with @del in place of every ':'. */ static int register_with(char del) { char rule[128]; snprintf(rule, sizeof(rule), "%c%s%cE%c%c%s%c%c%s%c", del, ENTRY, del, del, del, MAGIC, del, del, INTERP, del); return write_reg(rule); } /* No character of @set may delimit a register string. */ static void expect_refused(struct __test_metadata *_metadata, const char *set) { const char *d; for (d = set; *d; d++) { int rc = register_with(*d); EXPECT_EQ(rc, -1) TH_LOG("%#x delimited a register string", (unsigned char)*d); if (rc == 0) { unregister(ENTRY); continue; } EXPECT_EQ(errno, EINVAL); } } FIXTURE(delim) { }; FIXTURE_SETUP(delim) { if (getuid() != 0) SKIP(return, "test must be run as root"); if (!binfmt_misc_available()) SKIP(return, "no binfmt_misc"); /* A kernel without the allow-list takes any character but a flag. */ if (register_with('q') == 0) { unregister(ENTRY); SKIP(return, "kernel without the delimiter allow-list"); } } FIXTURE_TEARDOWN(delim) { unregister(ENTRY); } /* Punctuation delimits, which is all anything deployed ever uses. */ TEST_F(delim, punctuation_accepted) { const char *d; for (d = PUNCTUATION; *d; d++) { EXPECT_EQ(register_with(*d), 0) TH_LOG("'%c' refused with errno %d", *d, errno); unregister(ENTRY); } } /* Letters name the types and the flags, so none of them can delimit. */ TEST_F(delim, letters_refused) { expect_refused(_metadata, LETTERS); } /* The offset field is written in digits. */ TEST_F(delim, digits_refused) { expect_refused(_metadata, DIGITS); } TEST_F(delim, whitespace_refused) { expect_refused(_metadata, WHITESPACE); } TEST_F(delim, control_refused) { expect_refused(_metadata, CONTROL); } TEST_F(delim, non_ascii_refused) { expect_refused(_metadata, NON_ASCII); } /* The escape character would cut every magic that uses one short. */ TEST_F(delim, backslash_refused) { expect_refused(_metadata, "\\"); } TEST_HARNESS_MAIN