xref: /linux/tools/testing/selftests/filesystems/binderfs/binderfs_test.c (revision dd5b2498d845f925904cb2afabb6ba11bfc317c5)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define _GNU_SOURCE
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <sched.h>
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12 #include <sys/mount.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <linux/android/binder.h>
17 #include <linux/android/binderfs.h>
18 #include "../../kselftest.h"
19 
20 static ssize_t write_nointr(int fd, const void *buf, size_t count)
21 {
22 	ssize_t ret;
23 again:
24 	ret = write(fd, buf, count);
25 	if (ret < 0 && errno == EINTR)
26 		goto again;
27 
28 	return ret;
29 }
30 
31 static void write_to_file(const char *filename, const void *buf, size_t count,
32 			  int allowed_errno)
33 {
34 	int fd, saved_errno;
35 	ssize_t ret;
36 
37 	fd = open(filename, O_WRONLY | O_CLOEXEC);
38 	if (fd < 0)
39 		ksft_exit_fail_msg("%s - Failed to open file %s\n",
40 				   strerror(errno), filename);
41 
42 	ret = write_nointr(fd, buf, count);
43 	if (ret < 0) {
44 		if (allowed_errno && (errno == allowed_errno)) {
45 			close(fd);
46 			return;
47 		}
48 
49 		goto on_error;
50 	}
51 
52 	if ((size_t)ret != count)
53 		goto on_error;
54 
55 	close(fd);
56 	return;
57 
58 on_error:
59 	saved_errno = errno;
60 	close(fd);
61 	errno = saved_errno;
62 
63 	if (ret < 0)
64 		ksft_exit_fail_msg("%s - Failed to write to file %s\n",
65 				   strerror(errno), filename);
66 
67 	ksft_exit_fail_msg("Failed to write to file %s\n", filename);
68 }
69 
70 static void change_to_userns(void)
71 {
72 	int ret;
73 	uid_t uid;
74 	gid_t gid;
75 	/* {g,u}id_map files only allow a max of 4096 bytes written to them */
76 	char idmap[4096];
77 
78 	uid = getuid();
79 	gid = getgid();
80 
81 	ret = unshare(CLONE_NEWUSER);
82 	if (ret < 0)
83 		ksft_exit_fail_msg("%s - Failed to unshare user namespace\n",
84 				   strerror(errno));
85 
86 	write_to_file("/proc/self/setgroups", "deny", strlen("deny"), ENOENT);
87 
88 	ret = snprintf(idmap, sizeof(idmap), "0 %d 1", uid);
89 	if (ret < 0 || (size_t)ret >= sizeof(idmap))
90 		ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
91 				   strerror(errno));
92 
93 	write_to_file("/proc/self/uid_map", idmap, strlen(idmap), 0);
94 
95 	ret = snprintf(idmap, sizeof(idmap), "0 %d 1", gid);
96 	if (ret < 0 || (size_t)ret >= sizeof(idmap))
97 		ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
98 				   strerror(errno));
99 
100 	write_to_file("/proc/self/gid_map", idmap, strlen(idmap), 0);
101 
102 	ret = setgid(0);
103 	if (ret)
104 		ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
105 				   strerror(errno));
106 
107 	ret = setuid(0);
108 	if (ret)
109 		ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
110 				   strerror(errno));
111 }
112 
113 static void change_to_mountns(void)
114 {
115 	int ret;
116 
117 	ret = unshare(CLONE_NEWNS);
118 	if (ret < 0)
119 		ksft_exit_fail_msg("%s - Failed to unshare mount namespace\n",
120 				   strerror(errno));
121 
122 	ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
123 	if (ret < 0)
124 		ksft_exit_fail_msg("%s - Failed to mount / as private\n",
125 				   strerror(errno));
126 }
127 
128 static void rmdir_protect_errno(const char *dir)
129 {
130 	int saved_errno = errno;
131 	(void)rmdir(dir);
132 	errno = saved_errno;
133 }
134 
135 static void __do_binderfs_test(void)
136 {
137 	int fd, ret, saved_errno;
138 	size_t len;
139 	ssize_t wret;
140 	bool keep = false;
141 	struct binderfs_device device = { 0 };
142 	struct binder_version version = { 0 };
143 
144 	change_to_mountns();
145 
146 	ret = mkdir("/dev/binderfs", 0755);
147 	if (ret < 0) {
148 		if (errno != EEXIST)
149 			ksft_exit_fail_msg(
150 				"%s - Failed to create binderfs mountpoint\n",
151 				strerror(errno));
152 
153 		keep = true;
154 	}
155 
156 	ret = mount(NULL, "/dev/binderfs", "binder", 0, 0);
157 	if (ret < 0) {
158 		if (errno != ENODEV)
159 			ksft_exit_fail_msg("%s - Failed to mount binderfs\n",
160 					   strerror(errno));
161 
162 		keep ? : rmdir_protect_errno("/dev/binderfs");
163 		ksft_exit_skip(
164 			"The Android binderfs filesystem is not available\n");
165 	}
166 
167 	/* binderfs mount test passed */
168 	ksft_inc_pass_cnt();
169 
170 	memcpy(device.name, "my-binder", strlen("my-binder"));
171 
172 	fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
173 	if (fd < 0)
174 		ksft_exit_fail_msg(
175 			"%s - Failed to open binder-control device\n",
176 			strerror(errno));
177 
178 	ret = ioctl(fd, BINDER_CTL_ADD, &device);
179 	saved_errno = errno;
180 	close(fd);
181 	errno = saved_errno;
182 	if (ret < 0) {
183 		keep ? : rmdir_protect_errno("/dev/binderfs");
184 		ksft_exit_fail_msg(
185 			"%s - Failed to allocate new binder device\n",
186 			strerror(errno));
187 	}
188 
189 	ksft_print_msg(
190 		"Allocated new binder device with major %d, minor %d, and name %s\n",
191 		device.major, device.minor, device.name);
192 
193 	/* binder device allocation test passed */
194 	ksft_inc_pass_cnt();
195 
196 	fd = open("/dev/binderfs/my-binder", O_CLOEXEC | O_RDONLY);
197 	if (fd < 0) {
198 		keep ? : rmdir_protect_errno("/dev/binderfs");
199 		ksft_exit_fail_msg("%s - Failed to open my-binder device\n",
200 				   strerror(errno));
201 	}
202 
203 	ret = ioctl(fd, BINDER_VERSION, &version);
204 	saved_errno = errno;
205 	close(fd);
206 	errno = saved_errno;
207 	if (ret < 0) {
208 		keep ? : rmdir_protect_errno("/dev/binderfs");
209 		ksft_exit_fail_msg(
210 			"%s - Failed to open perform BINDER_VERSION request\n",
211 			strerror(errno));
212 	}
213 
214 	ksft_print_msg("Detected binder version: %d\n",
215 		       version.protocol_version);
216 
217 	/* binder transaction with binderfs binder device passed */
218 	ksft_inc_pass_cnt();
219 
220 	ret = unlink("/dev/binderfs/my-binder");
221 	if (ret < 0) {
222 		keep ? : rmdir_protect_errno("/dev/binderfs");
223 		ksft_exit_fail_msg("%s - Failed to delete binder device\n",
224 				   strerror(errno));
225 	}
226 
227 	/* binder device removal passed */
228 	ksft_inc_pass_cnt();
229 
230 	ret = unlink("/dev/binderfs/binder-control");
231 	if (!ret) {
232 		keep ? : rmdir_protect_errno("/dev/binderfs");
233 		ksft_exit_fail_msg("Managed to delete binder-control device\n");
234 	} else if (errno != EPERM) {
235 		keep ? : rmdir_protect_errno("/dev/binderfs");
236 		ksft_exit_fail_msg(
237 			"%s - Failed to delete binder-control device but exited with unexpected error code\n",
238 			strerror(errno));
239 	}
240 
241 	/* binder-control device removal failed as expected */
242 	ksft_inc_xfail_cnt();
243 
244 on_error:
245 	ret = umount2("/dev/binderfs", MNT_DETACH);
246 	keep ?: rmdir_protect_errno("/dev/binderfs");
247 	if (ret < 0)
248 		ksft_exit_fail_msg("%s - Failed to unmount binderfs\n",
249 				   strerror(errno));
250 
251 	/* binderfs unmount test passed */
252 	ksft_inc_pass_cnt();
253 }
254 
255 static void binderfs_test_privileged()
256 {
257 	if (geteuid() != 0)
258 		ksft_print_msg(
259 			"Tests are not run as root. Skipping privileged tests\n");
260 	else
261 		__do_binderfs_test();
262 }
263 
264 static void binderfs_test_unprivileged()
265 {
266 	change_to_userns();
267 	__do_binderfs_test();
268 }
269 
270 int main(int argc, char *argv[])
271 {
272 	binderfs_test_privileged();
273 	binderfs_test_unprivileged();
274 	ksft_exit_pass();
275 }
276