1 /* 2 * Check decoding of kcmp syscall. 3 * 4 * Copyright (c) 2016-2017 Eugene Syromyatnikov <evgsyr (at) gmail.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "tests.h" 31 32 #include <asm/unistd.h> 33 #include "scno.h" 34 35 #ifdef __NR_kcmp 36 37 # include <fcntl.h> 38 # include <stdarg.h> 39 # include <stdint.h> 40 # include <stdio.h> 41 # include <string.h> 42 # include <unistd.h> 43 44 # ifndef VERBOSE_FD 45 # define VERBOSE_FD 0 46 # endif 47 48 /* 49 * We prefer to use system headers in order to catch some possible deviations in 50 * system's headers from our perception of reality, but happy to include our own 51 * definitions as well. 52 */ 53 # ifdef HAVE_LINUX_KCMP_H 54 # include <linux/kcmp.h> 55 # else 56 # define KCMP_FILE 0 57 # define KCMP_VM 1 58 # define KCMP_FILES 2 59 # define KCMP_FS 3 60 # define KCMP_SIGHAND 4 61 # define KCMP_IO 5 62 # define KCMP_SYSVSEM 6 63 # endif 64 65 /* All other kcmp types have been added atomically */ 66 # define KCMP_EPOLL_TFD 7 67 68 # ifndef HAVE_STRUCT_KCMP_EPOLL_SLOT 69 struct kcmp_epoll_slot { 70 uint32_t efd; 71 uint32_t tfd; 72 uint32_t toff; 73 }; 74 # endif 75 76 static const kernel_ulong_t kcmp_max_type = KCMP_EPOLL_TFD; 77 78 static const char null_path[] = "/dev/null"; 79 static const char zero_path[] = "/dev/zero"; 80 81 # define NULL_FD 23 82 # define ZERO_FD 42 83 84 static void 85 printpidfd(const char *prefix, pid_t pid, unsigned fd) 86 { 87 printf("%s%d", prefix, fd); 88 } 89 90 /* 91 * Last argument is optional and is used as follows: 92 * * When type is KCMP_EPOLL_TFD, it signalises whether idx2 is a valid 93 * pointer. 94 */ 95 static void 96 do_kcmp(kernel_ulong_t pid1, kernel_ulong_t pid2, kernel_ulong_t type, 97 const char *type_str, kernel_ulong_t idx1, kernel_ulong_t idx2, ...) 98 { 99 long rc; 100 const char *errstr; 101 102 rc = syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); 103 errstr = sprintrc(rc); 104 105 printf("kcmp(%d, %d, ", (int) pid1, (int) pid2); 106 107 if (type_str) 108 printf("%s", type_str); 109 else 110 printf("%#x /* KCMP_??? */", (int) type); 111 112 if (type == KCMP_FILE) { 113 printpidfd(", ", pid1, idx1); 114 printpidfd(", ", pid2, idx2); 115 } else if (type == KCMP_EPOLL_TFD) { 116 va_list ap; 117 int valid_ptr; 118 119 va_start(ap, idx2); 120 valid_ptr = va_arg(ap, int); 121 va_end(ap); 122 123 printpidfd(", ", pid1, idx1); 124 printf(", "); 125 126 if (valid_ptr) { 127 struct kcmp_epoll_slot *slot = 128 (struct kcmp_epoll_slot *) (uintptr_t) idx2; 129 130 printpidfd("{efd=", pid2, slot->efd); 131 printpidfd(", tfd=", pid2, slot->tfd); 132 printf(", toff=%llu}", (unsigned long long) slot->toff); 133 } else { 134 if (idx2) 135 printf("%#llx", (unsigned long long) idx2); 136 else 137 printf("NULL"); 138 } 139 } else if (type > kcmp_max_type) { 140 printf(", %#llx, %#llx", 141 (unsigned long long) idx1, (unsigned long long) idx2); 142 } 143 144 printf(") = %s\n", errstr); 145 } 146 147 int 148 main(void) 149 { 150 static const kernel_ulong_t bogus_pid1 = 151 (kernel_ulong_t) 0xdeadca75face1057ULL; 152 static const kernel_ulong_t bogus_pid2 = 153 (kernel_ulong_t) 0xdefaced1defaced2ULL; 154 static const kernel_ulong_t bogus_type = 155 (kernel_ulong_t) 0xbadc0dedda7adeadULL; 156 static const kernel_ulong_t bogus_idx1 = 157 (kernel_ulong_t) 0xdec0ded3dec0ded4ULL; 158 static const kernel_ulong_t bogus_idx2 = 159 (kernel_ulong_t) 0xba5e1e55deadc0deULL; 160 static const struct kcmp_epoll_slot slot_data[] = { 161 { 0xdeadc0de, 0xfacef157, 0xbadc0ded }, 162 { NULL_FD, ZERO_FD, 0 }, 163 { 0, 0, 0 }, 164 }; 165 static kernel_ulong_t ptr_check = 166 F8ILL_KULONG_SUPPORTED ? F8ILL_KULONG_MASK : 0; 167 168 int fd; 169 unsigned i; 170 struct kcmp_epoll_slot *slot = tail_alloc(sizeof(*slot)); 171 172 /* Open some files to test printpidfd */ 173 fd = open(null_path, O_RDONLY); 174 if (fd < 0) 175 perror_msg_and_fail("open(\"%s\")", null_path); 176 if (fd != NULL_FD) { 177 if (dup2(fd, NULL_FD) < 0) 178 perror_msg_and_fail("dup2(fd, NULL_FD)"); 179 close(fd); 180 } 181 182 fd = open(zero_path, O_RDONLY); 183 if (fd < 0) 184 perror_msg_and_fail("open(\"%s\")", zero_path); 185 if (fd != ZERO_FD) { 186 if (dup2(fd, ZERO_FD) < 0) 187 perror_msg_and_fail("dup2(fd, ZERO_FD)"); 188 close(fd); 189 } 190 191 close(0); 192 193 /* Invalid values */ 194 do_kcmp(bogus_pid1, bogus_pid2, bogus_type, NULL, bogus_idx1, 195 bogus_idx2); 196 do_kcmp(F8ILL_KULONG_MASK, F8ILL_KULONG_MASK, kcmp_max_type + 1, NULL, 197 0, 0); 198 199 /* KCMP_FILE is the only type which has additional args */ 200 do_kcmp(3141592653U, 2718281828U, ARG_STR(KCMP_FILE), bogus_idx1, 201 bogus_idx2); 202 do_kcmp(-1, -1, ARG_STR(KCMP_FILE), NULL_FD, ZERO_FD); 203 204 /* Types without additional args */ 205 do_kcmp(-1, -1, ARG_STR(KCMP_VM), bogus_idx1, bogus_idx2); 206 do_kcmp(-1, -1, ARG_STR(KCMP_FILES), bogus_idx1, bogus_idx2); 207 do_kcmp(-1, -1, ARG_STR(KCMP_FS), bogus_idx1, bogus_idx2); 208 do_kcmp(-1, -1, ARG_STR(KCMP_SIGHAND), bogus_idx1, bogus_idx2); 209 do_kcmp(-1, -1, ARG_STR(KCMP_IO), bogus_idx1, bogus_idx2); 210 do_kcmp(-1, -1, ARG_STR(KCMP_SYSVSEM), bogus_idx1, bogus_idx2); 211 212 /* KCMP_EPOLL_TFD checks */ 213 do_kcmp(-1, -1, ARG_STR(KCMP_EPOLL_TFD), 214 F8ILL_KULONG_MASK | 2718281828U, ptr_check, 0); 215 do_kcmp(-1, -1, ARG_STR(KCMP_EPOLL_TFD), 216 3141592653U, (uintptr_t) slot + 1, 0); 217 218 for (i = 0; i < ARRAY_SIZE(slot_data); i++) { 219 memcpy(slot, slot_data + i, sizeof(*slot)); 220 221 do_kcmp(getpid(), getppid(), ARG_STR(KCMP_EPOLL_TFD), NULL_FD, 222 (uintptr_t) slot, 1); 223 } 224 225 puts("+++ exited with 0 +++"); 226 227 return 0; 228 } 229 230 #else 231 232 SKIP_MAIN_UNDEFINED("__NR_kcmp"); 233 234 #endif 235