root/tools/testing/selftests/filesystems/binderfs/binderfs_test.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. write_nointr
  2. write_to_file
  3. change_to_userns
  4. change_to_mountns
  5. rmdir_protect_errno
  6. __do_binderfs_test
  7. binderfs_test_privileged
  8. binderfs_test_unprivileged
  9. main

   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 }

/* [<][>][^][v][top][bottom][index][help] */