Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: add binderfs selftests

This adds the promised selftest for binderfs. It will verify the following
things:
- binderfs mounting works
- binder device allocation works
- performing a binder ioctl() request through a binderfs device works
- binder device removal works
- binder-control removal fails
- binderfs unmounting works

The tests are performed both privileged and unprivileged. The latter
verifies that binderfs behaves correctly in user namespaces.

Cc: Todd Kjos <tkjos@google.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Acked-by: Shuah Khan <shuah@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Christian Brauner and committed by
Greg Kroah-Hartman
75abec73 f17b5f06

+286
+1
tools/testing/selftests/Makefile
··· 10 10 TARGETS += efivarfs 11 11 TARGETS += exec 12 12 TARGETS += filesystems 13 + TARGETS += filesystems/binderfs 13 14 TARGETS += firmware 14 15 TARGETS += ftrace 15 16 TARGETS += futex
+1
tools/testing/selftests/filesystems/binderfs/.gitignore
··· 1 + binderfs_test
+6
tools/testing/selftests/filesystems/binderfs/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + CFLAGS += -I../../../../../usr/include/ 4 + TEST_GEN_PROGS := binderfs_test 5 + 6 + include ../../lib.mk
+275
tools/testing/selftests/filesystems/binderfs/binderfs_test.c
··· 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 + }
+3
tools/testing/selftests/filesystems/binderfs/config
··· 1 + CONFIG_ANDROID=y 2 + CONFIG_ANDROID_BINDERFS=y 3 + CONFIG_ANDROID_BINDER_IPC=y