binderfs_test.c 6.41 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// SPDX-License-Identifier: GPL-2.0

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder.h>
#include <linux/android/binderfs.h>
18

19
#include "../../kselftest.h"
20
#include "../../kselftest_harness.h"
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

static ssize_t write_nointr(int fd, const void *buf, size_t count)
{
	ssize_t ret;
again:
	ret = write(fd, buf, count);
	if (ret < 0 && errno == EINTR)
		goto again;

	return ret;
}

static void write_to_file(const char *filename, const void *buf, size_t count,
			  int allowed_errno)
{
	int fd, saved_errno;
	ssize_t ret;

	fd = open(filename, O_WRONLY | O_CLOEXEC);
	if (fd < 0)
		ksft_exit_fail_msg("%s - Failed to open file %s\n",
				   strerror(errno), filename);

	ret = write_nointr(fd, buf, count);
	if (ret < 0) {
		if (allowed_errno && (errno == allowed_errno)) {
			close(fd);
			return;
		}

		goto on_error;
	}

	if ((size_t)ret != count)
		goto on_error;

	close(fd);
	return;

on_error:
	saved_errno = errno;
	close(fd);
	errno = saved_errno;

	if (ret < 0)
		ksft_exit_fail_msg("%s - Failed to write to file %s\n",
				   strerror(errno), filename);

	ksft_exit_fail_msg("Failed to write to file %s\n", filename);
}

static void change_to_userns(void)
{
	int ret;
	uid_t uid;
	gid_t gid;
	/* {g,u}id_map files only allow a max of 4096 bytes written to them */
	char idmap[4096];

	uid = getuid();
	gid = getgid();

	ret = unshare(CLONE_NEWUSER);
	if (ret < 0)
		ksft_exit_fail_msg("%s - Failed to unshare user namespace\n",
				   strerror(errno));

	write_to_file("/proc/self/setgroups", "deny", strlen("deny"), ENOENT);

	ret = snprintf(idmap, sizeof(idmap), "0 %d 1", uid);
	if (ret < 0 || (size_t)ret >= sizeof(idmap))
		ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
				   strerror(errno));

	write_to_file("/proc/self/uid_map", idmap, strlen(idmap), 0);

	ret = snprintf(idmap, sizeof(idmap), "0 %d 1", gid);
	if (ret < 0 || (size_t)ret >= sizeof(idmap))
		ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
				   strerror(errno));

	write_to_file("/proc/self/gid_map", idmap, strlen(idmap), 0);

	ret = setgid(0);
	if (ret)
		ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
				   strerror(errno));

	ret = setuid(0);
	if (ret)
		ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
				   strerror(errno));
}

static void change_to_mountns(void)
{
	int ret;

	ret = unshare(CLONE_NEWNS);
	if (ret < 0)
		ksft_exit_fail_msg("%s - Failed to unshare mount namespace\n",
				   strerror(errno));

	ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
	if (ret < 0)
		ksft_exit_fail_msg("%s - Failed to mount / as private\n",
				   strerror(errno));
}

static void rmdir_protect_errno(const char *dir)
{
	int saved_errno = errno;
	(void)rmdir(dir);
	errno = saved_errno;
}

137
static int __do_binderfs_test(void)
138 139 140 141 142 143
{
	int fd, ret, saved_errno;
	size_t len;
	ssize_t wret;
	struct binderfs_device device = { 0 };
	struct binder_version version = { 0 };
144 145
	char binderfs_mntpt[] = P_tmpdir "/binderfs_XXXXXX",
		device_path[sizeof(P_tmpdir "/binderfs_XXXXXX/") + BINDERFS_MAX_NAME];
146 147 148

	change_to_mountns();

149 150 151 152
	if (!mkdtemp(binderfs_mntpt))
		ksft_exit_fail_msg(
			"%s - Failed to create binderfs mountpoint\n",
			strerror(errno));
153

154
	ret = mount(NULL, binderfs_mntpt, "binder", 0, 0);
155 156 157 158 159
	if (ret < 0) {
		if (errno != ENODEV)
			ksft_exit_fail_msg("%s - Failed to mount binderfs\n",
					   strerror(errno));

160
		rmdir_protect_errno(binderfs_mntpt);
161
		return 1;
162 163 164 165 166 167 168
	}

	/* binderfs mount test passed */
	ksft_inc_pass_cnt();

	memcpy(device.name, "my-binder", strlen("my-binder"));

169 170
	snprintf(device_path, sizeof(device_path), "%s/binder-control", binderfs_mntpt);
	fd = open(device_path, O_RDONLY | O_CLOEXEC);
171 172 173 174 175 176 177 178 179 180
	if (fd < 0)
		ksft_exit_fail_msg(
			"%s - Failed to open binder-control device\n",
			strerror(errno));

	ret = ioctl(fd, BINDER_CTL_ADD, &device);
	saved_errno = errno;
	close(fd);
	errno = saved_errno;
	if (ret < 0) {
181
		rmdir_protect_errno(binderfs_mntpt);
182 183 184 185 186 187 188 189 190 191 192 193
		ksft_exit_fail_msg(
			"%s - Failed to allocate new binder device\n",
			strerror(errno));
	}

	ksft_print_msg(
		"Allocated new binder device with major %d, minor %d, and name %s\n",
		device.major, device.minor, device.name);

	/* binder device allocation test passed */
	ksft_inc_pass_cnt();

194 195
	snprintf(device_path, sizeof(device_path), "%s/my-binder", binderfs_mntpt);
	fd = open(device_path, O_CLOEXEC | O_RDONLY);
196
	if (fd < 0) {
197
		rmdir_protect_errno(binderfs_mntpt);
198 199 200 201 202 203 204 205 206
		ksft_exit_fail_msg("%s - Failed to open my-binder device\n",
				   strerror(errno));
	}

	ret = ioctl(fd, BINDER_VERSION, &version);
	saved_errno = errno;
	close(fd);
	errno = saved_errno;
	if (ret < 0) {
207
		rmdir_protect_errno(binderfs_mntpt);
208 209 210 211 212 213 214 215 216 217 218
		ksft_exit_fail_msg(
			"%s - Failed to open perform BINDER_VERSION request\n",
			strerror(errno));
	}

	ksft_print_msg("Detected binder version: %d\n",
		       version.protocol_version);

	/* binder transaction with binderfs binder device passed */
	ksft_inc_pass_cnt();

219
	ret = unlink(device_path);
220
	if (ret < 0) {
221
		rmdir_protect_errno(binderfs_mntpt);
222 223 224 225 226 227 228
		ksft_exit_fail_msg("%s - Failed to delete binder device\n",
				   strerror(errno));
	}

	/* binder device removal passed */
	ksft_inc_pass_cnt();

229 230
	snprintf(device_path, sizeof(device_path), "%s/binder-control", binderfs_mntpt);
	ret = unlink(device_path);
231
	if (!ret) {
232
		rmdir_protect_errno(binderfs_mntpt);
233 234
		ksft_exit_fail_msg("Managed to delete binder-control device\n");
	} else if (errno != EPERM) {
235
		rmdir_protect_errno(binderfs_mntpt);
236 237 238 239 240 241 242 243 244
		ksft_exit_fail_msg(
			"%s - Failed to delete binder-control device but exited with unexpected error code\n",
			strerror(errno));
	}

	/* binder-control device removal failed as expected */
	ksft_inc_xfail_cnt();

on_error:
245 246
	ret = umount2(binderfs_mntpt, MNT_DETACH);
	rmdir_protect_errno(binderfs_mntpt);
247 248 249 250 251 252
	if (ret < 0)
		ksft_exit_fail_msg("%s - Failed to unmount binderfs\n",
				   strerror(errno));

	/* binderfs unmount test passed */
	ksft_inc_pass_cnt();
253
	return 0;
254 255
}

256
TEST(binderfs_test_privileged)
257 258
{
	if (geteuid() != 0)
259 260 261 262
		XFAIL(return, "Tests are not run as root. Skipping privileged tests");

	if (__do_binderfs_test() == 1)
		XFAIL(return, "The Android binderfs filesystem is not available");
263 264
}

265
TEST(binderfs_test_unprivileged)
266 267 268
{
	change_to_userns();

269 270
	if (__do_binderfs_test() == 1)
		XFAIL(return, "The Android binderfs filesystem is not available");
271
}
272 273

TEST_HARNESS_MAIN