Mirror of strace – the linux syscall tracer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

basic_filters.c 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /*
  2. * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
  3. * Copyright (c) 2016-2018 The strace developers.
  4. * All rights reserved.
  5. *
  6. * SPDX-License-Identifier: LGPL-2.1-or-later
  7. */
  8. #include "defs.h"
  9. #include <regex.h>
  10. #include "filter.h"
  11. #include "number_set.h"
  12. #include "xstring.h"
  13. /**
  14. * Checks whether a @-separated personality specification suffix is present.
  15. * Personality suffix is a one of strings stored in personality_designators
  16. * array.
  17. *
  18. * @param[in] s Specification string to check.
  19. * @param[out] p Where to store personality number if it is found.
  20. * @return If personality is found, the provided string is copied without
  21. * suffix and returned as a result (callee should de-alllocate it
  22. * with free() after use), and personality number is written to p.
  23. * Otherwise, NULL is returned and p is untouched.
  24. */
  25. static char *
  26. qualify_syscall_separate_personality(const char *s, unsigned int *p)
  27. {
  28. char *pos = strchr(s, '@');
  29. if (!pos)
  30. return NULL;
  31. for (unsigned int i = 0; i < SUPPORTED_PERSONALITIES; i++) {
  32. if (!strcmp(pos + 1, personality_designators[i])) {
  33. *p = i;
  34. return xstrndup(s, pos - s);
  35. }
  36. }
  37. error_msg_and_help("incorrect personality designator '%s'"
  38. " in qualification '%s'", pos + 1, s);
  39. }
  40. static bool
  41. qualify_syscall_number_personality(int n, unsigned int p,
  42. struct number_set *set)
  43. {
  44. if ((unsigned int) n >= nsyscall_vec[p])
  45. return false;
  46. add_number_to_set_array(n, set, p);
  47. return true;
  48. }
  49. static bool
  50. qualify_syscall_number(const char *s, struct number_set *set)
  51. {
  52. unsigned int p;
  53. char *num_str = qualify_syscall_separate_personality(s, &p);
  54. int n;
  55. if (num_str) {
  56. n = string_to_uint(num_str);
  57. free(num_str);
  58. if (n < 0)
  59. return false;
  60. return qualify_syscall_number_personality(n, p, set);
  61. }
  62. n = string_to_uint(s);
  63. if (n < 0)
  64. return false;
  65. bool done = false;
  66. for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
  67. done |= qualify_syscall_number_personality(n, p, set);
  68. return done;
  69. }
  70. static void
  71. regerror_msg_and_die(int errcode, const regex_t *preg,
  72. const char *str, const char *pattern)
  73. {
  74. char buf[512];
  75. regerror(errcode, preg, buf, sizeof(buf));
  76. error_msg_and_die("%s: %s: %s", str, pattern, buf);
  77. }
  78. static bool
  79. qualify_syscall_regex(const char *s, struct number_set *set)
  80. {
  81. regex_t preg;
  82. int rc;
  83. if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
  84. regerror_msg_and_die(rc, &preg, "regcomp", s);
  85. bool found = false;
  86. for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
  87. for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
  88. if (!sysent_vec[p][i].sys_name)
  89. continue;
  90. rc = regexec(&preg, sysent_vec[p][i].sys_name,
  91. 0, NULL, 0);
  92. if (rc == REG_NOMATCH) {
  93. char name_buf[128];
  94. char *pos = stpcpy(name_buf,
  95. sysent_vec[p][i].sys_name);
  96. (void) xappendstr(name_buf, pos, "@%s",
  97. personality_designators[p]);
  98. rc = regexec(&preg, name_buf, 0, NULL, 0);
  99. }
  100. if (rc == REG_NOMATCH)
  101. continue;
  102. else if (rc)
  103. regerror_msg_and_die(rc, &preg, "regexec", s);
  104. add_number_to_set_array(i, set, p);
  105. found = true;
  106. }
  107. }
  108. regfree(&preg);
  109. return found;
  110. }
  111. static unsigned int
  112. lookup_class(const char *s)
  113. {
  114. static const struct {
  115. const char *name;
  116. unsigned int value;
  117. } syscall_class[] = {
  118. { "%desc", TRACE_DESC },
  119. { "%file", TRACE_FILE },
  120. { "%memory", TRACE_MEMORY },
  121. { "%process", TRACE_PROCESS },
  122. { "%signal", TRACE_SIGNAL },
  123. { "%ipc", TRACE_IPC },
  124. { "%net", TRACE_NETWORK },
  125. { "%network", TRACE_NETWORK },
  126. { "%stat", TRACE_STAT },
  127. { "%lstat", TRACE_LSTAT },
  128. { "%fstat", TRACE_FSTAT },
  129. { "%%stat", TRACE_STAT_LIKE },
  130. { "%statfs", TRACE_STATFS },
  131. { "%fstatfs", TRACE_FSTATFS },
  132. { "%%statfs", TRACE_STATFS_LIKE },
  133. { "%pure", TRACE_PURE },
  134. /* legacy class names */
  135. { "desc", TRACE_DESC },
  136. { "file", TRACE_FILE },
  137. { "memory", TRACE_MEMORY },
  138. { "process", TRACE_PROCESS },
  139. { "signal", TRACE_SIGNAL },
  140. { "ipc", TRACE_IPC },
  141. { "network", TRACE_NETWORK },
  142. };
  143. for (unsigned int i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
  144. if (strcmp(s, syscall_class[i].name) == 0)
  145. return syscall_class[i].value;
  146. }
  147. return 0;
  148. }
  149. static bool
  150. qualify_syscall_class(const char *s, struct number_set *set)
  151. {
  152. const unsigned int n = lookup_class(s);
  153. if (!n)
  154. return false;
  155. for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
  156. for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
  157. if (sysent_vec[p][i].sys_name &&
  158. (sysent_vec[p][i].sys_flags & n) == n)
  159. add_number_to_set_array(i, set, p);
  160. }
  161. }
  162. return true;
  163. }
  164. kernel_long_t
  165. scno_by_name(const char *s, unsigned int p, kernel_long_t start)
  166. {
  167. if (p >= SUPPORTED_PERSONALITIES)
  168. return -1;
  169. for (kernel_ulong_t i = start; i < nsyscall_vec[p]; ++i) {
  170. if (sysent_vec[p][i].sys_name &&
  171. strcmp(s, sysent_vec[p][i].sys_name) == 0)
  172. return i;
  173. }
  174. return -1;
  175. }
  176. static bool
  177. qualify_syscall_name_personality(const char *s, unsigned int p,
  178. struct number_set *set)
  179. {
  180. bool found = false;
  181. for (kernel_long_t scno = 0; (scno = scno_by_name(s, p, scno)) >= 0;
  182. ++scno) {
  183. add_number_to_set_array(scno, set, p);
  184. found = true;
  185. }
  186. return found;
  187. }
  188. static bool
  189. qualify_syscall_name(const char *s, struct number_set *set)
  190. {
  191. unsigned int p;
  192. char *name_str = qualify_syscall_separate_personality(s, &p);
  193. bool found = false;
  194. if (name_str) {
  195. found = qualify_syscall_name_personality(name_str, p, set);
  196. free(name_str);
  197. return found;
  198. }
  199. for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
  200. found |= qualify_syscall_name_personality(s, p, set);
  201. return found;
  202. }
  203. static bool
  204. qualify_syscall(const char *token, struct number_set *set)
  205. {
  206. bool ignore_fail = false;
  207. while (*token == '?') {
  208. token++;
  209. ignore_fail = true;
  210. }
  211. if (*token >= '0' && *token <= '9')
  212. return qualify_syscall_number(token, set) || ignore_fail;
  213. if (*token == '/')
  214. return qualify_syscall_regex(token + 1, set) || ignore_fail;
  215. return qualify_syscall_class(token, set)
  216. || qualify_syscall_name(token, set)
  217. || ignore_fail;
  218. }
  219. /*
  220. * Add syscall numbers to SETs for each supported personality
  221. * according to STR specification.
  222. */
  223. void
  224. qualify_syscall_tokens(const char *const str, struct number_set *const set)
  225. {
  226. /* Clear all sets. */
  227. clear_number_set_array(set, SUPPORTED_PERSONALITIES);
  228. /*
  229. * Each leading ! character means inversion
  230. * of the remaining specification.
  231. */
  232. const char *s = str;
  233. while (*s == '!') {
  234. invert_number_set_array(set, SUPPORTED_PERSONALITIES);
  235. ++s;
  236. }
  237. if (strcmp(s, "none") == 0) {
  238. /*
  239. * No syscall numbers are added to sets.
  240. * Subsequent is_number_in_set* invocations
  241. * will return set[p]->not.
  242. */
  243. return;
  244. } else if (strcmp(s, "all") == 0) {
  245. /* "all" == "!none" */
  246. invert_number_set_array(set, SUPPORTED_PERSONALITIES);
  247. return;
  248. }
  249. /*
  250. * Split the string into comma separated tokens.
  251. * For each token, call qualify_syscall that will take care
  252. * if adding appropriate syscall numbers to sets.
  253. * The absence of tokens or a negative return code
  254. * from qualify_syscall is a fatal error.
  255. */
  256. char *copy = xstrdup(s);
  257. char *saveptr = NULL;
  258. bool done = false;
  259. for (const char *token = strtok_r(copy, ",", &saveptr);
  260. token; token = strtok_r(NULL, ",", &saveptr)) {
  261. done = qualify_syscall(token, set);
  262. if (!done)
  263. error_msg_and_die("invalid system call '%s'", token);
  264. }
  265. free(copy);
  266. if (!done)
  267. error_msg_and_die("invalid system call '%s'", str);
  268. }
  269. /*
  270. * Add numbers to SET according to STR specification.
  271. */
  272. void
  273. qualify_tokens(const char *const str, struct number_set *const set,
  274. string_to_uint_func func, const char *const name)
  275. {
  276. /* Clear the set. */
  277. clear_number_set_array(set, 1);
  278. /*
  279. * Each leading ! character means inversion
  280. * of the remaining specification.
  281. */
  282. const char *s = str;
  283. while (*s == '!') {
  284. invert_number_set_array(set, 1);
  285. ++s;
  286. }
  287. if (strcmp(s, "none") == 0) {
  288. /*
  289. * No numbers are added to the set.
  290. * Subsequent is_number_in_set* invocations
  291. * will return set->not.
  292. */
  293. return;
  294. } else if (strcmp(s, "all") == 0) {
  295. /* "all" == "!none" */
  296. invert_number_set_array(set, 1);
  297. return;
  298. }
  299. /*
  300. * Split the string into comma separated tokens.
  301. * For each token, find out the corresponding number
  302. * by calling FUNC, and add that number to the set.
  303. * The absence of tokens or a negative answer
  304. * from FUNC is a fatal error.
  305. */
  306. char *copy = xstrdup(s);
  307. char *saveptr = NULL;
  308. int number = -1;
  309. for (const char *token = strtok_r(copy, ",", &saveptr);
  310. token; token = strtok_r(NULL, ",", &saveptr)) {
  311. number = func(token);
  312. if (number < 0)
  313. error_msg_and_die("invalid %s '%s'", name, token);
  314. add_number_to_set(number, set);
  315. }
  316. free(copy);
  317. if (number < 0)
  318. error_msg_and_die("invalid %s '%s'", name, str);
  319. }