summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/exec/binfmt_misc_delim.c
blob: ffc17cb7854502d2c6f3250bee11c396c8f18ab5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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