1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Test which characters may delimit the fields of a register string.
4 */
5 #define _GNU_SOURCE
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 #include "binfmt_misc_common.h"
10 #include "kselftest_harness.h"
11
12 #define ENTRY "bmdelim"
13 /* Shares no character with the sets below, or a refusal proves nothing. */
14 #define MAGIC "bmmagic"
15 #define INTERP "/bin/true"
16
17 /*
18 * ASCII punctuation without '\' and '/'. The backslash is refused because
19 * it would cut a magic that uses \x to escape short. '/' is accepted
20 * but cannot delimit a rule that names an absolute interpreter.
21 */
22 #define PUNCTUATION "!\"#$%&'()*+,-.:;<=>?@[]^_`{|}~"
23
24 /* 'M', 'E' and 'B' name types, 'P' through 'D' are the flags. */
25 #define LETTERS "MEBPOCFTLDqz"
26 #define DIGITS "0157"
27 #define WHITESPACE " \t\n"
28 #define CONTROL "\001\033\177"
29 #define NON_ASCII "\200\244\377"
30
31 /* ':bmdelim:E::bmmagic::/bin/true:' with @del in place of every ':'. */
register_with(char del)32 static int register_with(char del)
33 {
34 char rule[128];
35
36 snprintf(rule, sizeof(rule), "%c%s%cE%c%c%s%c%c%s%c", del, ENTRY, del,
37 del, del, MAGIC, del, del, INTERP, del);
38 return write_reg(rule);
39 }
40
41 /* No character of @set may delimit a register string. */
expect_refused(struct __test_metadata * _metadata,const char * set)42 static void expect_refused(struct __test_metadata *_metadata, const char *set)
43 {
44 const char *d;
45
46 for (d = set; *d; d++) {
47 int rc = register_with(*d);
48
49 EXPECT_EQ(rc, -1)
50 TH_LOG("%#x delimited a register string",
51 (unsigned char)*d);
52 if (rc == 0) {
53 unregister(ENTRY);
54 continue;
55 }
56 EXPECT_EQ(errno, EINVAL);
57 }
58 }
59
FIXTURE(delim)60 FIXTURE(delim) {
61 };
62
FIXTURE_SETUP(delim)63 FIXTURE_SETUP(delim)
64 {
65 if (getuid() != 0)
66 SKIP(return, "test must be run as root");
67 if (!binfmt_misc_available())
68 SKIP(return, "no binfmt_misc");
69
70 /* A kernel without the allow-list takes any character but a flag. */
71 if (register_with('q') == 0) {
72 unregister(ENTRY);
73 SKIP(return, "kernel without the delimiter allow-list");
74 }
75 }
76
FIXTURE_TEARDOWN(delim)77 FIXTURE_TEARDOWN(delim)
78 {
79 unregister(ENTRY);
80 }
81
82 /* Punctuation delimits, which is all anything deployed ever uses. */
TEST_F(delim,punctuation_accepted)83 TEST_F(delim, punctuation_accepted)
84 {
85 const char *d;
86
87 for (d = PUNCTUATION; *d; d++) {
88 EXPECT_EQ(register_with(*d), 0)
89 TH_LOG("'%c' refused with errno %d", *d, errno);
90 unregister(ENTRY);
91 }
92 }
93
94 /* Letters name the types and the flags, so none of them can delimit. */
TEST_F(delim,letters_refused)95 TEST_F(delim, letters_refused)
96 {
97 expect_refused(_metadata, LETTERS);
98 }
99
100 /* The offset field is written in digits. */
TEST_F(delim,digits_refused)101 TEST_F(delim, digits_refused)
102 {
103 expect_refused(_metadata, DIGITS);
104 }
105
TEST_F(delim,whitespace_refused)106 TEST_F(delim, whitespace_refused)
107 {
108 expect_refused(_metadata, WHITESPACE);
109 }
110
TEST_F(delim,control_refused)111 TEST_F(delim, control_refused)
112 {
113 expect_refused(_metadata, CONTROL);
114 }
115
TEST_F(delim,non_ascii_refused)116 TEST_F(delim, non_ascii_refused)
117 {
118 expect_refused(_metadata, NON_ASCII);
119 }
120
121 /* The escape character would cut every magic that uses one short. */
TEST_F(delim,backslash_refused)122 TEST_F(delim, backslash_refused)
123 {
124 expect_refused(_metadata, "\\");
125 }
126
127 TEST_HARNESS_MAIN
128