(root)/
strace-6.5/
tests-mx32/
ptrace_syscall_info.c
       1  /*
       2   * Check decoding of ptrace PTRACE_GET_SYSCALL_INFO request.
       3   *
       4   * Copyright (c) 2018 Dmitry V. Levin <ldv@strace.io>
       5   * Copyright (c) 2018-2023 The strace developers.
       6   * All rights reserved.
       7   *
       8   * SPDX-License-Identifier: GPL-2.0-or-later
       9   */
      10  
      11  #include "tests.h"
      12  
      13  #include "ptrace.h"
      14  #include "scno.h"
      15  
      16  #include <errno.h>
      17  #include <stddef.h>
      18  #include <stdio.h>
      19  #include <string.h>
      20  #include <signal.h>
      21  #include <sys/wait.h>
      22  #include <unistd.h>
      23  #include <linux/audit.h>
      24  
      25  #include "xlat.h"
      26  #define XLAT_MACROS_ONLY
      27  /* For xlat/audit_arch.h */
      28  # include "xlat/elf_em.h"
      29  #undef XLAT_MACROS_ONLY
      30  #include "xlat/audit_arch.h"
      31  
      32  static const char *errstr;
      33  
      34  static long
      35  do_ptrace(unsigned long request, unsigned long pid,
      36  	  unsigned long addr, unsigned long data)
      37  {
      38  	long rc = syscall(__NR_ptrace, request, pid, addr, data);
      39  	errstr = sprintrc(rc);
      40  	return rc;
      41  }
      42  
      43  static pid_t pid;
      44  
      45  static void
      46  kill_tracee(void)
      47  {
      48  	if (!pid)
      49  		return;
      50  	int saved_errno = errno;
      51  	kill(pid, SIGKILL);
      52  	errno = saved_errno;
      53  }
      54  
      55  #define FAIL(fmt_, ...)							\
      56  	do {								\
      57  		kill_tracee();						\
      58  		error_msg_and_fail("%s:%d: " fmt_,			\
      59  				   __FILE__, __LINE__, ##__VA_ARGS__);	\
      60  	} while (0)
      61  
      62  #define PFAIL(fmt_, ...)						\
      63  	do {								\
      64  		kill_tracee();						\
      65  		perror_msg_and_fail("%s:%d: " fmt_,			\
      66  				    __FILE__, __LINE__, ##__VA_ARGS__);	\
      67  	} while (0)
      68  
      69  static const unsigned long args[][7] = {
      70  	/* a sequence of architecture-agnostic syscalls */
      71  	{
      72  		__NR_chdir,
      73  		(unsigned long) "",
      74  		0xbad1fed1,
      75  		0xbad2fed2,
      76  		0xbad3fed3,
      77  		0xbad4fed4,
      78  		0xbad5fed5
      79  	},
      80  	{
      81  		__NR_gettid,
      82  		0xcaf0bea0,
      83  		0xcaf1bea1,
      84  		0xcaf2bea2,
      85  		0xcaf3bea3,
      86  		0xcaf4bea4,
      87  		0xcaf5bea5
      88  	},
      89  	{
      90  		__NR_exit_group,
      91  		0,
      92  		0xfac1c0d1,
      93  		0xfac2c0d2,
      94  		0xfac3c0d3,
      95  		0xfac4c0d4,
      96  		0xfac5c0d5
      97  	}
      98  };
      99  
     100  #if !XLAT_RAW
     101  static const char *sc_names[] = {
     102  	"chdir",
     103  	"gettid",
     104  	"exit_group"
     105  };
     106  #endif
     107  
     108  static const unsigned int expected_none_size =
     109  	offsetof(struct_ptrace_syscall_info, entry);
     110  static const unsigned int expected_entry_size =
     111  	offsetofend(struct_ptrace_syscall_info, entry.args);
     112  static const unsigned int expected_exit_size =
     113  	offsetofend(struct_ptrace_syscall_info, exit.is_error);
     114  
     115  static unsigned long end_of_page;
     116  static unsigned int ptrace_stop;
     117  
     118  static bool
     119  test_none(void)
     120  {
     121  	do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 1, 0);
     122  	printf("ptrace(" XLAT_FMT ", %d, 1, NULL) = %s\n",
     123  	       XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, errstr);
     124  
     125  	do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 1, end_of_page);
     126  	printf("ptrace(" XLAT_FMT ", %d, 1, %#lx) = %s\n",
     127  	       XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, end_of_page, errstr);
     128  
     129  	for (unsigned int size = 0;
     130  	     size <= sizeof(struct_ptrace_syscall_info); ++size) {
     131  		unsigned long buf = end_of_page - size;
     132  		memset((void *) buf, -1, size);
     133  
     134  		long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
     135  		printf("ptrace(" XLAT_FMT ", %d, %u, ",
     136  		       XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, size);
     137  		if (rc < (long) expected_none_size || size == 0)
     138  			printf("%#lx) = %s\n", buf, errstr);
     139  
     140  		if (rc < 0)
     141  			return false;
     142  		if (rc < (long) expected_none_size)
     143  			FAIL("signal stop mismatch");
     144  		if (size == 0)
     145  			continue;
     146  
     147  		/* copy to a local structure to avoid unaligned access */
     148  		struct_ptrace_syscall_info info;
     149  		memcpy(&info, (void *) buf,  MIN(size, expected_none_size));
     150  
     151  		printf("{op=" XLAT_FMT, XLAT_ARGS(PTRACE_SYSCALL_INFO_NONE));
     152  		if (info.op != PTRACE_SYSCALL_INFO_NONE)
     153  			FAIL("signal stop mismatch");
     154  
     155  		if (size < offsetofend(struct_ptrace_syscall_info, arch))
     156  			goto printed_none;
     157  		printf(", arch=");
     158  		printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
     159  		if (!info.arch)
     160  			FAIL("signal stop mismatch");
     161  
     162  		if (size < offsetofend(struct_ptrace_syscall_info,
     163  				       instruction_pointer))
     164  			goto printed_none;
     165  		printf(", instruction_pointer=%#llx",
     166  		       (unsigned long long) info.instruction_pointer);
     167  		if (!info.instruction_pointer)
     168  			FAIL("signal stop mismatch");
     169  
     170  		if (size < offsetofend(struct_ptrace_syscall_info,
     171  				       stack_pointer))
     172  			goto printed_none;
     173  		printf(", stack_pointer=%#llx",
     174  		       (unsigned long long) info.stack_pointer);
     175  		if (!info.stack_pointer)
     176  			FAIL("signal stop mismatch");
     177  
     178  printed_none:
     179  		printf("}) = %s\n", errstr);
     180  	}
     181  
     182  	return true;
     183  }
     184  
     185  static void
     186  test_entry(void)
     187  {
     188  	for (unsigned int size = 0;
     189  	     size <= sizeof(struct_ptrace_syscall_info); ++size) {
     190  		unsigned long buf = end_of_page - size;
     191  		memset((void *) buf, -1, size);
     192  
     193  		long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
     194  		printf("ptrace(" XLAT_FMT ", %d, %u, ",
     195  		       XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, size);
     196  		if (rc < (long) expected_entry_size || size == 0)
     197  			printf("%#lx) = %s\n", buf, errstr);
     198  
     199  		if (rc < 0)
     200  			FAIL("PTRACE_GET_SYSCALL_INFO");
     201  		if (rc < (long) expected_entry_size)
     202  			FAIL("#%d: entry stop mismatch", ptrace_stop);
     203  		if (size == 0)
     204  			continue;
     205  
     206  		/* copy to a local structure to avoid unaligned access */
     207  		struct_ptrace_syscall_info info;
     208  		memcpy(&info, (void *) buf,  MIN(size, expected_entry_size));
     209  
     210  		printf("{op=" XLAT_FMT, XLAT_ARGS(PTRACE_SYSCALL_INFO_ENTRY));
     211  		if (info.op != PTRACE_SYSCALL_INFO_ENTRY)
     212  			FAIL("#%d: entry stop mismatch", ptrace_stop);
     213  
     214  		if (size < offsetofend(struct_ptrace_syscall_info, arch))
     215  			goto printed_entry_common;
     216  		printf(", arch=");
     217  		printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
     218  		if (!info.arch)
     219  			FAIL("#%d: entry stop mismatch", ptrace_stop);
     220  
     221  		if (size < offsetofend(struct_ptrace_syscall_info,
     222  				       instruction_pointer))
     223  			goto printed_entry_common;
     224  		printf(", instruction_pointer=%#llx",
     225  		       (unsigned long long) info.instruction_pointer);
     226  		if (!info.instruction_pointer)
     227  			FAIL("#%d: entry stop mismatch", ptrace_stop);
     228  
     229  		if (size < offsetofend(struct_ptrace_syscall_info,
     230  				       stack_pointer))
     231  			goto printed_entry_common;
     232  		printf(", stack_pointer=%#llx",
     233  		       (unsigned long long) info.stack_pointer);
     234  		if (!info.stack_pointer)
     235  			FAIL("#%d: entry stop mismatch", ptrace_stop);
     236  
     237  		if (size < offsetofend(struct_ptrace_syscall_info, entry.nr))
     238  			goto printed_entry_common;
     239  		printf(", entry={nr="
     240  		       NABBR("%llu") VERB(" /* ") NRAW("__NR_%s") VERB(" */"),
     241  		       XLAT_SEL((unsigned long long) info.entry.nr,
     242  				sc_names[ptrace_stop / 2]));
     243  		const unsigned long *exp_args = args[ptrace_stop / 2];
     244  		if (info.entry.nr != exp_args[0])
     245  			FAIL("#%d: entry stop mismatch", ptrace_stop);
     246  
     247  		for (unsigned int i = 0; i < ARRAY_SIZE(info.entry.args); ++i) {
     248  			const unsigned int i_size =
     249  				offsetofend(struct_ptrace_syscall_info,
     250  					    entry.args[i]);
     251  			if (size < i_size) {
     252  				if (i)
     253  					break;
     254  				goto printed_entry_nr;
     255  			}
     256  #if SIZEOF_KERNEL_LONG_T > SIZEOF_LONG
     257  # define CAST (unsigned long)
     258  #else
     259  # define CAST
     260  #endif
     261  			printf("%s%#llx", (i ? ", " : ", args=["),
     262  			       (unsigned long long) info.entry.args[i]);
     263  			if (CAST info.entry.args[i] != exp_args[i + 1])
     264  				FAIL("#%d: entry stop mismatch", ptrace_stop);
     265  		}
     266  		printf("]");
     267  
     268  printed_entry_nr:
     269  		printf("}");
     270  
     271  printed_entry_common:
     272  		printf("}) = %s\n", errstr);
     273  	}
     274  }
     275  
     276  static void
     277  test_exit(void)
     278  {
     279  	for (unsigned int size = 0;
     280  	     size <= sizeof(struct_ptrace_syscall_info); ++size) {
     281  		unsigned long buf = end_of_page - size;
     282  		memset((void *) buf, -1, size);
     283  
     284  		long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
     285  		printf("ptrace(" XLAT_FMT ", %d, %u, ",
     286  		       XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, size);
     287  		if (rc < (long) expected_exit_size || size == 0)
     288  			printf("%#lx) = %s\n", buf, errstr);
     289  
     290  		if (rc < 0)
     291  			FAIL("PTRACE_GET_SYSCALL_INFO");
     292  		if (rc < (long) expected_exit_size)
     293  			FAIL("#%d: exit stop mismatch", ptrace_stop);
     294  		if (size == 0)
     295  			continue;
     296  
     297  		/* copy to a local structure to avoid unaligned access */
     298  		struct_ptrace_syscall_info info;
     299  		memcpy(&info, (void *) buf,  MIN(size, expected_exit_size));
     300  
     301  		printf("{op=" XLAT_FMT, XLAT_ARGS(PTRACE_SYSCALL_INFO_EXIT));
     302  		if (info.op != PTRACE_SYSCALL_INFO_EXIT)
     303  			FAIL("#%d: exit stop mismatch", ptrace_stop);
     304  
     305  		if (size < offsetofend(struct_ptrace_syscall_info, arch))
     306  			goto printed_exit_common;
     307  		printf(", arch=");
     308  		printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
     309  		if (!info.arch)
     310  			FAIL("#%d: exit stop mismatch", ptrace_stop);
     311  
     312  		if (size < offsetofend(struct_ptrace_syscall_info,
     313  				       instruction_pointer))
     314  			goto printed_exit_common;
     315  		printf(", instruction_pointer=%#llx",
     316  		       (unsigned long long) info.instruction_pointer);
     317  		if (!info.instruction_pointer)
     318  			FAIL("#%d: exit stop mismatch", ptrace_stop);
     319  
     320  		if (size < offsetofend(struct_ptrace_syscall_info,
     321  				       stack_pointer))
     322  			goto printed_exit_common;
     323  		printf(", stack_pointer=%#llx",
     324  		       (unsigned long long) info.stack_pointer);
     325  		if (!info.stack_pointer)
     326  			FAIL("#%d: exit stop mismatch", ptrace_stop);
     327  
     328  		const struct {
     329  			unsigned int is_error;
     330  			int rval;
     331  			const char *str;
     332  		} exit_param[] = {
     333  			{ 1, -ENOENT, "-ENOENT" },	/* chdir */
     334  			{ 0, pid, NULL }		/* gettid */
     335  		}, *exp_param = &exit_param[ptrace_stop / 2 - 1];
     336  
     337  		if (size < offsetofend(struct_ptrace_syscall_info, exit.rval))
     338  			goto printed_exit_common;
     339  		if (size >= expected_exit_size && info.exit.is_error) {
     340  			printf(", exit={rval=" XLAT_FMT_D,
     341  			       XLAT_SEL(exp_param->rval, exp_param->str));
     342  		} else {
     343  			printf(", exit={rval=%lld", (long long) info.exit.rval);
     344  		}
     345  		if (info.exit.rval != exp_param->rval)
     346  			FAIL("#%d: exit stop mismatch", ptrace_stop);
     347  
     348  		if (size >= expected_exit_size) {
     349  			printf(", is_error=%u",
     350  			       (unsigned int) info.exit.is_error);
     351  			if (info.exit.is_error != exp_param->is_error)
     352  				FAIL("#%d: exit stop mismatch", ptrace_stop);
     353  		}
     354  
     355  		printf("}");
     356  
     357  printed_exit_common:
     358  		printf("}) = %s\n", errstr);
     359  	}
     360  }
     361  
     362  int
     363  main(void)
     364  {
     365  	end_of_page = (unsigned long) tail_alloc(1) + 1;
     366  
     367  	pid = getpid();
     368  	do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 0, 0);
     369  	printf("ptrace(" XLAT_FMT ", %d, 0, NULL) = %s\n",
     370  	       XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, errstr);
     371  
     372  	pid = fork();
     373  	if (pid < 0)
     374  		PFAIL("fork");
     375  
     376  	if (pid == 0) {
     377  		/* get the pid before PTRACE_TRACEME */
     378  		pid = getpid();
     379  		if (do_ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
     380  			/* exit with a nonzero exit status */
     381  			PFAIL("PTRACE_TRACEME");
     382  		}
     383  		kill(pid, SIGSTOP);
     384  		for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
     385  			syscall(args[i][0],
     386  				args[i][1], args[i][2], args[i][3],
     387  				args[i][4], args[i][5], args[i][6]);
     388  		}
     389  		/* unreachable */
     390  		_exit(1);
     391  	}
     392  
     393  	for (ptrace_stop = 0; ; ++ptrace_stop) {
     394  		int status;
     395  		long rc = waitpid(pid, &status, 0);
     396  		if (rc != pid) {
     397  			/* cannot happen */
     398  			PFAIL("#%d: unexpected wait result %ld",
     399  					    ptrace_stop, rc);
     400  		}
     401  		if (WIFEXITED(status)) {
     402  			/* tracee is no more */
     403  			pid = 0;
     404  			if (WEXITSTATUS(status) == 0)
     405  				break;
     406  			FAIL("#%d: unexpected exit status %u",
     407  			     ptrace_stop, WEXITSTATUS(status));
     408  		}
     409  		if (WIFSIGNALED(status)) {
     410  			/* tracee is no more */
     411  			pid = 0;
     412  			FAIL("#%d: unexpected signal %u",
     413  			     ptrace_stop, WTERMSIG(status));
     414  		}
     415  		if (!WIFSTOPPED(status)) {
     416  			/* cannot happen */
     417  			FAIL("#%d: unexpected wait status %#x",
     418  			     ptrace_stop, status);
     419  		}
     420  
     421  		switch (WSTOPSIG(status)) {
     422  		case SIGSTOP:
     423  			if (ptrace_stop)
     424  				FAIL("#%d: unexpected signal stop",
     425  					       ptrace_stop);
     426  			if (do_ptrace(PTRACE_SETOPTIONS, pid, 0,
     427  				   PTRACE_O_TRACESYSGOOD) < 0) {
     428  				/* cannot happen */
     429  				PFAIL("PTRACE_SETOPTIONS");
     430  			}
     431  			printf("ptrace(" XLAT_FMT ", %d, NULL, " XLAT_FMT
     432  			       ") = 0\n",
     433  			       XLAT_ARGS(PTRACE_SETOPTIONS), pid,
     434  			       XLAT_ARGS(PTRACE_O_TRACESYSGOOD));
     435  
     436  			if (!test_none())
     437  				goto done;
     438  			break;
     439  
     440  		case SIGTRAP | 0x80:
     441  			switch (ptrace_stop) {
     442  			case 1: /* entering chdir */
     443  			case 3: /* entering gettid */
     444  			case 5: /* entering exit_group */
     445  				test_entry();
     446  				break;
     447  			case 2: /* exiting chdir */
     448  			case 4: /* exiting gettid */
     449  				test_exit();
     450  				break;
     451  			default:
     452  				FAIL("#%d: unexpected syscall stop",
     453  				     ptrace_stop);
     454  			}
     455  			break;
     456  
     457  		default:
     458  			FAIL("#%d: unexpected stop signal %#x",
     459  			     ptrace_stop, WSTOPSIG(status));
     460  		}
     461  
     462  		if (do_ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) {
     463  			/* cannot happen */
     464  			PFAIL("PTRACE_SYSCALL");
     465  		}
     466  		printf("ptrace(" XLAT_FMT ", %d, NULL, 0) = 0\n",
     467  		       XLAT_ARGS(PTRACE_SYSCALL), pid);
     468  	}
     469  
     470  done:
     471  	if (pid) {
     472  		kill_tracee();
     473  		waitpid(pid, NULL, 0);
     474  	}
     475  
     476  	puts("+++ exited with 0 +++");
     477  	return 0;
     478  }