1 /* 2 * Copyright (c) 2016 Dmitry V. Levin <ldv (at) altlinux.org> 3 * Copyright (c) 2016-2018 The strace developers. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "defs.h" 30 31 #include <regex.h> 32 33 #include "filter.h" 34 #include "number_set.h" 35 #include "xstring.h" 36 37 38 /** 39 * Checks whether a @-separated personality specification suffix is present. 40 * Personality suffix is a one of strings stored in personality_designators 41 * array. 42 * 43 * @param[in] s Specification string to check. 44 * @param[out] p Where to store personality number if it is found. 45 * @return If personality is found, the provided string is copied without 46 * suffix and returned as a result (callee should de-alllocate it 47 * with free() after use), and personality number is written to p. 48 * Otherwise, NULL is returned and p is untouched. 49 */ 50 static char * 51 qualify_syscall_separate_personality(const char *s, unsigned int *p) 52 { 53 char *pos = strchr(s, '@'); 54 55 if (!pos) 56 return NULL; 57 58 for (unsigned int i = 0; i < SUPPORTED_PERSONALITIES; i++) { 59 if (!strcmp(pos + 1, personality_designators[i])) { 60 *p = i; 61 return xstrndup(s, pos - s); 62 } 63 } 64 65 error_msg_and_help("incorrect personality designator '%s'" 66 " in qualification '%s'", pos + 1, s); 67 } 68 69 static bool 70 qualify_syscall_number_personality(int n, unsigned int p, 71 struct number_set *set) 72 { 73 if ((unsigned int) n >= nsyscall_vec[p]) 74 return false; 75 76 add_number_to_set_array(n, set, p); 77 78 return true; 79 } 80 81 static bool 82 qualify_syscall_number(const char *s, struct number_set *set) 83 { 84 unsigned int p; 85 char *num_str = qualify_syscall_separate_personality(s, &p); 86 int n; 87 88 if (num_str) { 89 n = string_to_uint(num_str); 90 free(num_str); 91 92 if (n < 0) 93 return false; 94 95 return qualify_syscall_number_personality(n, p, set); 96 } 97 98 n = string_to_uint(s); 99 if (n < 0) 100 return false; 101 102 bool done = false; 103 104 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) 105 done |= qualify_syscall_number_personality(n, p, set); 106 107 return done; 108 } 109 110 static void 111 regerror_msg_and_die(int errcode, const regex_t *preg, 112 const char *str, const char *pattern) 113 { 114 char buf[512]; 115 116 regerror(errcode, preg, buf, sizeof(buf)); 117 error_msg_and_die("%s: %s: %s", str, pattern, buf); 118 } 119 120 static bool 121 qualify_syscall_regex(const char *s, struct number_set *set) 122 { 123 regex_t preg; 124 int rc; 125 126 if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0) 127 regerror_msg_and_die(rc, &preg, "regcomp", s); 128 129 bool found = false; 130 131 for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) { 132 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) { 133 if (!sysent_vec[p][i].sys_name) 134 continue; 135 136 rc = regexec(&preg, sysent_vec[p][i].sys_name, 137 0, NULL, 0); 138 139 if (rc == REG_NOMATCH) { 140 char name_buf[128]; 141 char *pos = stpcpy(name_buf, 142 sysent_vec[p][i].sys_name); 143 144 (void) xappendstr(name_buf, pos, "@%s", 145 personality_designators[p]); 146 147 rc = regexec(&preg, name_buf, 0, NULL, 0); 148 } 149 150 if (rc == REG_NOMATCH) 151 continue; 152 else if (rc) 153 regerror_msg_and_die(rc, &preg, "regexec", s); 154 155 add_number_to_set_array(i, set, p); 156 found = true; 157 } 158 } 159 160 regfree(&preg); 161 return found; 162 } 163 164 static unsigned int 165 lookup_class(const char *s) 166 { 167 static const struct { 168 const char *name; 169 unsigned int value; 170 } syscall_class[] = { 171 { "%desc", TRACE_DESC }, 172 { "%file", TRACE_FILE }, 173 { "%memory", TRACE_MEMORY }, 174 { "%process", TRACE_PROCESS }, 175 { "%signal", TRACE_SIGNAL }, 176 { "%ipc", TRACE_IPC }, 177 { "%network", TRACE_NETWORK }, 178 { "%stat", TRACE_STAT }, 179 { "%lstat", TRACE_LSTAT }, 180 { "%fstat", TRACE_FSTAT }, 181 { "%%stat", TRACE_STAT_LIKE }, 182 { "%statfs", TRACE_STATFS }, 183 { "%fstatfs", TRACE_FSTATFS }, 184 { "%%statfs", TRACE_STATFS_LIKE }, 185 { "%pure", TRACE_PURE }, 186 /* legacy class names */ 187 { "desc", TRACE_DESC }, 188 { "file", TRACE_FILE }, 189 { "memory", TRACE_MEMORY }, 190 { "process", TRACE_PROCESS }, 191 { "signal", TRACE_SIGNAL }, 192 { "ipc", TRACE_IPC }, 193 { "network", TRACE_NETWORK }, 194 }; 195 196 for (unsigned int i = 0; i < ARRAY_SIZE(syscall_class); ++i) { 197 if (strcmp(s, syscall_class[i].name) == 0) 198 return syscall_class[i].value; 199 } 200 201 return 0; 202 } 203 204 static bool 205 qualify_syscall_class(const char *s, struct number_set *set) 206 { 207 const unsigned int n = lookup_class(s); 208 if (!n) 209 return false; 210 211 for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) { 212 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) { 213 if (sysent_vec[p][i].sys_name && 214 (sysent_vec[p][i].sys_flags & n) == n) 215 add_number_to_set_array(i, set, p); 216 } 217 } 218 219 return true; 220 } 221 222 kernel_long_t 223 scno_by_name(const char *s, unsigned int p, kernel_long_t start) 224 { 225 if (p >= SUPPORTED_PERSONALITIES) 226 return -1; 227 228 for (kernel_ulong_t i = start; i < nsyscall_vec[p]; ++i) { 229 if (sysent_vec[p][i].sys_name && 230 strcmp(s, sysent_vec[p][i].sys_name) == 0) 231 return i; 232 } 233 234 return -1; 235 } 236 237 static bool 238 qualify_syscall_name_personality(const char *s, unsigned int p, 239 struct number_set *set) 240 { 241 bool found = false; 242 243 for (kernel_long_t scno = 0; (scno = scno_by_name(s, p, scno)) >= 0; 244 ++scno) { 245 add_number_to_set_array(scno, set, p); 246 found = true; 247 } 248 249 return found; 250 } 251 252 static bool 253 qualify_syscall_name(const char *s, struct number_set *set) 254 { 255 unsigned int p; 256 char *name_str = qualify_syscall_separate_personality(s, &p); 257 bool found = false; 258 259 if (name_str) { 260 found = qualify_syscall_name_personality(name_str, p, set); 261 free(name_str); 262 263 return found; 264 } 265 266 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) 267 found |= qualify_syscall_name_personality(s, p, set); 268 269 return found; 270 } 271 272 static bool 273 qualify_syscall(const char *token, struct number_set *set) 274 { 275 bool ignore_fail = false; 276 277 while (*token == '?') { 278 token++; 279 ignore_fail = true; 280 } 281 if (*token >= '0' && *token <= '9') 282 return qualify_syscall_number(token, set) || ignore_fail; 283 if (*token == '/') 284 return qualify_syscall_regex(token + 1, set) || ignore_fail; 285 return qualify_syscall_class(token, set) 286 || qualify_syscall_name(token, set) 287 || ignore_fail; 288 } 289 290 /* 291 * Add syscall numbers to SETs for each supported personality 292 * according to STR specification. 293 */ 294 void 295 qualify_syscall_tokens(const char *const str, struct number_set *const set) 296 { 297 /* Clear all sets. */ 298 clear_number_set_array(set, SUPPORTED_PERSONALITIES); 299 300 /* 301 * Each leading ! character means inversion 302 * of the remaining specification. 303 */ 304 const char *s = str; 305 while (*s == '!') { 306 invert_number_set_array(set, SUPPORTED_PERSONALITIES); 307 ++s; 308 } 309 310 if (strcmp(s, "none") == 0) { 311 /* 312 * No syscall numbers are added to sets. 313 * Subsequent is_number_in_set* invocations 314 * will return set[p]->not. 315 */ 316 return; 317 } else if (strcmp(s, "all") == 0) { 318 /* "all" == "!none" */ 319 invert_number_set_array(set, SUPPORTED_PERSONALITIES); 320 return; 321 } 322 323 /* 324 * Split the string into comma separated tokens. 325 * For each token, call qualify_syscall that will take care 326 * if adding appropriate syscall numbers to sets. 327 * The absence of tokens or a negative return code 328 * from qualify_syscall is a fatal error. 329 */ 330 char *copy = xstrdup(s); 331 char *saveptr = NULL; 332 bool done = false; 333 334 for (const char *token = strtok_r(copy, ",", &saveptr); 335 token; token = strtok_r(NULL, ",", &saveptr)) { 336 done = qualify_syscall(token, set); 337 if (!done) 338 error_msg_and_die("invalid system call '%s'", token); 339 } 340 341 free(copy); 342 343 if (!done) 344 error_msg_and_die("invalid system call '%s'", str); 345 } 346 347 /* 348 * Add numbers to SET according to STR specification. 349 */ 350 void 351 qualify_tokens(const char *const str, struct number_set *const set, 352 string_to_uint_func func, const char *const name) 353 { 354 /* Clear the set. */ 355 clear_number_set_array(set, 1); 356 357 /* 358 * Each leading ! character means inversion 359 * of the remaining specification. 360 */ 361 const char *s = str; 362 while (*s == '!') { 363 invert_number_set_array(set, 1); 364 ++s; 365 } 366 367 if (strcmp(s, "none") == 0) { 368 /* 369 * No numbers are added to the set. 370 * Subsequent is_number_in_set* invocations 371 * will return set->not. 372 */ 373 return; 374 } else if (strcmp(s, "all") == 0) { 375 /* "all" == "!none" */ 376 invert_number_set_array(set, 1); 377 return; 378 } 379 380 /* 381 * Split the string into comma separated tokens. 382 * For each token, find out the corresponding number 383 * by calling FUNC, and add that number to the set. 384 * The absence of tokens or a negative answer 385 * from FUNC is a fatal error. 386 */ 387 char *copy = xstrdup(s); 388 char *saveptr = NULL; 389 int number = -1; 390 391 for (const char *token = strtok_r(copy, ",", &saveptr); 392 token; token = strtok_r(NULL, ",", &saveptr)) { 393 number = func(token); 394 if (number < 0) 395 error_msg_and_die("invalid %s '%s'", name, token); 396 397 add_number_to_set(number, set); 398 } 399 400 free(copy); 401 402 if (number < 0) 403 error_msg_and_die("invalid %s '%s'", name, str); 404 } 405