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