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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// SPDX-License-Identifier: GPL-2.0
/*
* An 'F' entry keeps its interpreter open for as long as the entry exists,
* and the entry only goes away when the binfmt_misc superblock is destroyed.
* An interpreter that lives on a mount which in turn keeps that superblock
* alive therefore pins the instance that owns it, and nothing can break the
* cycle. Check the two ways userspace could arrange for that: an interpreter
* on the binfmt_misc instance itself, and one on a filesystem stacked on it.
*
* Runs unprivileged in a user namespace; binfmt_misc is FS_USERNS_MOUNT.
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <limits.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include "../filesystems/utils.h"
#include "kselftest_harness.h"
#define MNT "/tmp/binfmt_selfpin"
#define BACKING "/tmp/binfmt_selfpin_back"
#define LOWER BACKING "/lower"
#define MERGED "/tmp/binfmt_selfpin_merged"
#define MAGIC "\\xde\\xad"
#define RULE(interp) ":selfpin:M::" MAGIC "::" interp ":F"
/* Not on the instance, and unlike /bin/true it always exists. */
#define INTERP "/proc/self/exe"
#define OPTS_MAX (3 * PATH_MAX + 64)
static int ensure_dir(const char *path)
{
if (mkdir(path, 0755) && errno != EEXIST)
return -1;
return 0;
}
/* Write @rule to this instance's register file, preserving write(2)'s errno. */
static int register_at(struct __test_metadata *_metadata, const char *rule)
{
int fd, saved;
ssize_t n;
fd = open(MNT "/register", O_WRONLY);
ASSERT_GE(fd, 0);
n = write(fd, rule, strlen(rule));
saved = errno;
close(fd);
errno = saved;
return n < 0 ? -1 : 0;
}
/*
* Mount an overlay over @lower using a private upper/work pair, so the two
* mounts this test performs cannot interfere with each other and neither
* overlaps the lower layer.
*/
static int mount_overlay(const char *lower, int nr)
{
char opts[OPTS_MAX], upper[PATH_MAX], work[PATH_MAX];
snprintf(upper, sizeof(upper), "%s/upper%d", BACKING, nr);
snprintf(work, sizeof(work), "%s/work%d", BACKING, nr);
if (mkdir(upper, 0755) || mkdir(work, 0755))
return -1;
snprintf(opts, sizeof(opts), "lowerdir=%s,upperdir=%s,workdir=%s",
lower, upper, work);
return mount("ovl", MERGED, "overlay", 0, opts);
}
FIXTURE(selfpin) {
};
FIXTURE_SETUP(selfpin)
{
/* setup_userns() exits rather than returns if this is not there. */
if (access("/proc/self/ns/user", F_OK))
SKIP(return, "kernel without user namespaces");
ASSERT_EQ(setup_userns(), 0);
ASSERT_EQ(ensure_dir(MNT), 0);
if (mount("binfmt_misc", MNT, "binfmt_misc", 0, NULL)) {
int saved = errno;
/* Teardown doesn't run when setup skips, so clean up here. */
rmdir(MNT);
SKIP(return, "no binfmt_misc: %s", strerror(saved));
}
}
FIXTURE_TEARDOWN(selfpin)
{
/* The namespaces go with the process; just don't litter /tmp. */
umount2(MERGED, MNT_DETACH);
umount2(BACKING, MNT_DETACH);
umount2(MNT, MNT_DETACH);
rmdir(MERGED);
rmdir(BACKING);
rmdir(MNT);
}
/*
* The instance's own files are regular files the mounter owns, so they can be
* made executable. Opening one for exec still has to fail, otherwise the entry
* pins the very superblock it lives in.
*/
TEST_F(selfpin, interpreter_on_the_instance)
{
ASSERT_EQ(chmod(MNT "/status", 0755), 0);
ASSERT_NE(register_at(_metadata, RULE(MNT "/status")), 0);
EXPECT_EQ(errno, EACCES);
}
/* Same for an entry file rather than one of the control files. */
TEST_F(selfpin, interpreter_on_an_entry)
{
ASSERT_EQ(register_at(_metadata, ":victim:M::" MAGIC "::" INTERP ":"), 0);
ASSERT_EQ(chmod(MNT "/victim", 0755), 0);
ASSERT_NE(register_at(_metadata, RULE(MNT "/victim")), 0);
EXPECT_EQ(errno, EACCES);
}
/*
* A stacking filesystem holds a private clone of each layer for its whole
* lifetime, so an instance used as a layer can be pinned by an interpreter
* that does not live on it at all. Refuse to be a layer.
*/
TEST_F(selfpin, refuses_to_be_stacked_on)
{
ASSERT_EQ(ensure_dir(BACKING), 0);
ASSERT_EQ(mount("tmpfs", BACKING, "tmpfs", 0, NULL), 0);
ASSERT_EQ(mkdir(LOWER, 0755), 0);
ASSERT_EQ(ensure_dir(MERGED), 0);
/* Nothing to prove unless overlayfs works here at all. */
if (mount_overlay(LOWER, 1)) {
if (errno == ENODEV || errno == EPERM)
SKIP(return, "no unprivileged overlayfs");
SKIP(return, "overlayfs unusable here: %s", strerror(errno));
}
ASSERT_EQ(umount(MERGED), 0);
EXPECT_NE(mount_overlay(MNT, 2), 0);
}
/* An ordinary interpreter still registers with 'F'. */
TEST_F(selfpin, ordinary_interpreter_still_works)
{
EXPECT_EQ(register_at(_metadata, RULE(INTERP)), 0);
}
TEST_HARNESS_MAIN
|