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