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 /proc/*/fd lookup. 17 18 #undef NDEBUG 19 #include <assert.h> 20 #include <dirent.h> 21 #include <errno.h> 22 #include <limits.h> 23 #include <sched.h> 24 #include <stdio.h> 25 #include <unistd.h> 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <fcntl.h> 29 30 #include "proc.h" 31 32 /* lstat(2) has more "coverage" in case non-symlink pops up somehow. */ 33 static void test_lookup_pass(const char *pathname) 34 { 35 struct stat st; 36 ssize_t rv; 37 38 memset(&st, 0, sizeof(struct stat)); 39 rv = lstat(pathname, &st); 40 assert(rv == 0); 41 assert(S_ISLNK(st.st_mode)); 42 } 43 44 static void test_lookup_fail(const char *pathname) 45 { 46 struct stat st; 47 ssize_t rv; 48 49 rv = lstat(pathname, &st); 50 assert(rv == -1 && errno == ENOENT); 51 } 52 53 static void test_lookup(unsigned int fd) 54 { 55 char buf[64]; 56 unsigned int c; 57 unsigned int u; 58 int i; 59 60 snprintf(buf, sizeof(buf), "/proc/self/fd/%u", fd); 61 test_lookup_pass(buf); 62 63 /* leading junk */ 64 for (c = 1; c <= 255; c++) { 65 if (c == '/') 66 continue; 67 snprintf(buf, sizeof(buf), "/proc/self/fd/%c%u", c, fd); 68 test_lookup_fail(buf); 69 } 70 71 /* trailing junk */ 72 for (c = 1; c <= 255; c++) { 73 if (c == '/') 74 continue; 75 snprintf(buf, sizeof(buf), "/proc/self/fd/%u%c", fd, c); 76 test_lookup_fail(buf); 77 } 78 79 for (i = INT_MIN; i < INT_MIN + 1024; i++) { 80 snprintf(buf, sizeof(buf), "/proc/self/fd/%d", i); 81 test_lookup_fail(buf); 82 } 83 for (i = -1024; i < 0; i++) { 84 snprintf(buf, sizeof(buf), "/proc/self/fd/%d", i); 85 test_lookup_fail(buf); 86 } 87 for (u = INT_MAX - 1024; u <= (unsigned int)INT_MAX + 1024; u++) { 88 snprintf(buf, sizeof(buf), "/proc/self/fd/%u", u); 89 test_lookup_fail(buf); 90 } 91 for (u = UINT_MAX - 1024; u != 0; u++) { 92 snprintf(buf, sizeof(buf), "/proc/self/fd/%u", u); 93 test_lookup_fail(buf); 94 } 95 96 97 } 98 99 int main(void) 100 { 101 struct dirent *de; 102 unsigned int fd, target_fd; 103 104 if (unshare(CLONE_FILES) == -1) 105 return 1; 106 107 /* Wipe fdtable. */ 108 do { 109 DIR *d; 110 111 d = opendir("/proc/self/fd"); 112 if (!d) 113 return 1; 114 115 de = xreaddir(d); 116 assert(de->d_type == DT_DIR); 117 assert(streq(de->d_name, ".")); 118 119 de = xreaddir(d); 120 assert(de->d_type == DT_DIR); 121 assert(streq(de->d_name, "..")); 122 next: 123 de = xreaddir(d); 124 if (de) { 125 unsigned long long fd_ull; 126 unsigned int fd; 127 char *end; 128 129 assert(de->d_type == DT_LNK); 130 131 fd_ull = xstrtoull(de->d_name, &end); 132 assert(*end == '\0'); 133 assert(fd_ull == (unsigned int)fd_ull); 134 135 fd = fd_ull; 136 if (fd == dirfd(d)) 137 goto next; 138 close(fd); 139 } 140 141 closedir(d); 142 } while (de); 143 144 /* Now fdtable is clean. */ 145 146 fd = open("/", O_PATH|O_DIRECTORY); 147 assert(fd == 0); 148 test_lookup(fd); 149 close(fd); 150 151 /* Clean again! */ 152 153 fd = open("/", O_PATH|O_DIRECTORY); 154 assert(fd == 0); 155 /* Default RLIMIT_NOFILE-1 */ 156 target_fd = 1023; 157 while (target_fd > 0) { 158 if (dup2(fd, target_fd) == target_fd) 159 break; 160 target_fd /= 2; 161 } 162 assert(target_fd > 0); 163 close(fd); 164 test_lookup(target_fd); 165 close(target_fd); 166 167 return 0; 168 } 169