summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/exec
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-08-31 10:06:36 +0200
committerChristian Brauner <brauner@kernel.org>2026-08-31 10:06:36 +0200
commit87b76786cc04c8137133a360088d94ab2ed592ca (patch)
tree910d782fc26434d6fd9eea934ec145d7cff7547e /tools/testing/selftests/exec
parentcee9395acd8043be0644b25c34bfa86623f2b935 (diff)
parentcd9cbd151573a6170f0a8f0fc97e3834e35012c6 (diff)
Merge patch series "binfmt_misc: only let punctuation delimit a register string"
Christian Brauner <brauner@kernel.org> says: Currently we accept arbitrary delimiters which really makes no sense and from looking around it's an unused feature. Everytime we add a flag the number of possible delimiter shrinks and it makes extensions much more hazardous than they need to be. Stop it and only accept punctional as delimiters. This extends commit 8e85d50ba111 ("binfmt_misc: reject a flag character as the field delimiter"). Letters, digits, whitespace, control characters, bytes above 0x7f and the backslash now get -EINVAL. Everything deployed uses ':' anyway. * patches from https://patch.msgid.link/20260826-work-binfmt_misc-delim-v1-0-43618adf8599@kernel.org: binfmt_misc: document the field delimiter selftests/exec: test delimiter restrictions binfmt_misc: only let punctuation delimit a register string Link: https://patch.msgid.link/20260826-work-binfmt_misc-delim-v1-0-43618adf8599@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'tools/testing/selftests/exec')
-rw-r--r--tools/testing/selftests/exec/Makefile4
-rw-r--r--tools/testing/selftests/exec/binfmt_misc_delim.c127
2 files changed, 131 insertions, 0 deletions
diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile
index b640af8f02b5..2220ed345e92 100644
--- a/tools/testing/selftests/exec/Makefile
+++ b/tools/testing/selftests/exec/Makefile
@@ -45,6 +45,10 @@ TEST_GEN_FILES += binfmt_transparent_interp
TEST_GEN_PROGS += binfmt_misc_loader
TEST_GEN_FILES += binfmt_loader_payload binfmt_loader_payload_static
+# Only ASCII punctuation delimits the fields of a register string, so a new
+# flag character cannot change which strings register. No bpf toolchain.
+TEST_GEN_PROGS += binfmt_misc_delim
+
# binfmt_misc bpf-backed ('B') handler test: a libbpf harness plus its
# struct_ops objects and the test interpreter/app it routes between. Only
# built when clang, bpftool, the vmlinux BTF and libbpf are all present
diff --git a/tools/testing/selftests/exec/binfmt_misc_delim.c b/tools/testing/selftests/exec/binfmt_misc_delim.c
new file mode 100644
index 000000000000..ffc17cb78545
--- /dev/null
+++ b/tools/testing/selftests/exec/binfmt_misc_delim.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test which characters may delimit the fields of a register string.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+
+#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