root/tools/testing/selftests/proc/fd-003-kthread.c

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

DEFINITIONS

This source file includes following definitions.
  1. kernel_thread_fd
  2. test_readdir
  3. sys_statx
  4. test_lookup_fail
  5. test_lookup
  6. main

   1 /*
   2  * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com>
   3  *
   4  * Permission to use, copy, modify, and distribute this software for any
   5  * purpose with or without fee is hereby granted, provided that the above
   6  * copyright notice and this permission notice appear in all copies.
   7  *
   8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15  */
  16 // Test that /proc/$KERNEL_THREAD/fd/ is empty.
  17 
  18 #undef NDEBUG
  19 #include <sys/syscall.h>
  20 #include <assert.h>
  21 #include <dirent.h>
  22 #include <limits.h>
  23 #include <stdio.h>
  24 #include <string.h>
  25 #include <sys/types.h>
  26 #include <sys/stat.h>
  27 #include <fcntl.h>
  28 #include <unistd.h>
  29 
  30 #include "proc.h"
  31 
  32 #define PF_KHTREAD 0x00200000
  33 
  34 /*
  35  * Test for kernel threadness atomically with openat().
  36  *
  37  * Return /proc/$PID/fd descriptor if process is kernel thread.
  38  * Return -1 if a process is userspace process.
  39  */
  40 static int kernel_thread_fd(unsigned int pid)
  41 {
  42         unsigned int flags = 0;
  43         char buf[4096];
  44         int dir_fd, fd;
  45         ssize_t rv;
  46 
  47         snprintf(buf, sizeof(buf), "/proc/%u", pid);
  48         dir_fd = open(buf, O_RDONLY|O_DIRECTORY);
  49         if (dir_fd == -1)
  50                 return -1;
  51 
  52         /*
  53          * Believe it or not, struct task_struct::flags is directly exposed
  54          * to userspace!
  55          */
  56         fd = openat(dir_fd, "stat", O_RDONLY);
  57         if (fd == -1) {
  58                 close(dir_fd);
  59                 return -1;
  60         }
  61         rv = read(fd, buf, sizeof(buf));
  62         close(fd);
  63         if (0 < rv && rv <= sizeof(buf)) {
  64                 unsigned long long flags_ull;
  65                 char *p, *end;
  66                 int i;
  67 
  68                 assert(buf[rv - 1] == '\n');
  69                 buf[rv - 1] = '\0';
  70 
  71                 /* Search backwards: ->comm can contain whitespace and ')'. */
  72                 for (i = 0; i < 43; i++) {
  73                         p = strrchr(buf, ' ');
  74                         assert(p);
  75                         *p = '\0';
  76                 }
  77 
  78                 p = strrchr(buf, ' ');
  79                 assert(p);
  80 
  81                 flags_ull = xstrtoull(p + 1, &end);
  82                 assert(*end == '\0');
  83                 assert(flags_ull == (unsigned int)flags_ull);
  84 
  85                 flags = flags_ull;
  86         }
  87 
  88         fd = -1;
  89         if (flags & PF_KHTREAD) {
  90                 fd = openat(dir_fd, "fd", O_RDONLY|O_DIRECTORY);
  91         }
  92         close(dir_fd);
  93         return fd;
  94 }
  95 
  96 static void test_readdir(int fd)
  97 {
  98         DIR *d;
  99         struct dirent *de;
 100 
 101         d = fdopendir(fd);
 102         assert(d);
 103 
 104         de = xreaddir(d);
 105         assert(streq(de->d_name, "."));
 106         assert(de->d_type == DT_DIR);
 107 
 108         de = xreaddir(d);
 109         assert(streq(de->d_name, ".."));
 110         assert(de->d_type == DT_DIR);
 111 
 112         de = xreaddir(d);
 113         assert(!de);
 114 }
 115 
 116 static inline int sys_statx(int dirfd, const char *pathname, int flags,
 117                             unsigned int mask, void *stx)
 118 {
 119         return syscall(SYS_statx, dirfd, pathname, flags, mask, stx);
 120 }
 121 
 122 static void test_lookup_fail(int fd, const char *pathname)
 123 {
 124         char stx[256] __attribute__((aligned(8)));
 125         int rv;
 126 
 127         rv = sys_statx(fd, pathname, AT_SYMLINK_NOFOLLOW, 0, (void *)stx);
 128         assert(rv == -1 && errno == ENOENT);
 129 }
 130 
 131 static void test_lookup(int fd)
 132 {
 133         char buf[64];
 134         unsigned int u;
 135         int i;
 136 
 137         for (i = INT_MIN; i < INT_MIN + 1024; i++) {
 138                 snprintf(buf, sizeof(buf), "%d", i);
 139                 test_lookup_fail(fd, buf);
 140         }
 141         for (i = -1024; i < 1024; i++) {
 142                 snprintf(buf, sizeof(buf), "%d", i);
 143                 test_lookup_fail(fd, buf);
 144         }
 145         for (u = INT_MAX - 1024; u < (unsigned int)INT_MAX + 1024; u++) {
 146                 snprintf(buf, sizeof(buf), "%u", u);
 147                 test_lookup_fail(fd, buf);
 148         }
 149         for (u = UINT_MAX - 1024; u != 0; u++) {
 150                 snprintf(buf, sizeof(buf), "%u", u);
 151                 test_lookup_fail(fd, buf);
 152         }
 153 }
 154 
 155 int main(void)
 156 {
 157         unsigned int pid;
 158         int fd;
 159 
 160         /*
 161          * In theory this will loop indefinitely if kernel threads are exiled
 162          * from /proc.
 163          *
 164          * Start with kthreadd.
 165          */
 166         pid = 2;
 167         while ((fd = kernel_thread_fd(pid)) == -1 && pid < 1024) {
 168                 pid++;
 169         }
 170         /* EACCES if run as non-root. */
 171         if (pid >= 1024)
 172                 return 1;
 173 
 174         test_readdir(fd);
 175         test_lookup(fd);
 176 
 177         return 0;
 178 }

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