xref: /linux/tools/testing/selftests/exec/binfmt_misc_selfpin.c (revision b9cba7ebfe539f3e4bbdd03a1e0efa3b30b3f592)
1*25757bc8SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*25757bc8SChristian Brauner /*
3*25757bc8SChristian Brauner  * An 'F' entry keeps its interpreter open for as long as the entry exists,
4*25757bc8SChristian Brauner  * and the entry only goes away when the binfmt_misc superblock is destroyed.
5*25757bc8SChristian Brauner  * An interpreter that lives on a mount which in turn keeps that superblock
6*25757bc8SChristian Brauner  * alive therefore pins the instance that owns it, and nothing can break the
7*25757bc8SChristian Brauner  * cycle. Check the two ways userspace could arrange for that: an interpreter
8*25757bc8SChristian Brauner  * on the binfmt_misc instance itself, and one on a filesystem stacked on it.
9*25757bc8SChristian Brauner  *
10*25757bc8SChristian Brauner  * Runs unprivileged in a user namespace; binfmt_misc is FS_USERNS_MOUNT.
11*25757bc8SChristian Brauner  */
12*25757bc8SChristian Brauner #define _GNU_SOURCE
13*25757bc8SChristian Brauner #include <fcntl.h>
14*25757bc8SChristian Brauner #include <limits.h>
15*25757bc8SChristian Brauner #include <sched.h>
16*25757bc8SChristian Brauner #include <sys/mount.h>
17*25757bc8SChristian Brauner #include <sys/stat.h>
18*25757bc8SChristian Brauner 
19*25757bc8SChristian Brauner #include "../filesystems/utils.h"
20*25757bc8SChristian Brauner #include "kselftest_harness.h"
21*25757bc8SChristian Brauner 
22*25757bc8SChristian Brauner #define MNT		"/tmp/binfmt_selfpin"
23*25757bc8SChristian Brauner #define BACKING		"/tmp/binfmt_selfpin_back"
24*25757bc8SChristian Brauner #define LOWER		BACKING "/lower"
25*25757bc8SChristian Brauner #define MERGED		"/tmp/binfmt_selfpin_merged"
26*25757bc8SChristian Brauner 
27*25757bc8SChristian Brauner #define MAGIC		"\\xde\\xad"
28*25757bc8SChristian Brauner #define RULE(interp)	":selfpin:M::" MAGIC "::" interp ":F"
29*25757bc8SChristian Brauner /* Not on the instance, and unlike /bin/true it always exists. */
30*25757bc8SChristian Brauner #define INTERP		"/proc/self/exe"
31*25757bc8SChristian Brauner 
32*25757bc8SChristian Brauner #define OPTS_MAX	(3 * PATH_MAX + 64)
33*25757bc8SChristian Brauner 
34*25757bc8SChristian Brauner static int ensure_dir(const char *path)
35*25757bc8SChristian Brauner {
36*25757bc8SChristian Brauner 	if (mkdir(path, 0755) && errno != EEXIST)
37*25757bc8SChristian Brauner 		return -1;
38*25757bc8SChristian Brauner 	return 0;
39*25757bc8SChristian Brauner }
40*25757bc8SChristian Brauner 
41*25757bc8SChristian Brauner /* Write @rule to this instance's register file, preserving write(2)'s errno. */
42*25757bc8SChristian Brauner static int register_at(struct __test_metadata *_metadata, const char *rule)
43*25757bc8SChristian Brauner {
44*25757bc8SChristian Brauner 	int fd, saved;
45*25757bc8SChristian Brauner 	ssize_t n;
46*25757bc8SChristian Brauner 
47*25757bc8SChristian Brauner 	fd = open(MNT "/register", O_WRONLY);
48*25757bc8SChristian Brauner 	ASSERT_GE(fd, 0);
49*25757bc8SChristian Brauner 	n = write(fd, rule, strlen(rule));
50*25757bc8SChristian Brauner 	saved = errno;
51*25757bc8SChristian Brauner 	close(fd);
52*25757bc8SChristian Brauner 	errno = saved;
53*25757bc8SChristian Brauner 	return n < 0 ? -1 : 0;
54*25757bc8SChristian Brauner }
55*25757bc8SChristian Brauner 
56*25757bc8SChristian Brauner /*
57*25757bc8SChristian Brauner  * Mount an overlay over @lower using a private upper/work pair, so the two
58*25757bc8SChristian Brauner  * mounts this test performs cannot interfere with each other and neither
59*25757bc8SChristian Brauner  * overlaps the lower layer.
60*25757bc8SChristian Brauner  */
61*25757bc8SChristian Brauner static int mount_overlay(const char *lower, int nr)
62*25757bc8SChristian Brauner {
63*25757bc8SChristian Brauner 	char opts[OPTS_MAX], upper[PATH_MAX], work[PATH_MAX];
64*25757bc8SChristian Brauner 
65*25757bc8SChristian Brauner 	snprintf(upper, sizeof(upper), "%s/upper%d", BACKING, nr);
66*25757bc8SChristian Brauner 	snprintf(work, sizeof(work), "%s/work%d", BACKING, nr);
67*25757bc8SChristian Brauner 	if (mkdir(upper, 0755) || mkdir(work, 0755))
68*25757bc8SChristian Brauner 		return -1;
69*25757bc8SChristian Brauner 
70*25757bc8SChristian Brauner 	snprintf(opts, sizeof(opts), "lowerdir=%s,upperdir=%s,workdir=%s",
71*25757bc8SChristian Brauner 		 lower, upper, work);
72*25757bc8SChristian Brauner 	return mount("ovl", MERGED, "overlay", 0, opts);
73*25757bc8SChristian Brauner }
74*25757bc8SChristian Brauner 
75*25757bc8SChristian Brauner FIXTURE(selfpin) {
76*25757bc8SChristian Brauner };
77*25757bc8SChristian Brauner 
78*25757bc8SChristian Brauner FIXTURE_SETUP(selfpin)
79*25757bc8SChristian Brauner {
80*25757bc8SChristian Brauner 	/* setup_userns() exits rather than returns if this is not there. */
81*25757bc8SChristian Brauner 	if (access("/proc/self/ns/user", F_OK))
82*25757bc8SChristian Brauner 		SKIP(return, "kernel without user namespaces");
83*25757bc8SChristian Brauner 	ASSERT_EQ(setup_userns(), 0);
84*25757bc8SChristian Brauner 
85*25757bc8SChristian Brauner 	ASSERT_EQ(ensure_dir(MNT), 0);
86*25757bc8SChristian Brauner 	if (mount("binfmt_misc", MNT, "binfmt_misc", 0, NULL)) {
87*25757bc8SChristian Brauner 		int saved = errno;
88*25757bc8SChristian Brauner 
89*25757bc8SChristian Brauner 		/* Teardown doesn't run when setup skips, so clean up here. */
90*25757bc8SChristian Brauner 		rmdir(MNT);
91*25757bc8SChristian Brauner 		SKIP(return, "no binfmt_misc: %s", strerror(saved));
92*25757bc8SChristian Brauner 	}
93*25757bc8SChristian Brauner }
94*25757bc8SChristian Brauner 
95*25757bc8SChristian Brauner FIXTURE_TEARDOWN(selfpin)
96*25757bc8SChristian Brauner {
97*25757bc8SChristian Brauner 	/* The namespaces go with the process; just don't litter /tmp. */
98*25757bc8SChristian Brauner 	umount2(MERGED, MNT_DETACH);
99*25757bc8SChristian Brauner 	umount2(BACKING, MNT_DETACH);
100*25757bc8SChristian Brauner 	umount2(MNT, MNT_DETACH);
101*25757bc8SChristian Brauner 	rmdir(MERGED);
102*25757bc8SChristian Brauner 	rmdir(BACKING);
103*25757bc8SChristian Brauner 	rmdir(MNT);
104*25757bc8SChristian Brauner }
105*25757bc8SChristian Brauner 
106*25757bc8SChristian Brauner /*
107*25757bc8SChristian Brauner  * The instance's own files are regular files the mounter owns, so they can be
108*25757bc8SChristian Brauner  * made executable. Opening one for exec still has to fail, otherwise the entry
109*25757bc8SChristian Brauner  * pins the very superblock it lives in.
110*25757bc8SChristian Brauner  */
111*25757bc8SChristian Brauner TEST_F(selfpin, interpreter_on_the_instance)
112*25757bc8SChristian Brauner {
113*25757bc8SChristian Brauner 	ASSERT_EQ(chmod(MNT "/status", 0755), 0);
114*25757bc8SChristian Brauner 
115*25757bc8SChristian Brauner 	ASSERT_NE(register_at(_metadata, RULE(MNT "/status")), 0);
116*25757bc8SChristian Brauner 	EXPECT_EQ(errno, EACCES);
117*25757bc8SChristian Brauner }
118*25757bc8SChristian Brauner 
119*25757bc8SChristian Brauner /* Same for an entry file rather than one of the control files. */
120*25757bc8SChristian Brauner TEST_F(selfpin, interpreter_on_an_entry)
121*25757bc8SChristian Brauner {
122*25757bc8SChristian Brauner 	ASSERT_EQ(register_at(_metadata, ":victim:M::" MAGIC "::" INTERP ":"), 0);
123*25757bc8SChristian Brauner 	ASSERT_EQ(chmod(MNT "/victim", 0755), 0);
124*25757bc8SChristian Brauner 
125*25757bc8SChristian Brauner 	ASSERT_NE(register_at(_metadata, RULE(MNT "/victim")), 0);
126*25757bc8SChristian Brauner 	EXPECT_EQ(errno, EACCES);
127*25757bc8SChristian Brauner }
128*25757bc8SChristian Brauner 
129*25757bc8SChristian Brauner /*
130*25757bc8SChristian Brauner  * A stacking filesystem holds a private clone of each layer for its whole
131*25757bc8SChristian Brauner  * lifetime, so an instance used as a layer can be pinned by an interpreter
132*25757bc8SChristian Brauner  * that does not live on it at all. Refuse to be a layer.
133*25757bc8SChristian Brauner  */
134*25757bc8SChristian Brauner TEST_F(selfpin, refuses_to_be_stacked_on)
135*25757bc8SChristian Brauner {
136*25757bc8SChristian Brauner 	ASSERT_EQ(ensure_dir(BACKING), 0);
137*25757bc8SChristian Brauner 	ASSERT_EQ(mount("tmpfs", BACKING, "tmpfs", 0, NULL), 0);
138*25757bc8SChristian Brauner 	ASSERT_EQ(mkdir(LOWER, 0755), 0);
139*25757bc8SChristian Brauner 	ASSERT_EQ(ensure_dir(MERGED), 0);
140*25757bc8SChristian Brauner 
141*25757bc8SChristian Brauner 	/* Nothing to prove unless overlayfs works here at all. */
142*25757bc8SChristian Brauner 	if (mount_overlay(LOWER, 1)) {
143*25757bc8SChristian Brauner 		if (errno == ENODEV || errno == EPERM)
144*25757bc8SChristian Brauner 			SKIP(return, "no unprivileged overlayfs");
145*25757bc8SChristian Brauner 		SKIP(return, "overlayfs unusable here: %s", strerror(errno));
146*25757bc8SChristian Brauner 	}
147*25757bc8SChristian Brauner 	ASSERT_EQ(umount(MERGED), 0);
148*25757bc8SChristian Brauner 
149*25757bc8SChristian Brauner 	EXPECT_NE(mount_overlay(MNT, 2), 0);
150*25757bc8SChristian Brauner }
151*25757bc8SChristian Brauner 
152*25757bc8SChristian Brauner /* An ordinary interpreter still registers with 'F'. */
153*25757bc8SChristian Brauner TEST_F(selfpin, ordinary_interpreter_still_works)
154*25757bc8SChristian Brauner {
155*25757bc8SChristian Brauner 	EXPECT_EQ(register_at(_metadata, RULE(INTERP)), 0);
156*25757bc8SChristian Brauner }
157*25757bc8SChristian Brauner 
158*25757bc8SChristian Brauner TEST_HARNESS_MAIN
159