Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com>
0003  *
0004  * Permission to use, copy, modify, and distribute this software for any
0005  * purpose with or without fee is hereby granted, provided that the above
0006  * copyright notice and this permission notice appear in all copies.
0007  *
0008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0015  */
0016 // Test that /proc/$KERNEL_THREAD/fd/ is empty.
0017 
0018 #undef NDEBUG
0019 #include <sys/syscall.h>
0020 #include <assert.h>
0021 #include <dirent.h>
0022 #include <limits.h>
0023 #include <stdio.h>
0024 #include <string.h>
0025 #include <sys/types.h>
0026 #include <sys/stat.h>
0027 #include <fcntl.h>
0028 #include <unistd.h>
0029 
0030 #include "proc.h"
0031 
0032 #define PF_KHTREAD 0x00200000
0033 
0034 /*
0035  * Test for kernel threadness atomically with openat().
0036  *
0037  * Return /proc/$PID/fd descriptor if process is kernel thread.
0038  * Return -1 if a process is userspace process.
0039  */
0040 static int kernel_thread_fd(unsigned int pid)
0041 {
0042     unsigned int flags = 0;
0043     char buf[4096];
0044     int dir_fd, fd;
0045     ssize_t rv;
0046 
0047     snprintf(buf, sizeof(buf), "/proc/%u", pid);
0048     dir_fd = open(buf, O_RDONLY|O_DIRECTORY);
0049     if (dir_fd == -1)
0050         return -1;
0051 
0052     /*
0053      * Believe it or not, struct task_struct::flags is directly exposed
0054      * to userspace!
0055      */
0056     fd = openat(dir_fd, "stat", O_RDONLY);
0057     if (fd == -1) {
0058         close(dir_fd);
0059         return -1;
0060     }
0061     rv = read(fd, buf, sizeof(buf));
0062     close(fd);
0063     if (0 < rv && rv <= sizeof(buf)) {
0064         unsigned long long flags_ull;
0065         char *p, *end;
0066         int i;
0067 
0068         assert(buf[rv - 1] == '\n');
0069         buf[rv - 1] = '\0';
0070 
0071         /* Search backwards: ->comm can contain whitespace and ')'. */
0072         for (i = 0; i < 43; i++) {
0073             p = strrchr(buf, ' ');
0074             assert(p);
0075             *p = '\0';
0076         }
0077 
0078         p = strrchr(buf, ' ');
0079         assert(p);
0080 
0081         flags_ull = xstrtoull(p + 1, &end);
0082         assert(*end == '\0');
0083         assert(flags_ull == (unsigned int)flags_ull);
0084 
0085         flags = flags_ull;
0086     }
0087 
0088     fd = -1;
0089     if (flags & PF_KHTREAD) {
0090         fd = openat(dir_fd, "fd", O_RDONLY|O_DIRECTORY);
0091     }
0092     close(dir_fd);
0093     return fd;
0094 }
0095 
0096 static void test_readdir(int fd)
0097 {
0098     DIR *d;
0099     struct dirent *de;
0100 
0101     d = fdopendir(fd);
0102     assert(d);
0103 
0104     de = xreaddir(d);
0105     assert(streq(de->d_name, "."));
0106     assert(de->d_type == DT_DIR);
0107 
0108     de = xreaddir(d);
0109     assert(streq(de->d_name, ".."));
0110     assert(de->d_type == DT_DIR);
0111 
0112     de = xreaddir(d);
0113     assert(!de);
0114 }
0115 
0116 static inline int sys_statx(int dirfd, const char *pathname, int flags,
0117                 unsigned int mask, void *stx)
0118 {
0119     return syscall(SYS_statx, dirfd, pathname, flags, mask, stx);
0120 }
0121 
0122 static void test_lookup_fail(int fd, const char *pathname)
0123 {
0124     char stx[256] __attribute__((aligned(8)));
0125     int rv;
0126 
0127     rv = sys_statx(fd, pathname, AT_SYMLINK_NOFOLLOW, 0, (void *)stx);
0128     assert(rv == -1 && errno == ENOENT);
0129 }
0130 
0131 static void test_lookup(int fd)
0132 {
0133     char buf[64];
0134     unsigned int u;
0135     int i;
0136 
0137     for (i = INT_MIN; i < INT_MIN + 1024; i++) {
0138         snprintf(buf, sizeof(buf), "%d", i);
0139         test_lookup_fail(fd, buf);
0140     }
0141     for (i = -1024; i < 1024; i++) {
0142         snprintf(buf, sizeof(buf), "%d", i);
0143         test_lookup_fail(fd, buf);
0144     }
0145     for (u = INT_MAX - 1024; u < (unsigned int)INT_MAX + 1024; u++) {
0146         snprintf(buf, sizeof(buf), "%u", u);
0147         test_lookup_fail(fd, buf);
0148     }
0149     for (u = UINT_MAX - 1024; u != 0; u++) {
0150         snprintf(buf, sizeof(buf), "%u", u);
0151         test_lookup_fail(fd, buf);
0152     }
0153 }
0154 
0155 int main(void)
0156 {
0157     unsigned int pid;
0158     int fd;
0159 
0160     /*
0161      * In theory this will loop indefinitely if kernel threads are exiled
0162      * from /proc.
0163      *
0164      * Start with kthreadd.
0165      */
0166     pid = 2;
0167     while ((fd = kernel_thread_fd(pid)) == -1 && pid < 1024) {
0168         pid++;
0169     }
0170     /* EACCES if run as non-root. */
0171     if (pid >= 1024)
0172         return 1;
0173 
0174     test_readdir(fd);
0175     test_lookup(fd);
0176 
0177     return 0;
0178 }