xref: /linux/tools/testing/selftests/exec/binfmt_misc_delim.c (revision 14c897740388216acd59477a382de327d5e27ba4)
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 ':'. */
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. */
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 
60 FIXTURE(delim) {
61 };
62 
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 
77 FIXTURE_TEARDOWN(delim)
78 {
79 	unregister(ENTRY);
80 }
81 
82 /* Punctuation delimits, which is all anything deployed ever uses. */
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. */
95 TEST_F(delim, letters_refused)
96 {
97 	expect_refused(_metadata, LETTERS);
98 }
99 
100 /* The offset field is written in digits. */
101 TEST_F(delim, digits_refused)
102 {
103 	expect_refused(_metadata, DIGITS);
104 }
105 
106 TEST_F(delim, whitespace_refused)
107 {
108 	expect_refused(_metadata, WHITESPACE);
109 }
110 
111 TEST_F(delim, control_refused)
112 {
113 	expect_refused(_metadata, CONTROL);
114 }
115 
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. */
122 TEST_F(delim, backslash_refused)
123 {
124 	expect_refused(_metadata, "\\");
125 }
126 
127 TEST_HARNESS_MAIN
128