Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
  4 * All rights reserved.
  5 *
  6 * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
  7 * matches userspace expectations.
  8 */
  9
 10#include "../kselftest_harness.h"
 11#include <err.h>
 12#include <signal.h>
 13#include <asm/unistd.h>
 14#include "linux/ptrace.h"
 15
 16static int
 17kill_tracee(pid_t pid)
 18{
 19	if (!pid)
 20		return 0;
 21
 22	int saved_errno = errno;
 23
 24	int rc = kill(pid, SIGKILL);
 25
 26	errno = saved_errno;
 27	return rc;
 28}
 29
 30static long
 31sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
 32{
 33	return syscall(__NR_ptrace, request, pid, addr, data);
 34}
 35
 36#define LOG_KILL_TRACEE(fmt, ...)				\
 37	do {							\
 38		kill_tracee(pid);				\
 39		TH_LOG("wait #%d: " fmt,			\
 40		       ptrace_stop, ##__VA_ARGS__);		\
 41	} while (0)
 42
 43TEST(get_syscall_info)
 44{
 45	static const unsigned long args[][7] = {
 46		/* a sequence of architecture-agnostic syscalls */
 47		{
 48			__NR_chdir,
 49			(unsigned long) "",
 50			0xbad1fed1,
 51			0xbad2fed2,
 52			0xbad3fed3,
 53			0xbad4fed4,
 54			0xbad5fed5
 55		},
 56		{
 57			__NR_gettid,
 58			0xcaf0bea0,
 59			0xcaf1bea1,
 60			0xcaf2bea2,
 61			0xcaf3bea3,
 62			0xcaf4bea4,
 63			0xcaf5bea5
 64		},
 65		{
 66			__NR_exit_group,
 67			0,
 68			0xfac1c0d1,
 69			0xfac2c0d2,
 70			0xfac3c0d3,
 71			0xfac4c0d4,
 72			0xfac5c0d5
 73		}
 74	};
 75	const unsigned long *exp_args;
 76
 77	pid_t pid = fork();
 78
 79	ASSERT_LE(0, pid) {
 80		TH_LOG("fork: %m");
 81	}
 82
 83	if (pid == 0) {
 84		/* get the pid before PTRACE_TRACEME */
 85		pid = getpid();
 86		ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
 87			TH_LOG("PTRACE_TRACEME: %m");
 88		}
 89		ASSERT_EQ(0, kill(pid, SIGSTOP)) {
 90			/* cannot happen */
 91			TH_LOG("kill SIGSTOP: %m");
 92		}
 93		for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
 94			syscall(args[i][0],
 95				args[i][1], args[i][2], args[i][3],
 96				args[i][4], args[i][5], args[i][6]);
 97		}
 98		/* unreachable */
 99		_exit(1);
100	}
101
102	const struct {
103		unsigned int is_error;
104		int rval;
105	} *exp_param, exit_param[] = {
106		{ 1, -ENOENT },	/* chdir */
107		{ 0, pid }	/* gettid */
108	};
109
110	unsigned int ptrace_stop;
111
112	for (ptrace_stop = 0; ; ++ptrace_stop) {
113		struct ptrace_syscall_info info = {
114			.op = 0xff	/* invalid PTRACE_SYSCALL_INFO_* op */
115		};
116		const size_t size = sizeof(info);
117		const int expected_none_size =
118			(void *) &info.entry - (void *) &info;
119		const int expected_entry_size =
120			(void *) &info.entry.args[6] - (void *) &info;
121		const int expected_exit_size =
122			(void *) (&info.exit.is_error + 1) -
123			(void *) &info;
124		int status;
125		long rc;
126
127		ASSERT_EQ(pid, wait(&status)) {
128			/* cannot happen */
129			LOG_KILL_TRACEE("wait: %m");
130		}
131		if (WIFEXITED(status)) {
132			pid = 0;	/* the tracee is no more */
133			ASSERT_EQ(0, WEXITSTATUS(status));
134			break;
135		}
136		ASSERT_FALSE(WIFSIGNALED(status)) {
137			pid = 0;	/* the tracee is no more */
138			LOG_KILL_TRACEE("unexpected signal %u",
139					WTERMSIG(status));
140		}
141		ASSERT_TRUE(WIFSTOPPED(status)) {
142			/* cannot happen */
143			LOG_KILL_TRACEE("unexpected wait status %#x", status);
144		}
145
146		switch (WSTOPSIG(status)) {
147		case SIGSTOP:
148			ASSERT_EQ(0, ptrace_stop) {
149				LOG_KILL_TRACEE("unexpected signal stop");
150			}
151			ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
152						PTRACE_O_TRACESYSGOOD)) {
153				LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
154			}
155			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
156						      pid, size,
157						      (unsigned long) &info))) {
158				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
159			}
160			ASSERT_EQ(expected_none_size, rc) {
161				LOG_KILL_TRACEE("signal stop mismatch");
162			}
163			ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
164				LOG_KILL_TRACEE("signal stop mismatch");
165			}
166			ASSERT_TRUE(info.arch) {
167				LOG_KILL_TRACEE("signal stop mismatch");
168			}
169			ASSERT_TRUE(info.instruction_pointer) {
170				LOG_KILL_TRACEE("signal stop mismatch");
171			}
172			ASSERT_TRUE(info.stack_pointer) {
173				LOG_KILL_TRACEE("signal stop mismatch");
174			}
175			break;
176
177		case SIGTRAP | 0x80:
178			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
179						      pid, size,
180						      (unsigned long) &info))) {
181				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
182			}
183			switch (ptrace_stop) {
184			case 1: /* entering chdir */
185			case 3: /* entering gettid */
186			case 5: /* entering exit_group */
187				exp_args = args[ptrace_stop / 2];
188				ASSERT_EQ(expected_entry_size, rc) {
189					LOG_KILL_TRACEE("entry stop mismatch");
190				}
191				ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
192					LOG_KILL_TRACEE("entry stop mismatch");
193				}
194				ASSERT_TRUE(info.arch) {
195					LOG_KILL_TRACEE("entry stop mismatch");
196				}
197				ASSERT_TRUE(info.instruction_pointer) {
198					LOG_KILL_TRACEE("entry stop mismatch");
199				}
200				ASSERT_TRUE(info.stack_pointer) {
201					LOG_KILL_TRACEE("entry stop mismatch");
202				}
203				ASSERT_EQ(exp_args[0], info.entry.nr) {
204					LOG_KILL_TRACEE("entry stop mismatch");
205				}
206				ASSERT_EQ(exp_args[1], info.entry.args[0]) {
207					LOG_KILL_TRACEE("entry stop mismatch");
208				}
209				ASSERT_EQ(exp_args[2], info.entry.args[1]) {
210					LOG_KILL_TRACEE("entry stop mismatch");
211				}
212				ASSERT_EQ(exp_args[3], info.entry.args[2]) {
213					LOG_KILL_TRACEE("entry stop mismatch");
214				}
215				ASSERT_EQ(exp_args[4], info.entry.args[3]) {
216					LOG_KILL_TRACEE("entry stop mismatch");
217				}
218				ASSERT_EQ(exp_args[5], info.entry.args[4]) {
219					LOG_KILL_TRACEE("entry stop mismatch");
220				}
221				ASSERT_EQ(exp_args[6], info.entry.args[5]) {
222					LOG_KILL_TRACEE("entry stop mismatch");
223				}
224				break;
225			case 2: /* exiting chdir */
226			case 4: /* exiting gettid */
227				exp_param = &exit_param[ptrace_stop / 2 - 1];
228				ASSERT_EQ(expected_exit_size, rc) {
229					LOG_KILL_TRACEE("exit stop mismatch");
230				}
231				ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
232					LOG_KILL_TRACEE("exit stop mismatch");
233				}
234				ASSERT_TRUE(info.arch) {
235					LOG_KILL_TRACEE("exit stop mismatch");
236				}
237				ASSERT_TRUE(info.instruction_pointer) {
238					LOG_KILL_TRACEE("exit stop mismatch");
239				}
240				ASSERT_TRUE(info.stack_pointer) {
241					LOG_KILL_TRACEE("exit stop mismatch");
242				}
243				ASSERT_EQ(exp_param->is_error,
244					  info.exit.is_error) {
245					LOG_KILL_TRACEE("exit stop mismatch");
246				}
247				ASSERT_EQ(exp_param->rval, info.exit.rval) {
248					LOG_KILL_TRACEE("exit stop mismatch");
249				}
250				break;
251			default:
252				LOG_KILL_TRACEE("unexpected syscall stop");
253				abort();
254			}
255			break;
256
257		default:
258			LOG_KILL_TRACEE("unexpected stop signal %#x",
259					WSTOPSIG(status));
260			abort();
261		}
262
263		ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
264			LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
265		}
266	}
267
268	ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
269}
270
271TEST_HARNESS_MAIN
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
  4 * All rights reserved.
  5 *
  6 * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
  7 * matches userspace expectations.
  8 */
  9
 10#include "../kselftest_harness.h"
 11#include <err.h>
 12#include <signal.h>
 13#include <asm/unistd.h>
 14#include "linux/ptrace.h"
 15
 16static int
 17kill_tracee(pid_t pid)
 18{
 19	if (!pid)
 20		return 0;
 21
 22	int saved_errno = errno;
 23
 24	int rc = kill(pid, SIGKILL);
 25
 26	errno = saved_errno;
 27	return rc;
 28}
 29
 30static long
 31sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
 32{
 33	return syscall(__NR_ptrace, request, pid, addr, data);
 34}
 35
 36#define LOG_KILL_TRACEE(fmt, ...)				\
 37	do {							\
 38		kill_tracee(pid);				\
 39		TH_LOG("wait #%d: " fmt,			\
 40		       ptrace_stop, ##__VA_ARGS__);		\
 41	} while (0)
 42
 43TEST(get_syscall_info)
 44{
 45	static const unsigned long args[][7] = {
 46		/* a sequence of architecture-agnostic syscalls */
 47		{
 48			__NR_chdir,
 49			(unsigned long) "",
 50			0xbad1fed1,
 51			0xbad2fed2,
 52			0xbad3fed3,
 53			0xbad4fed4,
 54			0xbad5fed5
 55		},
 56		{
 57			__NR_gettid,
 58			0xcaf0bea0,
 59			0xcaf1bea1,
 60			0xcaf2bea2,
 61			0xcaf3bea3,
 62			0xcaf4bea4,
 63			0xcaf5bea5
 64		},
 65		{
 66			__NR_exit_group,
 67			0,
 68			0xfac1c0d1,
 69			0xfac2c0d2,
 70			0xfac3c0d3,
 71			0xfac4c0d4,
 72			0xfac5c0d5
 73		}
 74	};
 75	const unsigned long *exp_args;
 76
 77	pid_t pid = fork();
 78
 79	ASSERT_LE(0, pid) {
 80		TH_LOG("fork: %m");
 81	}
 82
 83	if (pid == 0) {
 84		/* get the pid before PTRACE_TRACEME */
 85		pid = getpid();
 86		ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
 87			TH_LOG("PTRACE_TRACEME: %m");
 88		}
 89		ASSERT_EQ(0, kill(pid, SIGSTOP)) {
 90			/* cannot happen */
 91			TH_LOG("kill SIGSTOP: %m");
 92		}
 93		for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
 94			syscall(args[i][0],
 95				args[i][1], args[i][2], args[i][3],
 96				args[i][4], args[i][5], args[i][6]);
 97		}
 98		/* unreachable */
 99		_exit(1);
100	}
101
102	const struct {
103		unsigned int is_error;
104		int rval;
105	} *exp_param, exit_param[] = {
106		{ 1, -ENOENT },	/* chdir */
107		{ 0, pid }	/* gettid */
108	};
109
110	unsigned int ptrace_stop;
111
112	for (ptrace_stop = 0; ; ++ptrace_stop) {
113		struct ptrace_syscall_info info = {
114			.op = 0xff	/* invalid PTRACE_SYSCALL_INFO_* op */
115		};
116		const size_t size = sizeof(info);
117		const int expected_none_size =
118			(void *) &info.entry - (void *) &info;
119		const int expected_entry_size =
120			(void *) &info.entry.args[6] - (void *) &info;
121		const int expected_exit_size =
122			(void *) (&info.exit.is_error + 1) -
123			(void *) &info;
124		int status;
125		long rc;
126
127		ASSERT_EQ(pid, wait(&status)) {
128			/* cannot happen */
129			LOG_KILL_TRACEE("wait: %m");
130		}
131		if (WIFEXITED(status)) {
132			pid = 0;	/* the tracee is no more */
133			ASSERT_EQ(0, WEXITSTATUS(status));
134			break;
135		}
136		ASSERT_FALSE(WIFSIGNALED(status)) {
137			pid = 0;	/* the tracee is no more */
138			LOG_KILL_TRACEE("unexpected signal %u",
139					WTERMSIG(status));
140		}
141		ASSERT_TRUE(WIFSTOPPED(status)) {
142			/* cannot happen */
143			LOG_KILL_TRACEE("unexpected wait status %#x", status);
144		}
145
146		switch (WSTOPSIG(status)) {
147		case SIGSTOP:
148			ASSERT_EQ(0, ptrace_stop) {
149				LOG_KILL_TRACEE("unexpected signal stop");
150			}
151			ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
152						PTRACE_O_TRACESYSGOOD)) {
153				LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
154			}
155			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
156						      pid, size,
157						      (unsigned long) &info))) {
158				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
159			}
160			ASSERT_EQ(expected_none_size, rc) {
161				LOG_KILL_TRACEE("signal stop mismatch");
162			}
163			ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
164				LOG_KILL_TRACEE("signal stop mismatch");
165			}
166			ASSERT_TRUE(info.arch) {
167				LOG_KILL_TRACEE("signal stop mismatch");
168			}
169			ASSERT_TRUE(info.instruction_pointer) {
170				LOG_KILL_TRACEE("signal stop mismatch");
171			}
172			ASSERT_TRUE(info.stack_pointer) {
173				LOG_KILL_TRACEE("signal stop mismatch");
174			}
175			break;
176
177		case SIGTRAP | 0x80:
178			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
179						      pid, size,
180						      (unsigned long) &info))) {
181				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
182			}
183			switch (ptrace_stop) {
184			case 1: /* entering chdir */
185			case 3: /* entering gettid */
186			case 5: /* entering exit_group */
187				exp_args = args[ptrace_stop / 2];
188				ASSERT_EQ(expected_entry_size, rc) {
189					LOG_KILL_TRACEE("entry stop mismatch");
190				}
191				ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
192					LOG_KILL_TRACEE("entry stop mismatch");
193				}
194				ASSERT_TRUE(info.arch) {
195					LOG_KILL_TRACEE("entry stop mismatch");
196				}
197				ASSERT_TRUE(info.instruction_pointer) {
198					LOG_KILL_TRACEE("entry stop mismatch");
199				}
200				ASSERT_TRUE(info.stack_pointer) {
201					LOG_KILL_TRACEE("entry stop mismatch");
202				}
203				ASSERT_EQ(exp_args[0], info.entry.nr) {
204					LOG_KILL_TRACEE("entry stop mismatch");
205				}
206				ASSERT_EQ(exp_args[1], info.entry.args[0]) {
207					LOG_KILL_TRACEE("entry stop mismatch");
208				}
209				ASSERT_EQ(exp_args[2], info.entry.args[1]) {
210					LOG_KILL_TRACEE("entry stop mismatch");
211				}
212				ASSERT_EQ(exp_args[3], info.entry.args[2]) {
213					LOG_KILL_TRACEE("entry stop mismatch");
214				}
215				ASSERT_EQ(exp_args[4], info.entry.args[3]) {
216					LOG_KILL_TRACEE("entry stop mismatch");
217				}
218				ASSERT_EQ(exp_args[5], info.entry.args[4]) {
219					LOG_KILL_TRACEE("entry stop mismatch");
220				}
221				ASSERT_EQ(exp_args[6], info.entry.args[5]) {
222					LOG_KILL_TRACEE("entry stop mismatch");
223				}
224				break;
225			case 2: /* exiting chdir */
226			case 4: /* exiting gettid */
227				exp_param = &exit_param[ptrace_stop / 2 - 1];
228				ASSERT_EQ(expected_exit_size, rc) {
229					LOG_KILL_TRACEE("exit stop mismatch");
230				}
231				ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
232					LOG_KILL_TRACEE("exit stop mismatch");
233				}
234				ASSERT_TRUE(info.arch) {
235					LOG_KILL_TRACEE("exit stop mismatch");
236				}
237				ASSERT_TRUE(info.instruction_pointer) {
238					LOG_KILL_TRACEE("exit stop mismatch");
239				}
240				ASSERT_TRUE(info.stack_pointer) {
241					LOG_KILL_TRACEE("exit stop mismatch");
242				}
243				ASSERT_EQ(exp_param->is_error,
244					  info.exit.is_error) {
245					LOG_KILL_TRACEE("exit stop mismatch");
246				}
247				ASSERT_EQ(exp_param->rval, info.exit.rval) {
248					LOG_KILL_TRACEE("exit stop mismatch");
249				}
250				break;
251			default:
252				LOG_KILL_TRACEE("unexpected syscall stop");
253				abort();
254			}
255			break;
256
257		default:
258			LOG_KILL_TRACEE("unexpected stop signal %#x",
259					WSTOPSIG(status));
260			abort();
261		}
262
263		ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
264			LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
265		}
266	}
267
268	ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
269}
270
271TEST_HARNESS_MAIN